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