Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 1 | pmbaty | 1 | /* |
| 2 | * This file is part of the DXX-Rebirth project <https://www.dxx-rebirth.com/>. |
||
| 3 | * It is copyright by its individual contributors, as recorded in the |
||
| 4 | * project's Git history. See COPYING.txt at the top level for license |
||
| 5 | * terms and a link to the Git history. |
||
| 6 | */ |
||
| 7 | /* 8 bit decoding routines */ |
||
| 8 | |||
| 9 | #include <cstdint> |
||
| 10 | #include <stdio.h> |
||
| 11 | #include <string.h> |
||
| 12 | |||
| 13 | #include "decoders.h" |
||
| 14 | #include "console.h" |
||
| 15 | |||
| 16 | #include "dxxsconf.h" |
||
| 17 | #include "compiler-range_for.h" |
||
| 18 | #include "d_range.h" |
||
| 19 | #include <array> |
||
| 20 | |||
| 21 | static void dispatchDecoder(unsigned char **pFrame, unsigned char codeType, const unsigned char **pData, int *pDataRemain, int *curXb, int *curYb); |
||
| 22 | |||
| 23 | void decodeFrame8(unsigned char *pFrame, const unsigned char *pMap, int mapRemain, const unsigned char *pData, int dataRemain) |
||
| 24 | { |
||
| 25 | int xb, yb; |
||
| 26 | |||
| 27 | xb = g_width >> 3; |
||
| 28 | yb = g_height >> 3; |
||
| 29 | for (int j=0; j<yb; j++) |
||
| 30 | { |
||
| 31 | for (int i=0; i<xb/2; i++) |
||
| 32 | { |
||
| 33 | dispatchDecoder(&pFrame, (*pMap) & 0xf, &pData, &dataRemain, &i, &j); |
||
| 34 | if (pFrame < g_vBackBuf1) |
||
| 35 | con_printf(CON_CRITICAL, "danger! pointing out of bounds below after dispatch decoder: %d, %d (1) [%x]", i, j, (*pMap) & 0xf); |
||
| 36 | else if (pFrame >= g_vBackBuf1 + g_width*g_height) |
||
| 37 | con_printf(CON_CRITICAL, "danger! pointing out of bounds above after dispatch decoder: %d, %d (1) [%x]", i, j, (*pMap) & 0xf); |
||
| 38 | dispatchDecoder(&pFrame, (*pMap) >> 4, &pData, &dataRemain, &i, &j); |
||
| 39 | if (pFrame < g_vBackBuf1) |
||
| 40 | con_printf(CON_CRITICAL, "danger! pointing out of bounds below after dispatch decoder: %d, %d (2) [%x]", i, j, (*pMap) >> 4); |
||
| 41 | else if (pFrame >= g_vBackBuf1 + g_width*g_height) |
||
| 42 | con_printf(CON_CRITICAL, "danger! pointing out of bounds above after dispatch decoder: %d, %d (2) [%x]", i, j, (*pMap) >> 4); |
||
| 43 | |||
| 44 | ++pMap; |
||
| 45 | --mapRemain; |
||
| 46 | } |
||
| 47 | |||
| 48 | pFrame += 7*g_width; |
||
| 49 | } |
||
| 50 | } |
||
| 51 | |||
| 52 | static void relClose(int i, int *x, int *y) |
||
| 53 | { |
||
| 54 | int ma, mi; |
||
| 55 | |||
| 56 | ma = i >> 4; |
||
| 57 | mi = i & 0xf; |
||
| 58 | |||
| 59 | *x = mi - 8; |
||
| 60 | *y = ma - 8; |
||
| 61 | } |
||
| 62 | |||
| 63 | static void relFar(int i, int sign, int *x, int *y) |
||
| 64 | { |
||
| 65 | if (i < 56) |
||
| 66 | { |
||
| 67 | *x = sign * (8 + (i % 7)); |
||
| 68 | *y = sign * (i / 7); |
||
| 69 | } |
||
| 70 | else |
||
| 71 | { |
||
| 72 | *x = sign * (-14 + (i - 56) % 29); |
||
| 73 | *y = sign * (8 + (i - 56) / 29); |
||
| 74 | } |
||
| 75 | } |
||
| 76 | |||
| 77 | /* copies an 8x8 block from pSrc to pDest. |
||
| 78 | pDest and pSrc are both g_width bytes wide */ |
||
| 79 | static void copyFrame(uint8_t *pDest, const uint8_t *pSrc) |
||
| 80 | { |
||
| 81 | const auto width = g_width; |
||
| 82 | range_for (const int i, xrange(8u)) |
||
| 83 | { |
||
| 84 | (void)i; |
||
| 85 | memcpy(pDest, pSrc, 8); |
||
| 86 | pDest += width; |
||
| 87 | pSrc += width; |
||
| 88 | } |
||
| 89 | } |
||
| 90 | |||
| 91 | // Fill in the next eight bytes with p[0], p[1], p[2], or p[3], |
||
| 92 | // depending on the corresponding two-bit value in pat0 and pat1 |
||
| 93 | static void patternRow4Pixels(unsigned char *pFrame, |
||
| 94 | unsigned char pat0, unsigned char pat1, |
||
| 95 | const std::array<uint8_t, 4> &p) |
||
| 96 | { |
||
| 97 | unsigned short mask=0x0003; |
||
| 98 | unsigned short shift=0; |
||
| 99 | unsigned short pattern = (pat1 << 8) | pat0; |
||
| 100 | |||
| 101 | while (mask != 0) |
||
| 102 | { |
||
| 103 | *pFrame++ = p[(mask & pattern) >> shift]; |
||
| 104 | mask <<= 2; |
||
| 105 | shift += 2; |
||
| 106 | } |
||
| 107 | } |
||
| 108 | |||
| 109 | // Fill in the next four 2x2 pixel blocks with p[0], p[1], p[2], or p[3], |
||
| 110 | // depending on the corresponding two-bit value in pat0. |
||
| 111 | static void patternRow4Pixels2(unsigned char *pFrame, |
||
| 112 | unsigned char pat0, |
||
| 113 | const std::array<uint8_t, 4> &p) |
||
| 114 | { |
||
| 115 | unsigned char mask=0x03; |
||
| 116 | unsigned char shift=0; |
||
| 117 | unsigned char pel; |
||
| 118 | |||
| 119 | const auto width = g_width; |
||
| 120 | while (mask != 0) |
||
| 121 | { |
||
| 122 | pel = p[(mask & pat0) >> shift]; |
||
| 123 | pFrame[0] = pel; |
||
| 124 | pFrame[1] = pel; |
||
| 125 | pFrame[width + 0] = pel; |
||
| 126 | pFrame[width + 1] = pel; |
||
| 127 | pFrame += 2; |
||
| 128 | mask <<= 2; |
||
| 129 | shift += 2; |
||
| 130 | } |
||
| 131 | } |
||
| 132 | |||
| 133 | // Fill in the next four 2x1 pixel blocks with p[0], p[1], p[2], or p[3], |
||
| 134 | // depending on the corresponding two-bit value in pat. |
||
| 135 | static void patternRow4Pixels2x1(unsigned char *pFrame, unsigned char pat, const std::array<uint8_t, 4> &p) |
||
| 136 | { |
||
| 137 | unsigned char mask=0x03; |
||
| 138 | unsigned char shift=0; |
||
| 139 | unsigned char pel; |
||
| 140 | |||
| 141 | while (mask != 0) |
||
| 142 | { |
||
| 143 | pel = p[(mask & pat) >> shift]; |
||
| 144 | pFrame[0] = pel; |
||
| 145 | pFrame[1] = pel; |
||
| 146 | pFrame += 2; |
||
| 147 | mask <<= 2; |
||
| 148 | shift += 2; |
||
| 149 | } |
||
| 150 | } |
||
| 151 | |||
| 152 | // Fill in the next 4x4 pixel block with p[0], p[1], p[2], or p[3], |
||
| 153 | // depending on the corresponding two-bit value in pat0, pat1, pat2, and pat3. |
||
| 154 | static void patternQuadrant4Pixels(unsigned char *pFrame, unsigned char pat0, unsigned char pat1, unsigned char pat2, unsigned char pat3, const std::array<uint8_t, 4> &p) |
||
| 155 | { |
||
| 156 | unsigned long mask = 0x00000003UL; |
||
| 157 | int shift=0; |
||
| 158 | unsigned long pat = (pat3 << 24) | (pat2 << 16) | (pat1 << 8) | pat0; |
||
| 159 | |||
| 160 | const auto width = g_width; |
||
| 161 | range_for (const int i, xrange(16u)) |
||
| 162 | { |
||
| 163 | pFrame[i&3] = p[(pat & mask) >> shift]; |
||
| 164 | |||
| 165 | if ((i&3) == 3) |
||
| 166 | pFrame += width; |
||
| 167 | |||
| 168 | mask <<= 2; |
||
| 169 | shift += 2; |
||
| 170 | } |
||
| 171 | } |
||
| 172 | |||
| 173 | // fills the next 8 pixels with either p[0] or p[1], depending on pattern |
||
| 174 | static void patternRow2Pixels(unsigned char *pFrame, unsigned char pat, const std::array<uint8_t, 4> &p) |
||
| 175 | { |
||
| 176 | unsigned char mask=0x01; |
||
| 177 | |||
| 178 | while (mask != 0) |
||
| 179 | { |
||
| 180 | *pFrame++ = p[(mask & pat) ? 1 : 0]; |
||
| 181 | mask <<= 1; |
||
| 182 | } |
||
| 183 | } |
||
| 184 | |||
| 185 | // fills the next four 2 x 2 pixel boxes with either p[0] or p[1], depending on pattern |
||
| 186 | static void patternRow2Pixels2(unsigned char *pFrame, unsigned char pat, const std::array<uint8_t, 4> &p) |
||
| 187 | { |
||
| 188 | unsigned char pel; |
||
| 189 | unsigned char mask=0x1; |
||
| 190 | |||
| 191 | const auto width = g_width; |
||
| 192 | while (mask != 0x10) |
||
| 193 | { |
||
| 194 | pel = p[(mask & pat) ? 1 : 0]; |
||
| 195 | |||
| 196 | pFrame[0] = pel; // upper-left |
||
| 197 | pFrame[1] = pel; // upper-right |
||
| 198 | pFrame[width + 0] = pel; // lower-left |
||
| 199 | pFrame[width + 1] = pel; // lower-right |
||
| 200 | pFrame += 2; |
||
| 201 | |||
| 202 | mask <<= 1; |
||
| 203 | } |
||
| 204 | } |
||
| 205 | |||
| 206 | // fills pixels in the next 4 x 4 pixel boxes with either p[0] or p[1], depending on pat0 and pat1. |
||
| 207 | static void patternQuadrant2Pixels(unsigned char *pFrame, unsigned char pat0, unsigned char pat1, const std::array<uint8_t, 4> &p) |
||
| 208 | { |
||
| 209 | unsigned char pel; |
||
| 210 | unsigned short mask = 0x0001; |
||
| 211 | unsigned short pat = (pat1 << 8) | pat0; |
||
| 212 | |||
| 213 | const auto width = g_width; |
||
| 214 | range_for (const int i, xrange(4u)) |
||
| 215 | { |
||
| 216 | range_for (const int j, xrange(4u)) |
||
| 217 | { |
||
| 218 | pel = p[(pat & mask) ? 1 : 0]; |
||
| 219 | |||
| 220 | pFrame[j + i * width] = pel; |
||
| 221 | |||
| 222 | mask <<= 1; |
||
| 223 | } |
||
| 224 | } |
||
| 225 | } |
||
| 226 | |||
| 227 | static void dispatchDecoder(unsigned char **pFrame, unsigned char codeType, const unsigned char **pData, int *pDataRemain, int *curXb, int *curYb) |
||
| 228 | { |
||
| 229 | std::array<uint8_t, 4> p, pat; |
||
| 230 | int x, y; |
||
| 231 | |||
| 232 | /* Data is processed in 8x8 pixel blocks. |
||
| 233 | There are 16 ways to encode each block. |
||
| 234 | */ |
||
| 235 | |||
| 236 | switch(codeType) |
||
| 237 | { |
||
| 238 | case 0x0: |
||
| 239 | /* block is copied from block in current frame */ |
||
| 240 | copyFrame(*pFrame, *pFrame + (g_vBackBuf2 - g_vBackBuf1)); |
||
| 241 | DXX_BOOST_FALLTHROUGH; |
||
| 242 | case 0x1: |
||
| 243 | /* block is unchanged from two frames ago */ |
||
| 244 | *pFrame += 8; |
||
| 245 | break; |
||
| 246 | |||
| 247 | case 0x2: |
||
| 248 | /* Block is copied from nearby (below and/or to the right) within the |
||
| 249 | new frame. The offset within the buffer from which to grab the |
||
| 250 | patch of 8 pixels is given by grabbing a byte B from the data |
||
| 251 | stream, which is broken into a positive x and y offset according |
||
| 252 | to the following mapping: |
||
| 253 | |||
| 254 | if B < 56: |
||
| 255 | x = 8 + (B % 7) |
||
| 256 | y = B / 7 |
||
| 257 | else |
||
| 258 | x = -14 + ((B - 56) % 29) |
||
| 259 | y = 8 + ((B - 56) / 29) |
||
| 260 | */ |
||
| 261 | relFar(*(*pData)++, 1, &x, &y); |
||
| 262 | copyFrame(*pFrame, *pFrame + x + y*g_width); |
||
| 263 | *pFrame += 8; |
||
| 264 | --*pDataRemain; |
||
| 265 | break; |
||
| 266 | |||
| 267 | case 0x3: |
||
| 268 | /* Block is copied from nearby (above and/or to the left) within the |
||
| 269 | new frame. |
||
| 270 | |||
| 271 | if B < 56: |
||
| 272 | x = -(8 + (B % 7)) |
||
| 273 | y = -(B / 7) |
||
| 274 | else |
||
| 275 | x = -(-14 + ((B - 56) % 29)) |
||
| 276 | y = -( 8 + ((B - 56) / 29)) |
||
| 277 | */ |
||
| 278 | relFar(*(*pData)++, -1, &x, &y); |
||
| 279 | copyFrame(*pFrame, *pFrame + x + y*g_width); |
||
| 280 | *pFrame += 8; |
||
| 281 | --*pDataRemain; |
||
| 282 | break; |
||
| 283 | |||
| 284 | case 0x4: |
||
| 285 | /* Similar to 0x2 and 0x3, except this method copies from the |
||
| 286 | "current" frame, rather than the "new" frame, and instead of the |
||
| 287 | lopsided mapping they use, this one uses one which is symmetric |
||
| 288 | and centered around the top-left corner of the block. This uses |
||
| 289 | only 1 byte still, though, so the range is decreased, since we |
||
| 290 | have to encode all directions in a single byte. The byte we pull |
||
| 291 | from the data stream, I'll call B. Call the highest 4 bits of B |
||
| 292 | BH and the lowest 4 bytes BL. Then the offset from which to copy |
||
| 293 | the data is: |
||
| 294 | |||
| 295 | x = -8 + BL |
||
| 296 | y = -8 + BH |
||
| 297 | */ |
||
| 298 | relClose(*(*pData)++, &x, &y); |
||
| 299 | copyFrame(*pFrame, *pFrame + (g_vBackBuf2 - g_vBackBuf1) + x + y*g_width); |
||
| 300 | *pFrame += 8; |
||
| 301 | --*pDataRemain; |
||
| 302 | break; |
||
| 303 | |||
| 304 | case 0x5: |
||
| 305 | /* Similar to 0x4, but instead of one byte for the offset, this uses |
||
| 306 | two bytes to encode a larger range, the first being the x offset |
||
| 307 | as a signed 8-bit value, and the second being the y offset as a |
||
| 308 | signed 8-bit value. |
||
| 309 | */ |
||
| 310 | x = static_cast<int8_t>(*(*pData)++); |
||
| 311 | y = static_cast<int8_t>(*(*pData)++); |
||
| 312 | copyFrame(*pFrame, *pFrame + (g_vBackBuf2 - g_vBackBuf1) + x + y*g_width); |
||
| 313 | *pFrame += 8; |
||
| 314 | *pDataRemain -= 2; |
||
| 315 | break; |
||
| 316 | |||
| 317 | case 0x6: |
||
| 318 | /* I can't figure out how any file containing a block of this type |
||
| 319 | could still be playable, since it appears that it would leave the |
||
| 320 | internal bookkeeping in an inconsistent state in the BG player |
||
| 321 | code. Ahh, well. Perhaps it was a bug in the BG player code that |
||
| 322 | just didn't happen to be exposed by any of the included movies. |
||
| 323 | Anyway, this skips the next two blocks, doing nothing to them. |
||
| 324 | Note that if you've reached the end of a row, this means going on |
||
| 325 | to the next row. |
||
| 326 | */ |
||
| 327 | { |
||
| 328 | const auto width = g_width; |
||
| 329 | range_for (const int i, xrange(2u)) |
||
| 330 | { |
||
| 331 | (void)i; |
||
| 332 | *pFrame += 16; |
||
| 333 | if (++*curXb == (width >> 3)) |
||
| 334 | { |
||
| 335 | *pFrame += 7 * width; |
||
| 336 | *curXb = 0; |
||
| 337 | if (++*curYb == (g_height >> 3)) |
||
| 338 | return; |
||
| 339 | } |
||
| 340 | } |
||
| 341 | } |
||
| 342 | break; |
||
| 343 | |||
| 344 | case 0x7: |
||
| 345 | /* Ok, here's where it starts to get really...interesting. This is, |
||
| 346 | incidentally, the part where they started using self-modifying |
||
| 347 | code. So, most of the following encodings are "patterned" blocks, |
||
| 348 | where we are given a number of pixel values and then bitmapped |
||
| 349 | values to specify which pixel values belong to which squares. For |
||
| 350 | this encoding, we are given the following in the data stream: |
||
| 351 | |||
| 352 | P0 P1 |
||
| 353 | |||
| 354 | These are pixel values (i.e. 8-bit indices into the palette). If |
||
| 355 | P0 <= P1, we then get 8 more bytes from the data stream, one for |
||
| 356 | each row in the block: |
||
| 357 | |||
| 358 | B0 B1 B2 B3 B4 B5 B6 B7 |
||
| 359 | |||
| 360 | For each row, the leftmost pixel is represented by the low-order |
||
| 361 | bit, and the rightmost by the high-order bit. Use your imagination |
||
| 362 | in between. If a bit is set, the pixel value is P1 and if it is |
||
| 363 | unset, the pixel value is P0. |
||
| 364 | |||
| 365 | So, for example, if we had: |
||
| 366 | |||
| 367 | 11 22 fe 83 83 83 83 83 83 fe |
||
| 368 | |||
| 369 | This would represent the following layout: |
||
| 370 | |||
| 371 | 11 22 22 22 22 22 22 22 ; fe == 11111110 |
||
| 372 | 22 22 11 11 11 11 11 22 ; 83 == 10000011 |
||
| 373 | 22 22 11 11 11 11 11 22 ; 83 == 10000011 |
||
| 374 | 22 22 11 11 11 11 11 22 ; 83 == 10000011 |
||
| 375 | 22 22 11 11 11 11 11 22 ; 83 == 10000011 |
||
| 376 | 22 22 11 11 11 11 11 22 ; 83 == 10000011 |
||
| 377 | 22 22 11 11 11 11 11 22 ; 83 == 10000011 |
||
| 378 | 11 22 22 22 22 22 22 22 ; fe == 11111110 |
||
| 379 | |||
| 380 | If, on the other hand, P0 > P1, we get two more bytes from the |
||
| 381 | data stream: |
||
| 382 | |||
| 383 | B0 B1 |
||
| 384 | |||
| 385 | Each of these bytes contains two 4-bit patterns. These patterns |
||
| 386 | work like the patterns above with 8 bytes, except each bit |
||
| 387 | represents a 2x2 pixel region. |
||
| 388 | |||
| 389 | B0 contains the pattern for the top two rows and B1 contains |
||
| 390 | the pattern for the bottom two rows. Note that the low-order |
||
| 391 | nibble of each byte contains the pattern for the upper of the |
||
| 392 | two rows that that byte controls. |
||
| 393 | |||
| 394 | So if we had: |
||
| 395 | |||
| 396 | 22 11 7e 83 |
||
| 397 | |||
| 398 | The output would be: |
||
| 399 | |||
| 400 | 11 11 22 22 22 22 22 22 ; e == 1 1 1 0 |
||
| 401 | 11 11 22 22 22 22 22 22 ; |
||
| 402 | 22 22 22 22 22 22 11 11 ; 7 == 0 1 1 1 |
||
| 403 | 22 22 22 22 22 22 11 11 ; |
||
| 404 | 11 11 11 11 11 11 22 22 ; 3 == 1 0 0 0 |
||
| 405 | 11 11 11 11 11 11 22 22 ; |
||
| 406 | 22 22 22 22 11 11 11 11 ; 8 == 0 0 1 1 |
||
| 407 | 22 22 22 22 11 11 11 11 ; |
||
| 408 | */ |
||
| 409 | p[0] = *(*pData)++; |
||
| 410 | p[1] = *(*pData)++; |
||
| 411 | { |
||
| 412 | const auto width = g_width; |
||
| 413 | if (p[0] <= p[1]) |
||
| 414 | { |
||
| 415 | range_for (const int i, xrange(8u)) |
||
| 416 | { |
||
| 417 | (void)i; |
||
| 418 | patternRow2Pixels(*pFrame, *(*pData)++, p); |
||
| 419 | *pFrame += width; |
||
| 420 | } |
||
| 421 | } |
||
| 422 | else |
||
| 423 | { |
||
| 424 | const auto width2 = 2 * width; |
||
| 425 | range_for (const int i, xrange(2u)) |
||
| 426 | { |
||
| 427 | (void)i; |
||
| 428 | patternRow2Pixels2(*pFrame, *(*pData) & 0xf, p); |
||
| 429 | *pFrame += width2; |
||
| 430 | patternRow2Pixels2(*pFrame, *(*pData)++ >> 4, p); |
||
| 431 | *pFrame += width2; |
||
| 432 | } |
||
| 433 | } |
||
| 434 | *pFrame -= (8 * width - 8); |
||
| 435 | } |
||
| 436 | break; |
||
| 437 | |||
| 438 | case 0x8: |
||
| 439 | /* Ok, this one is basically like encoding 0x7, only more |
||
| 440 | complicated. Again, we start out by getting two bytes on the data |
||
| 441 | stream: |
||
| 442 | |||
| 443 | P0 P1 |
||
| 444 | |||
| 445 | if P0 <= P1 then we get the following from the data stream: |
||
| 446 | |||
| 447 | B0 B1 |
||
| 448 | P2 P3 B2 B3 |
||
| 449 | P4 P5 B4 B5 |
||
| 450 | P6 P7 B6 B7 |
||
| 451 | |||
| 452 | P0 P1 and B0 B1 are used for the top-left corner, P2 P3 B2 B3 for |
||
| 453 | the bottom-left corner, P4 P5 B4 B5 for the top-right, P6 P7 B6 B7 |
||
| 454 | for the bottom-right. (So, each codes for a 4x4 pixel array.) |
||
| 455 | Since we have 16 bits in B0 B1, there is one bit for each pixel in |
||
| 456 | the array. The convention for the bit-mapping is, again, left to |
||
| 457 | right and top to bottom. |
||
| 458 | |||
| 459 | So, basically, the top-left quarter of the block is an arbitrary |
||
| 460 | pattern with 2 pixels, the bottom-left a different arbitrary |
||
| 461 | pattern with 2 different pixels, and so on. |
||
| 462 | |||
| 463 | For example if the next 16 bytes were: |
||
| 464 | |||
| 465 | 00 22 f9 9f 44 55 aa 55 11 33 cc 33 66 77 01 ef |
||
| 466 | |||
| 467 | We'd draw: |
||
| 468 | |||
| 469 | 22 22 22 22 | 11 11 33 33 ; f = 1111, c = 1100 |
||
| 470 | 22 00 00 22 | 11 11 33 33 ; 9 = 1001, c = 1100 |
||
| 471 | 22 00 00 22 | 33 33 11 11 ; 9 = 1001, 3 = 0011 |
||
| 472 | 22 22 22 22 | 33 33 11 11 ; f = 1111, 3 = 0011 |
||
| 473 | ------------+------------ |
||
| 474 | 44 55 44 55 | 66 66 66 66 ; a = 1010, 0 = 0000 |
||
| 475 | 44 55 44 55 | 77 66 66 66 ; a = 1010, 1 = 0001 |
||
| 476 | 55 44 55 44 | 66 77 77 77 ; 5 = 0101, e = 1110 |
||
| 477 | 55 44 55 44 | 77 77 77 77 ; 5 = 0101, f = 1111 |
||
| 478 | |||
| 479 | I've added a dividing line in the above to clearly delineate the |
||
| 480 | quadrants. |
||
| 481 | |||
| 482 | |||
| 483 | Now, if P0 > P1 then we get 10 more bytes from the data stream: |
||
| 484 | |||
| 485 | B0 B1 B2 B3 P2 P3 B4 B5 B6 B7 |
||
| 486 | |||
| 487 | Now, if P2 <= P3, then the first six bytes [P0 P1 B0 B1 B2 B3] |
||
| 488 | represent the left half of the block and the latter six bytes |
||
| 489 | [P2 P3 B4 B5 B6 B7] represent the right half. |
||
| 490 | |||
| 491 | For example: |
||
| 492 | |||
| 493 | 22 00 01 37 f7 31 11 66 8c e6 73 31 |
||
| 494 | |||
| 495 | yeilds: |
||
| 496 | |||
| 497 | 22 22 22 22 | 11 11 11 66 ; 0: 0000 | 8: 1000 |
||
| 498 | 00 22 22 22 | 11 11 66 66 ; 1: 0001 | C: 1100 |
||
| 499 | 00 00 22 22 | 11 66 66 66 ; 3: 0011 | e: 1110 |
||
| 500 | 00 00 00 22 | 11 66 11 66 ; 7: 0111 | 6: 0101 |
||
| 501 | 00 00 00 00 | 66 66 66 11 ; f: 1111 | 7: 0111 |
||
| 502 | 00 00 00 22 | 66 66 11 11 ; 7: 0111 | 3: 0011 |
||
| 503 | 00 00 22 22 | 66 66 11 11 ; 3: 0011 | 3: 0011 |
||
| 504 | 00 22 22 22 | 66 11 11 11 ; 1: 0001 | 1: 0001 |
||
| 505 | |||
| 506 | |||
| 507 | On the other hand, if P0 > P1 and P2 > P3, then |
||
| 508 | [P0 P1 B0 B1 B2 B3] represent the top half of the |
||
| 509 | block and [P2 P3 B4 B5 B6 B7] represent the bottom half. |
||
| 510 | |||
| 511 | For example: |
||
| 512 | |||
| 513 | 22 00 cc 66 33 19 66 11 18 24 42 81 |
||
| 514 | |||
| 515 | yeilds: |
||
| 516 | |||
| 517 | 22 22 00 00 22 22 00 00 ; cc: 11001100 |
||
| 518 | 22 00 00 22 22 00 00 22 ; 66: 01100110 |
||
| 519 | 00 00 22 22 00 00 22 22 ; 33: 00110011 |
||
| 520 | 00 22 22 00 00 22 22 22 ; 19: 00011001 |
||
| 521 | ----------------------- |
||
| 522 | 66 66 66 11 11 66 66 66 ; 18: 00011000 |
||
| 523 | 66 66 11 66 66 11 66 66 ; 24: 00100100 |
||
| 524 | 66 11 66 66 66 66 11 66 ; 42: 01000010 |
||
| 525 | 11 66 66 66 66 66 66 11 ; 81: 10000001 |
||
| 526 | */ |
||
| 527 | { |
||
| 528 | const auto width = g_width; |
||
| 529 | if ( (*pData)[0] <= (*pData)[1]) |
||
| 530 | { |
||
| 531 | // four quadrant case |
||
| 532 | range_for (const int i, xrange(4u)) |
||
| 533 | { |
||
| 534 | p[0] = *(*pData)++; |
||
| 535 | p[1] = *(*pData)++; |
||
| 536 | pat[0] = *(*pData)++; |
||
| 537 | pat[1] = *(*pData)++; |
||
| 538 | patternQuadrant2Pixels(*pFrame, pat[0], pat[1], p); |
||
| 539 | |||
| 540 | // alternate between moving down and moving up and right |
||
| 541 | if (i & 1) |
||
| 542 | *pFrame += 4 - 4 * width; // up and right |
||
| 543 | else |
||
| 544 | *pFrame += 4 * width; // down |
||
| 545 | } |
||
| 546 | } |
||
| 547 | else if ( (*pData)[6] <= (*pData)[7]) |
||
| 548 | { |
||
| 549 | // split horizontal |
||
| 550 | range_for (const int i, xrange(4u)) |
||
| 551 | { |
||
| 552 | if ((i & 1) == 0) |
||
| 553 | { |
||
| 554 | p[0] = *(*pData)++; |
||
| 555 | p[1] = *(*pData)++; |
||
| 556 | } |
||
| 557 | pat[0] = *(*pData)++; |
||
| 558 | pat[1] = *(*pData)++; |
||
| 559 | patternQuadrant2Pixels(*pFrame, pat[0], pat[1], p); |
||
| 560 | |||
| 561 | if (i & 1) |
||
| 562 | *pFrame -= (4 * width - 4); |
||
| 563 | else |
||
| 564 | *pFrame += 4 * width; |
||
| 565 | } |
||
| 566 | } |
||
| 567 | else |
||
| 568 | { |
||
| 569 | // split vertical |
||
| 570 | range_for (const int i, xrange(8u)) |
||
| 571 | { |
||
| 572 | if ((i & 3) == 0) |
||
| 573 | { |
||
| 574 | p[0] = *(*pData)++; |
||
| 575 | p[1] = *(*pData)++; |
||
| 576 | } |
||
| 577 | patternRow2Pixels(*pFrame, *(*pData)++, p); |
||
| 578 | *pFrame += width; |
||
| 579 | } |
||
| 580 | *pFrame -= (8 * width - 8); |
||
| 581 | } |
||
| 582 | } |
||
| 583 | break; |
||
| 584 | |||
| 585 | case 0x9: |
||
| 586 | /* Similar to the previous 2 encodings, only more complicated. And |
||
| 587 | it will get worse before it gets better. No longer are we dealing |
||
| 588 | with patterns over two pixel values. Now we are dealing with |
||
| 589 | patterns over 4 pixel values with 2 bits assigned to each pixel |
||
| 590 | (or block of pixels). |
||
| 591 | |||
| 592 | So, first on the data stream are our 4 pixel values: |
||
| 593 | |||
| 594 | P0 P1 P2 P3 |
||
| 595 | |||
| 596 | Now, if P0 <= P1 AND P2 <= P3, we get 16 bytes of pattern, each |
||
| 597 | 2 bits representing a 1x1 pixel (00=P0, 01=P1, 10=P2, 11=P3). The |
||
| 598 | ordering is again left to right and top to bottom. The most |
||
| 599 | significant bits represent the left side at the top, and so on. |
||
| 600 | |||
| 601 | If P0 <= P1 AND P2 > P3, we get 4 bytes of pattern, each 2 bits |
||
| 602 | representing a 2x2 pixel. Ordering is left to right and top to |
||
| 603 | bottom. |
||
| 604 | |||
| 605 | if P0 > P1 AND P2 <= P3, we get 8 bytes of pattern, each 2 bits |
||
| 606 | representing a 2x1 pixel (i.e. 2 pixels wide, and 1 high). |
||
| 607 | |||
| 608 | if P0 > P1 AND P2 > P3, we get 8 bytes of pattern, each 2 bits |
||
| 609 | representing a 1x2 pixel (i.e. 1 pixel wide, and 2 high). |
||
| 610 | */ |
||
| 611 | { |
||
| 612 | const auto width = g_width; |
||
| 613 | if ( (*pData)[0] <= (*pData)[1]) |
||
| 614 | { |
||
| 615 | if ( (*pData)[2] <= (*pData)[3]) |
||
| 616 | { |
||
| 617 | p[0] = *(*pData)++; |
||
| 618 | p[1] = *(*pData)++; |
||
| 619 | p[2] = *(*pData)++; |
||
| 620 | p[3] = *(*pData)++; |
||
| 621 | |||
| 622 | range_for (const int i, xrange(8u)) |
||
| 623 | { |
||
| 624 | (void)i; |
||
| 625 | pat[0] = *(*pData)++; |
||
| 626 | pat[1] = *(*pData)++; |
||
| 627 | patternRow4Pixels(*pFrame, pat[0], pat[1], p); |
||
| 628 | *pFrame += width; |
||
| 629 | } |
||
| 630 | |||
| 631 | *pFrame -= (8 * width - 8); |
||
| 632 | } |
||
| 633 | else |
||
| 634 | { |
||
| 635 | p[0] = *(*pData)++; |
||
| 636 | p[1] = *(*pData)++; |
||
| 637 | p[2] = *(*pData)++; |
||
| 638 | p[3] = *(*pData)++; |
||
| 639 | |||
| 640 | patternRow4Pixels2(*pFrame, *(*pData)++, p); |
||
| 641 | *pFrame += 2 * width; |
||
| 642 | patternRow4Pixels2(*pFrame, *(*pData)++, p); |
||
| 643 | *pFrame += 2 * width; |
||
| 644 | patternRow4Pixels2(*pFrame, *(*pData)++, p); |
||
| 645 | *pFrame += 2 * width; |
||
| 646 | patternRow4Pixels2(*pFrame, *(*pData)++, p); |
||
| 647 | *pFrame -= (6 * width - 8); |
||
| 648 | } |
||
| 649 | } |
||
| 650 | else |
||
| 651 | { |
||
| 652 | if ( (*pData)[2] <= (*pData)[3]) |
||
| 653 | { |
||
| 654 | // draw 2x1 strips |
||
| 655 | p[0] = *(*pData)++; |
||
| 656 | p[1] = *(*pData)++; |
||
| 657 | p[2] = *(*pData)++; |
||
| 658 | p[3] = *(*pData)++; |
||
| 659 | |||
| 660 | range_for (const int i, xrange(8u)) |
||
| 661 | { |
||
| 662 | (void)i; |
||
| 663 | pat[0] = *(*pData)++; |
||
| 664 | patternRow4Pixels2x1(*pFrame, pat[0], p); |
||
| 665 | *pFrame += width; |
||
| 666 | } |
||
| 667 | } |
||
| 668 | else |
||
| 669 | { |
||
| 670 | // draw 1x2 strips |
||
| 671 | p[0] = *(*pData)++; |
||
| 672 | p[1] = *(*pData)++; |
||
| 673 | p[2] = *(*pData)++; |
||
| 674 | p[3] = *(*pData)++; |
||
| 675 | |||
| 676 | range_for (const int i, xrange(4u)) |
||
| 677 | { |
||
| 678 | (void)i; |
||
| 679 | pat[0] = *(*pData)++; |
||
| 680 | pat[1] = *(*pData)++; |
||
| 681 | patternRow4Pixels(*pFrame, pat[0], pat[1], p); |
||
| 682 | *pFrame += width; |
||
| 683 | patternRow4Pixels(*pFrame, pat[0], pat[1], p); |
||
| 684 | *pFrame += width; |
||
| 685 | } |
||
| 686 | } |
||
| 687 | *pFrame -= (8 * width - 8); |
||
| 688 | } |
||
| 689 | } |
||
| 690 | break; |
||
| 691 | |||
| 692 | case 0xa: |
||
| 693 | /* Similar to the previous, only a little more complicated. |
||
| 694 | |||
| 695 | We are still dealing with patterns over 4 pixel values with 2 bits |
||
| 696 | assigned to each pixel (or block of pixels). |
||
| 697 | |||
| 698 | So, first on the data stream are our 4 pixel values: |
||
| 699 | |||
| 700 | P0 P1 P2 P3 |
||
| 701 | |||
| 702 | Now, if P0 <= P1, the block is divided into 4 quadrants, ordered |
||
| 703 | (as with opcode 0x8) TL, BL, TR, BR. In this case the next data |
||
| 704 | in the data stream should be: |
||
| 705 | |||
| 706 | B0 B1 B2 B3 |
||
| 707 | P4 P5 P6 P7 B4 B5 B6 B7 |
||
| 708 | P8 P9 P10 P11 B8 B9 B10 B11 |
||
| 709 | P12 P13 P14 P15 B12 B13 B14 B15 |
||
| 710 | |||
| 711 | Each 2 bits represent a 1x1 pixel (00=P0, 01=P1, 10=P2, 11=P3). |
||
| 712 | The ordering is again left to right and top to bottom. The most |
||
| 713 | significant bits represent the right side at the top, and so on. |
||
| 714 | |||
| 715 | If P0 > P1 then the next data on the data stream is: |
||
| 716 | |||
| 717 | B0 B1 B2 B3 B4 B5 B6 B7 |
||
| 718 | P4 P5 P6 P7 B8 B9 B10 B11 B12 B13 B14 B15 |
||
| 719 | |||
| 720 | Now, in this case, if P4 <= P5, |
||
| 721 | [P0 P1 P2 P3 B0 B1 B2 B3 B4 B5 B6 B7] represent the left half of |
||
| 722 | the block and the other bytes represent the right half. If P4 > |
||
| 723 | P5, then [P0 P1 P2 P3 B0 B1 B2 B3 B4 B5 B6 B7] represent the top |
||
| 724 | half of the block and the other bytes represent the bottom half. |
||
| 725 | */ |
||
| 726 | { |
||
| 727 | const auto width = g_width; |
||
| 728 | if ( (*pData)[0] <= (*pData)[1]) |
||
| 729 | { |
||
| 730 | range_for (const int i, xrange(4u)) |
||
| 731 | { |
||
| 732 | p[0] = *(*pData)++; |
||
| 733 | p[1] = *(*pData)++; |
||
| 734 | p[2] = *(*pData)++; |
||
| 735 | p[3] = *(*pData)++; |
||
| 736 | pat[0] = *(*pData)++; |
||
| 737 | pat[1] = *(*pData)++; |
||
| 738 | pat[2] = *(*pData)++; |
||
| 739 | pat[3] = *(*pData)++; |
||
| 740 | |||
| 741 | patternQuadrant4Pixels(*pFrame, pat[0], pat[1], pat[2], pat[3], p); |
||
| 742 | |||
| 743 | if (i & 1) |
||
| 744 | *pFrame -= (4 * width - 4); |
||
| 745 | else |
||
| 746 | *pFrame += 4 * width; |
||
| 747 | } |
||
| 748 | } |
||
| 749 | else |
||
| 750 | { |
||
| 751 | if ( (*pData)[12] <= (*pData)[13]) |
||
| 752 | { |
||
| 753 | // split vertical |
||
| 754 | range_for (const int i, xrange(4u)) |
||
| 755 | { |
||
| 756 | if ((i&1) == 0) |
||
| 757 | { |
||
| 758 | p[0] = *(*pData)++; |
||
| 759 | p[1] = *(*pData)++; |
||
| 760 | p[2] = *(*pData)++; |
||
| 761 | p[3] = *(*pData)++; |
||
| 762 | } |
||
| 763 | |||
| 764 | pat[0] = *(*pData)++; |
||
| 765 | pat[1] = *(*pData)++; |
||
| 766 | pat[2] = *(*pData)++; |
||
| 767 | pat[3] = *(*pData)++; |
||
| 768 | |||
| 769 | patternQuadrant4Pixels(*pFrame, pat[0], pat[1], pat[2], pat[3], p); |
||
| 770 | |||
| 771 | if (i & 1) |
||
| 772 | *pFrame -= (4 * width - 4); |
||
| 773 | else |
||
| 774 | *pFrame += 4 * width; |
||
| 775 | } |
||
| 776 | } |
||
| 777 | else |
||
| 778 | { |
||
| 779 | // split horizontal |
||
| 780 | range_for (const int i, xrange(8u)) |
||
| 781 | { |
||
| 782 | if ((i&3) == 0) |
||
| 783 | { |
||
| 784 | p[0] = *(*pData)++; |
||
| 785 | p[1] = *(*pData)++; |
||
| 786 | p[2] = *(*pData)++; |
||
| 787 | p[3] = *(*pData)++; |
||
| 788 | } |
||
| 789 | |||
| 790 | pat[0] = *(*pData)++; |
||
| 791 | pat[1] = *(*pData)++; |
||
| 792 | patternRow4Pixels(*pFrame, pat[0], pat[1], p); |
||
| 793 | *pFrame += width; |
||
| 794 | } |
||
| 795 | |||
| 796 | *pFrame -= (8 * width - 8); |
||
| 797 | } |
||
| 798 | } |
||
| 799 | } |
||
| 800 | break; |
||
| 801 | |||
| 802 | case 0xb: |
||
| 803 | /* In this encoding we get raw pixel data in the data stream -- 64 |
||
| 804 | bytes of pixel data. 1 byte for each pixel, and in the standard |
||
| 805 | order (l->r, t->b). |
||
| 806 | */ |
||
| 807 | { |
||
| 808 | const auto width = g_width; |
||
| 809 | range_for (const int i, xrange(8u)) |
||
| 810 | { |
||
| 811 | (void)i; |
||
| 812 | memcpy(*pFrame, *pData, 8); |
||
| 813 | *pFrame += width; |
||
| 814 | *pData += 8; |
||
| 815 | *pDataRemain -= 8; |
||
| 816 | } |
||
| 817 | *pFrame -= (8 * width - 8); |
||
| 818 | } |
||
| 819 | break; |
||
| 820 | |||
| 821 | case 0xc: |
||
| 822 | /* In this encoding we get raw pixel data in the data stream -- 16 |
||
| 823 | bytes of pixel data. 1 byte for each block of 2x2 pixels, and in |
||
| 824 | the standard order (l->r, t->b). |
||
| 825 | */ |
||
| 826 | { |
||
| 827 | const auto width = g_width; |
||
| 828 | range_for (const int i, xrange(4u)) |
||
| 829 | { |
||
| 830 | (void)i; |
||
| 831 | range_for (const int j, xrange(2u)) |
||
| 832 | { |
||
| 833 | (void)j; |
||
| 834 | range_for (const int k, xrange(4u)) |
||
| 835 | { |
||
| 836 | (*pFrame)[2*k] = (*pData)[k]; |
||
| 837 | (*pFrame)[2*k+1] = (*pData)[k]; |
||
| 838 | } |
||
| 839 | *pFrame += width; |
||
| 840 | } |
||
| 841 | *pData += 4; |
||
| 842 | *pDataRemain -= 4; |
||
| 843 | } |
||
| 844 | *pFrame -= (8 * width - 8); |
||
| 845 | } |
||
| 846 | break; |
||
| 847 | |||
| 848 | case 0xd: |
||
| 849 | /* In this encoding we get raw pixel data in the data stream -- 4 |
||
| 850 | bytes of pixel data. 1 byte for each block of 4x4 pixels, and in |
||
| 851 | the standard order (l->r, t->b). |
||
| 852 | */ |
||
| 853 | { |
||
| 854 | const auto width = g_width; |
||
| 855 | range_for (const int i, xrange(2u)) |
||
| 856 | { |
||
| 857 | (void)i; |
||
| 858 | range_for (const int j, xrange(4u)) |
||
| 859 | { |
||
| 860 | range_for (const int k, xrange(4u)) |
||
| 861 | { |
||
| 862 | (*pFrame)[k * width+j] = (*pData)[0]; |
||
| 863 | (*pFrame)[k * width+j+4] = (*pData)[1]; |
||
| 864 | } |
||
| 865 | } |
||
| 866 | *pFrame += 4 * width; |
||
| 867 | *pData += 2; |
||
| 868 | *pDataRemain -= 2; |
||
| 869 | } |
||
| 870 | *pFrame -= (8 * width - 8); |
||
| 871 | } |
||
| 872 | break; |
||
| 873 | |||
| 874 | case 0xe: |
||
| 875 | /* This encoding represents a solid 8x8 frame. We get 1 byte of pixel |
||
| 876 | data from the data stream. |
||
| 877 | */ |
||
| 878 | { |
||
| 879 | const auto width = g_width; |
||
| 880 | range_for (const int i, xrange(8u)) |
||
| 881 | { |
||
| 882 | (void)i; |
||
| 883 | memset(*pFrame, **pData, 8); |
||
| 884 | *pFrame += width; |
||
| 885 | } |
||
| 886 | ++*pData; |
||
| 887 | --*pDataRemain; |
||
| 888 | *pFrame -= (8 * width - 8); |
||
| 889 | } |
||
| 890 | break; |
||
| 891 | |||
| 892 | case 0xf: |
||
| 893 | /* This encoding represents a "dithered" frame, which is |
||
| 894 | checkerboarded with alternate pixels of two colors. We get 2 |
||
| 895 | bytes of pixel data from the data stream, and these bytes are |
||
| 896 | alternated: |
||
| 897 | |||
| 898 | P0 P1 P0 P1 P0 P1 P0 P1 |
||
| 899 | P1 P0 P1 P0 P1 P0 P1 P0 |
||
| 900 | ... |
||
| 901 | P0 P1 P0 P1 P0 P1 P0 P1 |
||
| 902 | P1 P0 P1 P0 P1 P0 P1 P0 |
||
| 903 | */ |
||
| 904 | { |
||
| 905 | const auto width = g_width; |
||
| 906 | range_for (const int i, xrange(8u)) |
||
| 907 | { |
||
| 908 | range_for (const int j, xrange(8u)) |
||
| 909 | { |
||
| 910 | (*pFrame)[j] = (*pData)[(i+j)&1]; |
||
| 911 | } |
||
| 912 | *pFrame += width; |
||
| 913 | } |
||
| 914 | *pData += 2; |
||
| 915 | *pDataRemain -= 2; |
||
| 916 | *pFrame -= (8 * width - 8); |
||
| 917 | } |
||
| 918 | break; |
||
| 919 | |||
| 920 | default: |
||
| 921 | break; |
||
| 922 | } |
||
| 923 | } |