ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/abitmap.cpp
Revision: 2.1
Committed: Wed Aug 14 20:05:23 2024 UTC (8 months, 2 weeks ago) by greg
Branch: MAIN
Log Message:
feat(rxpict): Added new C++ picture rendering tool with multi-processing and spectral output

File Contents

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