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

# Content
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 }