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