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