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