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