ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/abitmap.cpp
Revision: 2.2
Committed: Sat Aug 24 23:25:24 2024 UTC (8 months, 1 week ago) by greg
Branch: MAIN
CVS Tags: HEAD
Changes since 2.1: +3 -0 lines
Log Message:
chore: Added RCS tags to files

File Contents

# User Rev Content
1 greg 2.2 #ifndef lint
2     static const char RCSid[] = "$Id$";
3     #endif
4 greg 2.1 /*
5     * abitmap.cpp
6     * panlib
7     *
8     * General bitmap class implementation
9     *
10     * Created by gward on Wed Oct 31 2001.
11     * Copyright (c) 2022 Anyhere Software. All rights reserved.
12     *
13     */
14    
15     #include <string.h>
16     #include <math.h>
17     #include "abitmap.h"
18    
19     // Private workhorse bit copy function; handles overlaps but no length checks
20     static void
21     moveBits(uint32 *bdst, uint32 idst, const uint32 *bsrc, uint32 isrc, uint32 n)
22     {
23     if (!n)
24     return;
25     bdst += idst >> 5; idst &= 0x1f;
26     bsrc += isrc >> 5; isrc &= 0x1f;
27     if ((bdst == bsrc) & (idst == isrc))
28     return;
29     uint32 sword[2];
30     if (n <= 32) { // short string case
31     sword[0] = bsrc[0];
32     sword[1] = bsrc[isrc+n > 32];
33     bsrc = sword;
34     while (n--) {
35     *bdst &= ~(1 << idst);
36     *bdst |= ((*bsrc >> isrc) & 1) << idst;
37     if (++idst > 0x1f) { idst=0; ++bdst; }
38     if (++isrc > 0x1f) { isrc=0; ++bsrc; }
39     }
40     return;
41     }
42     const bool reverse = (bdst==bsrc) ? (idst > isrc) : (bdst > bsrc);
43     const int boff = isrc-idst & 0x1f;
44     const bool woff = (isrc > idst);
45     const bool partstart = (idst != 0);
46     const bool partend = ((idst+n & 0x1f) != 0);
47     const int lastword = (idst+n) >> 5;
48     uint32 mask;
49     // messy starting-word stuff
50     #define DO_FIRSTPART if (partstart) { \
51     sword[0] = bsrc[0]; sword[1] = bsrc[1]; \
52     bdst[0] &= mask = (1<<idst)-1; \
53     if (boff) { \
54     bdst[0] |= sword[woff]<<(32-boff) & ~mask; \
55     if (woff) bdst[0] |= sword[0]>>boff & ~mask; \
56     } else \
57     bdst[0] |= sword[0] & ~mask; \
58     } else
59     // messy ending-word stuff
60     #define DO_LASTPART if (partend) { \
61     mask = ~0 << (idst+n & 0x1f); \
62     bool beyond = (1<<(32-boff) & ~mask); \
63     sword[0] = bsrc[lastword-1]; \
64     if (!boff | woff | beyond) sword[1] = bsrc[lastword]; \
65     bdst[lastword] &= mask; \
66     if (boff) { \
67     bdst[lastword] |= (sword[woff]>>boff) & ~mask; \
68     if (beyond) \
69     bdst[lastword] |= (woff ? bsrc[lastword+1] : sword[1]) \
70     << (32-boff) & ~mask; \
71     } else \
72     bdst[lastword] |= sword[1] & ~mask; \
73     } else
74     if (reverse)
75     DO_LASTPART;
76     else
77     DO_FIRSTPART;
78     if (boff) { // middle part (unaligned case)
79     int i;
80     if (reverse) {
81     for (i = lastword; --i > 0; )
82     bdst[i] = bsrc[i+woff]<<(32-boff) | bsrc[i+woff-1]>>boff;
83     if (!partstart) {
84     bdst[0] = bsrc[woff]<<(32-boff);
85     if (woff) bdst[0] |= bsrc[0]>>boff;
86     }
87     } else {
88     if (!partstart) {
89     bdst[0] = bsrc[woff]<<(32-boff);
90     if (woff) bdst[0] |= bsrc[0]>>boff;
91     }
92     for (i = 0; ++i < lastword; )
93     bdst[i] = bsrc[i+woff]<<(32-boff) | bsrc[i+woff-1]>>boff;
94     }
95     } else { // middle (aligned word case)
96     memmove(bdst+partstart, bsrc+partstart,
97     (lastword-partstart)*sizeof(uint32));
98     }
99     if (reverse)
100     DO_FIRSTPART;
101     else
102     DO_LASTPART;
103     #undef DO_FIRSTPART
104     #undef DO_LASTPART
105     }
106    
107     // Create and clear a new bitmap
108     bool
109     ABitMap::NewBitMap(uint32 n, bool clrset)
110     {
111     int32 onwords = bmlen();
112     len = n;
113     int32 nwords = bmlen();
114     if (nwords != onwords) {
115     delete [] bmap;
116     if (nwords) bmap = new uint32 [nwords];
117     else bmap = 0;
118     }
119     if (!nwords)
120     return false;
121     ClearBitMap(clrset);
122     return true;
123     }
124    
125     // Clear bitmap to given value
126     void
127     ABitMap::ClearBitMap(bool clrset)
128     {
129     memset(bmap, clrset * 0xff, sizeof(uint32)*bmlen());
130     }
131    
132     // Invert the entire bitmap
133     void
134     ABitMap::Invert()
135     {
136     uint32 * wp = bmap + bmlen();
137     while (wp-- > bmap)
138     *wp = ~*wp;
139     }
140    
141     // Extract bitmap section
142     bool
143     ABitMap::GetBits(ABitMap *dp, uint32 i) const
144     {
145     if (!dp | (dp == this))
146     return false;
147     if (i >= len)
148     return false;
149     if (!dp->len && !dp->NewBitMap(len-i))
150     return false;
151     if (!i & (dp->len == len)) {
152     *dp = *this;
153     return true;
154     }
155     uint32 n = dp->len;
156     if (n > len-i)
157     n = len-i;
158     moveBits(dp->bmap, 0, bmap, i, n);
159     return true;
160     }
161    
162     // Overlay bitmap section (ignores bits past end)
163     bool
164     ABitMap::AssignBits(uint32 i, const ABitMap &src)
165     {
166     if (!src.len)
167     return true;
168     if (!i & (src.len == len)) {
169     *this = src;
170     return true;
171     }
172     if (i >= len)
173     return false;
174     moveBits(bmap, i, src.bmap, 0, (len-i < src.len) ? len-i : src.len);
175     return true;
176     }
177    
178     // Apply operation to bitmap section
179     bool
180     ABitMap::OpBits(uint32 i, char op, const ABitMap &src)
181     {
182     if (!src.len | !len)
183     return false;
184     if (op == '=')
185     return AssignBits(i, src);
186     ABitMap bits(src.len);
187     if (!GetBits(&bits, i))
188     return false;
189     switch (op) {
190     case '|':
191     bits |= src;
192     break;
193     case '&':
194     bits &= src;
195     break;
196     case '^':
197     bits ^= src;
198     break;
199     case '-':
200     case '>':
201     bits -= src;
202     break;
203     case '<':
204     bits.Invert();
205     bits &= src;
206     break;
207     default:
208     return false;
209     }
210     return AssignBits(i, bits);
211     }
212    
213     // Clear bitmap section
214     void
215     ABitMap::ClearBits(uint32 i, uint32 n, bool clrset)
216     {
217     if (i >= len)
218     return;
219     if (n >= len - i) {
220     if (!i) {
221     ClearBitMap(clrset);
222     return;
223     }
224     n = len - i;
225     } else if (!n)
226     return;
227    
228     const uint32 * const sectEnd = bmap + ((i+n)>>5);
229     uint32 * wp = bmap + (i>>5);
230     if (wp == sectEnd) { // single word clear?
231     const uint32 bits = (~0 << (i & 0x1f) &
232     (1 << (i+n & 0x1f)) - 1);
233     if (clrset)
234     *wp |= bits;
235     else
236     *wp &= ~bits;
237     return;
238     }
239     const uint32 clrWord = clrset * ~0;
240     if (i & 0x1f) { // partial first word?
241     if (clrset)
242     *wp++ |= ~0 << (i & 0x1f);
243     else
244     *wp++ &= (1 << (i & 0x1f)) - 1;
245     }
246     while (wp < sectEnd) // central words
247     *wp++ = clrWord;
248     if (i+n & 0x1f) { // partial last word?
249     if (clrset)
250     *wp |= (1 << (i+n & 0x1f)) - 1;
251     else
252     *wp &= ~0 << (i+n & 0x1f);
253     }
254     }
255    
256     // Total all bits set in bitmap
257     uint32
258     ABitMap::SumTotal(bool bit2cnt) const
259     {
260     static char bitCount[256];
261     int i;
262     if (!bitCount[255]) {
263     for (i = 256; --i; )
264     for (int bits = i; bits; bits >>= 1)
265     bitCount[i] += (bits & 1);
266     }
267     uint32 count = 0;
268     const unsigned char * cp;
269     cp = (const unsigned char *)(bmap + bmlen());
270     if (len & 0x1f) { // partial last word
271     uint32 lastBits = WordV(len-1);
272     lastBits &= (1<<(len&0x1f)) - 1;
273     for (i = sizeof(uint32); i--; lastBits >>= 8)
274     count += bitCount[lastBits & 0xff];
275     cp -= sizeof(uint32);
276     }
277     while (cp > (const unsigned char *)bmap)
278     count += bitCount[*--cp];
279     if (bit2cnt)
280     return count;
281     return len - count;
282     }
283    
284     // Return the next bit position matching val (or ABMend if no match)
285     uint32
286     ABitMap::Find(uint32 i, bool val) const
287     {
288     const uint32 clrWord = !val * ~0;
289     const uint32 * wp = bmap + (i>>5);
290     uint32 b = Bit(i);
291    
292     wp -= !(i & 0x1f);
293    
294     while (i < len) {
295     if (!(i & 0x1f)) {
296     while (*++wp == clrWord)
297     if ((i += 0x20) >= len)
298     return ABMend;
299     b = 1;
300     }
301     if (((*wp & b) != 0) == val)
302     return i;
303     b <<= 1;
304     ++i;
305     }
306     return ABMend;
307     }
308    
309     // Shift bits downward in bitmap, zero fill
310     ABitMap &
311     ABitMap::operator>>=(uint32 nbits)
312     {
313     if (!nbits)
314     return *this;
315     if (nbits >= len) {
316     ClearBitMap();
317     return *this;
318     }
319     moveBits(bmap, 0, bmap, nbits, len-nbits);
320     ClearBits(len-nbits, nbits);
321     return *this;
322     }
323    
324     // Shift bits upwards in bitmap, zero fill
325     ABitMap &
326     ABitMap::operator<<=(uint32 nbits)
327     {
328     if (!nbits)
329     return *this;
330     if (nbits >= len) {
331     ClearBitMap();
332     return *this;
333     }
334     moveBits(bmap, nbits, bmap, 0, len-nbits);
335     ClearBits(0, nbits);
336     return *this;
337     }
338    
339     // Bitmap copy operator
340     ABitMap &
341     ABitMap::operator=(const ABitMap &src)
342     {
343     if (this == &src)
344     return *this;
345     int32 nwords = src.bmlen();
346     if (nwords != bmlen()) {
347     delete [] bmap;
348     if (nwords) bmap = new uint32 [nwords];
349     else bmap = 0;
350     }
351     len = src.len;
352     memcpy(bmap, src.bmap, nwords*sizeof(uint32));
353     return *this;
354     }
355    
356     // Bitmap OR-copy operator (reverts to copy for different size bitmaps)
357     ABitMap &
358     ABitMap::operator|=(const ABitMap &src)
359     {
360     if (this == &src)
361     return *this;
362     if (len != src.len)
363     return *this = src;
364     const int32 nwords = bmlen();
365     const uint32 * owp = src.bmap + nwords;
366     uint32 * wp = bmap + nwords;
367     while (wp > bmap)
368     *--wp |= *--owp;
369     return *this;
370     }
371    
372     // Bitmap AND-assign operator (no effect for different size bitmaps)
373     ABitMap &
374     ABitMap::operator&=(const ABitMap &src)
375     {
376     if (this == &src)
377     return *this;
378     if (len != src.len)
379     return *this;
380     const int32 nwords = bmlen();
381     const uint32 * owp = src.bmap + nwords;
382     uint32 * wp = bmap + nwords;
383     while (wp > bmap)
384     *--wp &= *--owp;
385     return *this;
386     }
387    
388     // Bitmap XOR-assign operator (no effect for different size bitmaps)
389     ABitMap &
390     ABitMap::operator^=(const ABitMap &src)
391     {
392     if (this == &src) {
393     ClearBitMap();
394     return *this;
395     }
396     if (len != src.len)
397     return *this;
398     const int32 nwords = bmlen();
399     const uint32 * owp = src.bmap + nwords;
400     uint32 * wp = bmap + nwords;
401     while (wp > bmap)
402     *--wp ^= *--owp;
403     return *this;
404     }
405    
406     // Clear bits set in second bitmap
407     bool
408     ABitMap::ClearBitsFrom(const ABitMap &src)
409     {
410     if (this == &src) {
411     ClearBitMap();
412     return true;
413     }
414     if (src.len != len)
415     return false;
416     uint32 * wp = bmap + bmlen();
417     const uint32 * sp = src.bmap + src.bmlen();
418     while (wp > bmap)
419     *--wp &= ~*--sp;
420     return true;
421     }
422    
423     // Compare two bitmaps for equality
424     bool
425     ABitMap::operator==(const ABitMap &that) const
426     {
427     if (this == &that)
428     return true;
429     if (len != that.len)
430     return false;
431     if (!len)
432     return true;
433     const int nwords = len >> 5;
434     const uint32 * owp = that.bmap + nwords;
435     const uint32 * wp = bmap + nwords;
436     if (len & 0x1f && (*--wp ^ *--owp) & (1<<(len&0x1f))-1)
437     return false;
438     while (wp > bmap)
439     if (*--wp != *--owp)
440     return false;
441     return true;
442     }
443    
444     /*************** Run-length compander section ***************
445     * First bit is:
446     * 0 => non-run
447     * 1 => run
448     * Next N "0" bits indicate counter length-3, "1"-terminated:
449     * 001 => e.g., 5-bit counter follows
450     * Next N+3 bits constitute counter M, with implied "1" in leftmost bit
451     * 00001 => e.g., 33 run or non-run length
452     * Next bit is "0" for a run of M 0's, or "1" for a run of 1's,
453     * OR M bits of non-run data.
454     *
455     * Example 1: "00101101" => "0 1 000 00101101"
456     * Example 2: 49 1's => "1 001 10001 1"
457     * Example 3: 7326 0's => "1 0000000001 110010011110 0"
458     *
459     * Note that any run or non-run must span 8 bits or more in source, and
460     * will take up at least 6 encoded bits for a run and 13 for a non-run.
461     * Encoding a bitmap < 72 bits long is never a win, and < 8 is
462     * not possible. A bitmap <= 64 bits in length will be passed as is.
463     * Decoding is trivial compared to logical constraints during encode.
464     */
465    
466     #define RLEmagic 0x1700A9A5 // magic number (32-bits)
467     #define MinCntB 3 // minimum counter length
468     #define MinCnt (1<<MinCntB) // minimum encodable bit sequence
469    
470     // Get original bitmap length if RLE (or 0)
471     uint32
472     ABitMap::RLength() const
473     {
474     if (bmlen() < 3 || bmap[0] != RLEmagic) return 0;
475     return bmap[1];
476     }
477    
478     // Compress into a run-length encoded bitmap
479     bool
480     ABitMap::GetRLE(ABitMap *rlep) const
481     {
482     if (!rlep)
483     return false;
484     if (RLength()) // already encoded?
485     return false;
486     if (len <= 64) { // don't bother?
487     *rlep = *this;
488     return len;
489     }
490     // create draft bitmap
491     ABitMap tmap(len + len/50 + 128);
492     tmap.bmap[0] = RLEmagic; // mark as RLE
493     tmap.bmap[1] = len; // record original length
494     uint32 i=0, o=64; // encode bits
495     while (i < len) {
496     uint32 cnt, cbex;
497     uint32 start = i++; // check for usable run
498     if (!Find(&i, !Check(start)))
499     i = len;
500     else if (i > len - MinCnt)
501     i = len - MinCnt;
502    
503     if (i >= start + MinCnt) {
504     tmap.Set(o++); // encode a run
505     cnt = (i - start)>>MinCntB;
506     cbex = MinCntB; // counter width
507     while (cnt > 1) {
508     o++;
509     cbex++;
510     cnt >>= 1;
511     }
512     tmap.Set(o++); // 1 terminator, then count
513     cnt = i - start;
514     while (cbex-- > 0) {
515     if (cnt & 1<<cbex) tmap.Set(o);
516     ++o;
517     } // followed by repeat bit
518     if (Check(start))
519     tmap.Set(o);
520     ++o;
521     continue; // move on to next
522     }
523     i = start + MinCnt; // else encode non-run
524     if (i + MinCnt > len)
525     i = len; // non-run to end
526     while (i < len) { // stop at next useful run
527     bool cand = Check(i);
528     uint32 candStart = i;
529     while (++i < len && Check(i) == cand)
530     if (i - candStart > 2*(2+MinCntB)+1) {
531     i = candStart;
532     candStart = ABMend;
533     break;
534     }
535     if (candStart == ABMend)
536     break; // found run worth stopping for
537     }
538     o++; // encode our non-run
539     cnt = (i - start)>>MinCntB;
540     if (!cnt)
541     goto calamity; // should never occur!
542     cbex = MinCntB; // counter width
543     while (cnt > 1) {
544     o++;
545     cbex++;
546     cnt >>= 1;
547     }
548     tmap.Set(o++); // 1 terminator, then count
549     cnt = i - start;
550     while (cbex-- > 0) {
551     if (cnt & 1<<cbex) tmap.Set(o);
552     ++o;
553     } // finally, copy bit data
554     if (o + cnt > tmap.len)
555     goto calamity; // over-ran temp buffer!
556     moveBits(tmap.bmap, o, bmap, start, cnt);
557     o += cnt; // onwards...
558     }
559     // copy to right-sized array
560     if (rlep->NewBitMap(o) && tmap.GetBits(rlep, 0))
561     return true;
562     calamity: // XXX should really speak up if we get here!
563     return false;
564     }
565    
566     // Reconstitute bits from RLE encoding
567     bool
568     ABitMap::SetFromRLE(const ABitMap &rle)
569     {
570     if (rle.len <= 64) { // never was encoded?
571     *this = rle;
572     return len;
573     }
574     if (&rle == this) { // cuidado!
575     ABitMap tmap;
576     return tmap.Take(this) && SetFromRLE(tmap);
577     }
578     if (!NewBitMap(rle.RLength())) // start from 0's
579     return false;
580     uint32 i=64, o=0; // decode bits
581     while (i < rle.len) {
582     bool isrun = rle.Check(i++);
583     int cntlen = MinCntB;
584     while (!rle.Check(i++)) cntlen++;
585     if (cntlen > 31)
586     return false;
587     uint32 rlen = 1; // get output count
588     while (cntlen--)
589     rlen = (rlen<<1) + rle.Check(i++);
590     if (!isrun) { // copy bits
591     if ((i+rlen > rle.len) | (o+rlen > len))
592     return false;
593     moveBits(bmap, o, rle.bmap, i, rlen);
594     i += rlen;
595     } else if (rle.Check(i++))
596     ClearBits(o, rlen, true);
597    
598     if ((o += rlen) >= len) // advance output index
599     break;
600     }
601     return (i == rle.len) & (o == len);
602     }
603    
604     /*************** 2-D bitmap section ***************/
605    
606     // Reconstitute bits from RLE encoding (size must match)
607     bool
608     ABitMap2::SetFromRLE(int w, int h, const ABitMap &rle)
609     {
610     uint32 orig_len = rle.RLength();
611    
612     if ((w <= 0) | (h <= 0) | (orig_len != (uint32)w*h))
613     return false;
614    
615     if (!ABitMap::SetFromRLE(rle))
616     return false;
617    
618     width = w; height = h;
619     return true;
620     }
621    
622     // Extract bitmap section, true if some overlap
623     bool
624     ABitMap2::GetRect(ABitMap2 *dp, int sx, int sy) const
625     {
626     if (!dp | (dp == this))
627     return false;
628     if ((sx >= width) | (sy >= height))
629     return false;
630     if (dp->width <= 0 && !dp->NewBitMap(width-sx, height-sy))
631     return false;
632     if (!sx & !sy & (dp->width == width) & (dp->height == height)) {
633     *dp = *this;
634     return true;
635     }
636     int dx=0, dy=0;
637     if (sx < 0) {
638     if ((dx = -sx) >= dp->width) return false;
639     sx = 0;
640     }
641     if (sy < 0) {
642     if ((dy = -sy) >= dp->height) return false;
643     sy = 0;
644     }
645     const int rowwidth = (dp->width-dx > width-sx) ? width-sx : dp->width-dx;
646     int rowcount = (dp->height-dy > height-sy) ? height-sy : dp->height-dy;
647     if ((rowwidth == width) & (width == dp->width))
648     moveBits(dp->base(), dp->width*dy, base(), width*sy, rowwidth*rowcount);
649     else
650     while (rowcount--)
651     moveBits(dp->base(), dp->width*dy++ + dx,
652     base(), width*sy++ + sx, rowwidth);
653     return true;
654     }
655    
656     // Assign bitmap section (ignores anything past edges)
657     bool
658     ABitMap2::AssignRect(int dx, int dy, const ABitMap2 &src)
659     {
660     if (src.width <= 0)
661     return true;
662     if ((dx >= width) | (dy >= height))
663     return false;
664     if (!dx & !dy && (src.width == width) & (src.height == height)) {
665     *this = src;
666     return true;
667     }
668     int sx=0, sy=0;
669     int w=src.width, h=src.height;
670     if (dx < 0) {
671     if ((sx = -dx) >= w) return false;
672     dx = 0; w -= sx;
673     }
674     if (dy < 0) {
675     if ((sy = -dy) >= h) return false;
676     dy = 0; h -= sy;
677     }
678     if (dx+w > width) w = width - dx;
679     if (dy+h > height) h = height - dy;
680     if ((w <= 0) | (h <= 0))
681     return false;
682     if ((w == width) & (width == src.width))
683     moveBits(base(), width*dy, src.base(), src.width*sy, w*h);
684     else
685     while (h--)
686     moveBits(base(), width*dy++ + dx,
687     src.base(), src.width*sy++ + sx, w);
688     return true;
689     }
690    
691     // Apply operation to bitmap section
692     bool
693     ABitMap2::OpRect(int dx, int dy, char op, const ABitMap2 &src)
694     {
695     if ((src.width <= 0) | (width <= 0))
696     return false;
697     if (op == '=')
698     return AssignRect(dx, dy, src);
699     ABitMap2 rbits(src.width, src.height);
700     if (!GetRect(&rbits, dx, dy))
701     return false;
702     switch (op) {
703     case '|':
704     rbits |= src;
705     break;
706     case '&':
707     rbits &= src;
708     break;
709     case '^':
710     rbits ^= src;
711     break;
712     case '-':
713     case '>':
714     rbits -= src;
715     break;
716     case '<':
717     rbits.Invert();
718     rbits &= src;
719     break;
720     default:
721     return false;
722     }
723     return AssignRect(dx, dy, rbits);
724     }
725    
726     // Clear a rectangle
727     void
728     ABitMap2::ClearRect(int x, int y, int w, int h, bool clrset)
729     {
730     if (x < 0) { w += x; x = 0; }
731     if (y < 0) { h += y; y = 0; }
732     if (w > width - x)
733     w = width - x;
734     if (w <= 0)
735     return;
736     if (h > height - y)
737     h = height - y;
738     if (h <= 0)
739     return;
740     if (w == width) // contiguous case
741     ClearBits(y*width, h*width, clrset);
742     else
743     while (h--) // discontiguous
744     ClearBits(bmi(x,y++), w, clrset);
745     }
746    
747     // Get bounds of assigned region
748     bool
749     ABitMap2::GetBoundRect(int xymin[2], int wh[2], bool val) const
750     {
751     int x=0, y=0;
752    
753     if (!xymin | !wh)
754     return false;
755     if (!Find(&x, &y, val)) {
756     xymin[0] = xymin[1] = wh[0] = wh[1] = 0;
757     return false;
758     }
759     xymin[0] = x; xymin[1] = y;
760     int xymax[2] = {x, y};
761     do {
762     if (x < xymin[0]) xymin[0] = x;
763     xymax[1] = y;
764    
765     if (Check(width-1, y) == val) {
766     xymax[0] = width-1;
767     x = 0; ++y;
768     continue;
769     }
770     if (++x < xymax[0]) x = xymax[0];
771     Find(&x, &y, !val); // never false after above Check()
772     if (x-1 > xymax[0]) xymax[0] = x-1;
773     } while (Find(&x, &y, val));
774    
775     wh[0] = xymax[0] - xymin[0] + 1;
776     wh[1] = xymax[1] - xymin[1] + 1;
777     return true;
778     }
779    
780     // Shift bitmap image right-left and down-up, filling as indicated
781     void
782     ABitMap2::Shift(int dx, int dy, int fill)
783     {
784     int adx = (dx > 0) ? dx : -dx;
785     int ady = (dy > 0) ? dy : -dy;
786     if ((adx >= width) | (ady >= height)) {
787     if (fill >= 0)
788     ClearBitMap(fill);
789     return;
790     }
791     int bitshift = dy*width + dx;
792     if (bitshift > 0) // shift underlying bits
793     operator<<=(bitshift);
794     else if (bitshift < 0)
795     operator>>=(-bitshift);
796     else
797     return;
798     if (fill < 0) // no fill -- we're done
799     return;
800     int hmarg[4]; // new horizontal margin
801     hmarg[0] = (dx < 0) ? width-adx : 0;
802     hmarg[1] = (dy > 0) ? dy : 0;
803     hmarg[2] = adx;
804     hmarg[3] = height - ady;
805     if (fill) { // fill corner with 1's
806     ClearRect(hmarg[0], hmarg[1], hmarg[2], hmarg[3], true);
807     ClearRect(0, (dy < 0) ? height-ady : 0, width, ady, true);
808     } else { // fill side with 0's
809     ClearRect(hmarg[0], hmarg[1]-1, hmarg[2], hmarg[3]+2);
810     }
811     }
812    
813     static inline int
814     iSqrt(double x)
815     {
816     if (x <= 0) return 0;
817     return int(sqrt(x) + .5);
818     }
819    
820     // Dilate (or erode) selection by given radius
821     void
822     ABitMap2::Expand(double rad, bool val)
823     {
824     if (rad < 0) {
825     rad = -rad;
826     val = !val;
827     }
828     if ((width <= 0) | (rad < 1))
829     return;
830     // check what we have here
831     int xyorg[2], wh[2];
832     if (!GetBoundRect(xyorg, wh, val))
833     return; // empty bitmap!
834     const int wh_orig[2] = {width, height};
835     const int irad = int(rad+.5);
836     // optimize if >= 3/4 empty
837     if ((width >= 128) & (height >= 128) &&
838     ((wh[0] += irad<<1) <= width>>1) &
839     ((wh[1] += irad<<1) <= height>>1)) {
840     ABitMap2 subRgn(wh[0], wh[1], !val);
841     GetRect(&subRgn, xyorg[0] -= irad, xyorg[1] -= irad);
842     Take(&subRgn); // work with subregion
843     }
844     // copy original pattern
845     ABitMap2 origMap = *this;
846     // stamp out larger one
847     for (int y = -irad; y <= irad; y++) {
848     const int spanRad = iSqrt(rad*rad - y*y);
849     for (int x = -spanRad; x <= spanRad; x++) {
850     if (!x & !y) continue;
851     ABitMap2 stamp = origMap;
852     stamp.Shift(x, y, !val);
853     if (val)
854     *this |= stamp;
855     else
856     *this &= stamp;
857     }
858     }
859     if ((width == wh_orig[0]) & (height == wh_orig[1]))
860     return;
861     // restore original dimensions
862     origMap.NewBitMap(wh_orig[0], wh_orig[1], !val) &&
863     origMap.AssignRect(xyorg[0], xyorg[1], *this) &&
864     Take(&origMap);
865     }