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

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id$";
3 #endif
4 /*
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 }