diff options
Diffstat (limited to 'apps/plugins/xworld/video.c')
-rw-r--r-- | apps/plugins/xworld/video.c | 1141 |
1 files changed, 1141 insertions, 0 deletions
diff --git a/apps/plugins/xworld/video.c b/apps/plugins/xworld/video.c new file mode 100644 index 0000000000..b4182df7a7 --- /dev/null +++ b/apps/plugins/xworld/video.c | |||
@@ -0,0 +1,1141 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2014 Franklin Wei, Benjamin Brown | ||
11 | * Copyright (C) 2004 Gregory Montoir | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or | ||
14 | * modify it under the terms of the GNU General Public License | ||
15 | * as published by the Free Software Foundation; either version 2 | ||
16 | * of the License, or (at your option) any later version. | ||
17 | * | ||
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
19 | * KIND, either express or implied. | ||
20 | * | ||
21 | ***************************************************************************/ | ||
22 | |||
23 | #include "video.h" | ||
24 | #include "video_data.h" | ||
25 | #include "resource.h" | ||
26 | #include "serializer.h" | ||
27 | #include "sys.h" | ||
28 | #include "file.h" | ||
29 | |||
30 | void polygon_readVertices(struct Polygon* g, const uint8_t *p, uint16_t zoom) { | ||
31 | g->bbw = (*p++) * zoom / 64; | ||
32 | g->bbh = (*p++) * zoom / 64; | ||
33 | g->numPoints = *p++; | ||
34 | assert((g->numPoints & 1) == 0 && g->numPoints < MAX_POINTS); | ||
35 | |||
36 | //Read all points, directly from bytecode segment | ||
37 | for (int i = 0; i < g->numPoints; ++i) { | ||
38 | struct Point *pt = &g->points[i]; | ||
39 | pt->x = (*p++) * zoom / 64; | ||
40 | pt->y = (*p++) * zoom / 64; | ||
41 | } | ||
42 | } | ||
43 | |||
44 | void video_create(struct Video* v, struct Resource* res, struct System* sys) | ||
45 | { | ||
46 | v->res = res; | ||
47 | v->sys = sys; | ||
48 | } | ||
49 | |||
50 | void video_init(struct Video* v) { | ||
51 | v->paletteIdRequested = NO_PALETTE_CHANGE_REQUESTED; | ||
52 | v->page_data = v->res->_memPtrStart + MEM_BLOCK_SIZE; | ||
53 | |||
54 | rb->memset(v->page_data, 0, 4 * VID_PAGE_SIZE); | ||
55 | |||
56 | for (int i = 0; i < 4; ++i) { | ||
57 | v->_pagePtrs[i] = v->page_data + i * VID_PAGE_SIZE; | ||
58 | } | ||
59 | |||
60 | v->_curPagePtr3 = video_getPagePtr(v, 1); | ||
61 | v->_curPagePtr2 = video_getPagePtr(v, 2); | ||
62 | |||
63 | video_changePagePtr1(v, 0xFE); | ||
64 | |||
65 | v->_interpTable[0] = 0x4000; | ||
66 | |||
67 | for (int i = 1; i < 0x400; ++i) { | ||
68 | v->_interpTable[i] = 0x4000 / i; | ||
69 | } | ||
70 | } | ||
71 | |||
72 | void video_setDataBuffer(struct Video* v, uint8_t *dataBuf, uint16_t offset) { | ||
73 | |||
74 | v->_dataBuf = dataBuf; | ||
75 | v->_pData.pc = dataBuf + offset; | ||
76 | } | ||
77 | |||
78 | |||
79 | /* A shape can be given in two different ways: | ||
80 | |||
81 | - A list of screenspace vertices. | ||
82 | - A list of objectspace vertices, based on a delta from the first vertex. | ||
83 | |||
84 | This is a recursive function. */ | ||
85 | void video_readAndDrawPolygon(struct Video* v, uint8_t color, uint16_t zoom, const struct Point *pt) { | ||
86 | |||
87 | uint8_t i = scriptPtr_fetchByte(&v->_pData); | ||
88 | |||
89 | //This is | ||
90 | if (i >= 0xC0) { // 0xc0 = 192 | ||
91 | |||
92 | // WTF ? | ||
93 | if (color & 0x80) { //0x80 = 128 (1000 0000) | ||
94 | color = i & 0x3F; //0x3F = 63 (0011 1111) | ||
95 | } | ||
96 | |||
97 | // pc is misleading here since we are not reading bytecode but only | ||
98 | // vertices informations. | ||
99 | polygon_readVertices(&v->polygon, v->_pData.pc, zoom); | ||
100 | |||
101 | video_fillPolygon(v, color, zoom, pt); | ||
102 | |||
103 | |||
104 | |||
105 | } else { | ||
106 | i &= 0x3F; //0x3F = 63 | ||
107 | if (i == 1) { | ||
108 | warning("video_readAndDrawPolygon() ec=0x%X (i != 2)", 0xF80); | ||
109 | } else if (i == 2) { | ||
110 | video_readAndDrawPolygonHierarchy(v, zoom, pt); | ||
111 | |||
112 | } else { | ||
113 | warning("video_readAndDrawPolygon() ec=0x%X (i != 2)", 0xFBB); | ||
114 | } | ||
115 | } | ||
116 | |||
117 | |||
118 | |||
119 | } | ||
120 | |||
121 | void video_fillPolygon(struct Video* v, uint16_t color, uint16_t zoom, const struct Point *pt) { | ||
122 | |||
123 | (void) zoom; | ||
124 | |||
125 | if (v->polygon.bbw == 0 && v->polygon.bbh == 1 && v->polygon.numPoints == 4) { | ||
126 | video_drawPoint(v, color, pt->x, pt->y); | ||
127 | |||
128 | return; | ||
129 | } | ||
130 | |||
131 | int16_t x1 = pt->x - v->polygon.bbw / 2; | ||
132 | int16_t x2 = pt->x + v->polygon.bbw / 2; | ||
133 | int16_t y1 = pt->y - v->polygon.bbh / 2; | ||
134 | int16_t y2 = pt->y + v->polygon.bbh / 2; | ||
135 | |||
136 | if (x1 > 319 || x2 < 0 || y1 > 199 || y2 < 0) | ||
137 | return; | ||
138 | |||
139 | v->_hliney = y1; | ||
140 | |||
141 | uint16_t i, j; | ||
142 | i = 0; | ||
143 | j = v->polygon.numPoints - 1; | ||
144 | |||
145 | x2 = v->polygon.points[i].x + x1; | ||
146 | x1 = v->polygon.points[j].x + x1; | ||
147 | |||
148 | ++i; | ||
149 | --j; | ||
150 | |||
151 | drawLine drawFct; | ||
152 | if (color < 0x10) { | ||
153 | drawFct = &video_drawLineN; | ||
154 | } else if (color > 0x10) { | ||
155 | drawFct = &video_drawLineP; | ||
156 | } else { | ||
157 | drawFct = &video_drawLineBlend; | ||
158 | } | ||
159 | |||
160 | uint32_t cpt1 = x1 << 16; | ||
161 | uint32_t cpt2 = x2 << 16; | ||
162 | |||
163 | while (1) { | ||
164 | v->polygon.numPoints -= 2; | ||
165 | if (v->polygon.numPoints == 0) { | ||
166 | #if TRACE_FRAMEBUFFER | ||
167 | video_dumpFrameBuffers(v, "fillPolygonEnd"); | ||
168 | #endif | ||
169 | #if TRACE_BG_BUFFER | ||
170 | video_dumpBackGroundBuffer(v); | ||
171 | #endif | ||
172 | break; | ||
173 | } | ||
174 | uint16_t h; | ||
175 | int32_t step1 = video_calcStep(v, &v->polygon.points[j + 1], &v->polygon.points[j], &h); | ||
176 | int32_t step2 = video_calcStep(v, &v->polygon.points[i - 1], &v->polygon.points[i], &h); | ||
177 | |||
178 | ++i; | ||
179 | --j; | ||
180 | |||
181 | cpt1 = (cpt1 & 0xFFFF0000) | 0x7FFF; | ||
182 | cpt2 = (cpt2 & 0xFFFF0000) | 0x8000; | ||
183 | |||
184 | if (h == 0) { | ||
185 | cpt1 += step1; | ||
186 | cpt2 += step2; | ||
187 | } else { | ||
188 | for (; h != 0; --h) { | ||
189 | if (v->_hliney >= 0) { | ||
190 | x1 = cpt1 >> 16; | ||
191 | x2 = cpt2 >> 16; | ||
192 | if (x1 <= 319 && x2 >= 0) { | ||
193 | if (x1 < 0) x1 = 0; | ||
194 | if (x2 > 319) x2 = 319; | ||
195 | (*drawFct)(v, x1, x2, color); | ||
196 | } | ||
197 | } | ||
198 | cpt1 += step1; | ||
199 | cpt2 += step2; | ||
200 | ++v->_hliney; | ||
201 | if (v->_hliney > 199) return; | ||
202 | } | ||
203 | } | ||
204 | |||
205 | #if TRACE_FRAMEBUFFER | ||
206 | video_dumpFrameBuffers(v, "fillPolygonChild"); | ||
207 | #endif | ||
208 | #if TRACE_BG_BUFFER | ||
209 | |||
210 | video_dumpBackGroundBuffer(v); | ||
211 | #endif | ||
212 | } | ||
213 | } | ||
214 | |||
215 | /* | ||
216 | What is read from the bytecode is not a pure screnspace polygon but a polygonspace polygon. | ||
217 | |||
218 | */ | ||
219 | void video_readAndDrawPolygonHierarchy(struct Video* v, uint16_t zoom, const struct Point *pgc) { | ||
220 | |||
221 | struct Point pt = *pgc; | ||
222 | pt.x -= scriptPtr_fetchByte(&v->_pData) * zoom / 64; | ||
223 | pt.y -= scriptPtr_fetchByte(&v->_pData) * zoom / 64; | ||
224 | |||
225 | int16_t childs = scriptPtr_fetchByte(&v->_pData); | ||
226 | debug(DBG_VIDEO, "video_readAndDrawPolygonHierarchy childs=%d", childs); | ||
227 | |||
228 | for ( ; childs >= 0; --childs) { | ||
229 | |||
230 | uint16_t off = scriptPtr_fetchWord(&v->_pData); | ||
231 | |||
232 | struct Point po; | ||
233 | po = pt; | ||
234 | po.x += scriptPtr_fetchByte(&v->_pData) * zoom / 64; | ||
235 | po.y += scriptPtr_fetchByte(&v->_pData) * zoom / 64; | ||
236 | |||
237 | uint16_t color = 0xFF; | ||
238 | uint16_t _bp = off; | ||
239 | off &= 0x7FFF; | ||
240 | |||
241 | if (_bp & 0x8000) { | ||
242 | color = *v->_pData.pc & 0x7F; | ||
243 | v->_pData.pc += 2; | ||
244 | } | ||
245 | |||
246 | uint8_t *bak = v->_pData.pc; | ||
247 | v->_pData.pc = v->_dataBuf + off * 2; | ||
248 | |||
249 | |||
250 | video_readAndDrawPolygon(v, color, zoom, &po); | ||
251 | |||
252 | |||
253 | v->_pData.pc = bak; | ||
254 | } | ||
255 | |||
256 | |||
257 | } | ||
258 | |||
259 | int32_t video_calcStep(struct Video* v, const struct Point *p1, const struct Point *p2, uint16_t *dy) { | ||
260 | (void) v; | ||
261 | *dy = p2->y - p1->y; | ||
262 | return (p2->x - p1->x) * v->_interpTable[*dy] * 4; | ||
263 | } | ||
264 | |||
265 | void video_drawString(struct Video* v, uint8_t color, uint16_t x, uint16_t y, uint16_t stringId) { | ||
266 | |||
267 | const struct StrEntry *se = video_stringsTableEng; | ||
268 | |||
269 | //Search for the location where the string is located. | ||
270 | while (se->id != END_OF_STRING_DICTIONARY && se->id != stringId) | ||
271 | ++se; | ||
272 | |||
273 | debug(DBG_VIDEO, "video_drawString(%d, %d, %d, '%s')", color, x, y, se->str); | ||
274 | |||
275 | //Not found | ||
276 | if (se->id == END_OF_STRING_DICTIONARY) | ||
277 | return; | ||
278 | |||
279 | |||
280 | //Used if the string contains a return carriage. | ||
281 | uint16_t xOrigin = x; | ||
282 | int len = rb->strlen(se->str); | ||
283 | for (int i = 0; i < len; ++i) { | ||
284 | |||
285 | if (se->str[i] == '\n') { | ||
286 | y += 8; | ||
287 | x = xOrigin; | ||
288 | continue; | ||
289 | } | ||
290 | |||
291 | video_drawChar(v, se->str[i], x, y, color, v->_curPagePtr1); | ||
292 | x++; | ||
293 | |||
294 | } | ||
295 | } | ||
296 | |||
297 | void video_drawChar(struct Video* v, uint8_t character, uint16_t x, uint16_t y, uint8_t color, uint8_t *buf) { | ||
298 | (void) v; | ||
299 | if (x <= 39 && y <= 192) { | ||
300 | |||
301 | /* each character is 8x8 */ | ||
302 | const uint8_t *ft = video_font + (character - ' ') * 8; | ||
303 | |||
304 | /* x is multiplied by 4 and not 8 because there are two pixels per byte */ | ||
305 | uint8_t *p = buf + x * 4 + y * 160; | ||
306 | |||
307 | for (int j = 0; j < 8; ++j) { | ||
308 | uint8_t ch = *(ft + j); | ||
309 | for (int i = 0; i < 4; ++i) { | ||
310 | uint8_t b = *(p + i); | ||
311 | uint8_t cmask = 0xFF; | ||
312 | uint8_t colb = 0; | ||
313 | if (ch & 0x80) { | ||
314 | colb |= color << 4; | ||
315 | cmask &= 0x0F; | ||
316 | } | ||
317 | ch <<= 1; | ||
318 | if (ch & 0x80) { | ||
319 | colb |= color; | ||
320 | cmask &= 0xF0; | ||
321 | } | ||
322 | ch <<= 1; | ||
323 | *(p + i) = (b & cmask) | colb; | ||
324 | } | ||
325 | /* skip to the next line (320 pixels = 160 bytes) */ | ||
326 | p += 160; | ||
327 | } | ||
328 | } | ||
329 | } | ||
330 | |||
331 | void video_drawPoint(struct Video* v, uint8_t color, int16_t x, int16_t y) { | ||
332 | debug(DBG_VIDEO, "drawPoint(%d, %d, %d)", color, x, y); | ||
333 | if (x >= 0 && x <= 319 && y >= 0 && y <= 199) { | ||
334 | uint16_t off = y * 160 + x / 2; | ||
335 | |||
336 | uint8_t cmasko, cmaskn; | ||
337 | if (x & 1) { | ||
338 | cmaskn = 0x0F; | ||
339 | cmasko = 0xF0; | ||
340 | } else { | ||
341 | cmaskn = 0xF0; | ||
342 | cmasko = 0x0F; | ||
343 | } | ||
344 | |||
345 | uint8_t colb = (color << 4) | color; | ||
346 | if (color == 0x10) { | ||
347 | cmaskn &= 0x88; | ||
348 | cmasko = ~cmaskn; | ||
349 | colb = 0x88; | ||
350 | } else if (color == 0x11) { | ||
351 | colb = *(v->_pagePtrs[0] + off); | ||
352 | } | ||
353 | uint8_t b = *(v->_curPagePtr1 + off); | ||
354 | *(v->_curPagePtr1 + off) = (b & cmasko) | (colb & cmaskn); | ||
355 | } | ||
356 | } | ||
357 | |||
358 | /* Blend a line in the current framebuffer (v->_curPagePtr1) | ||
359 | */ | ||
360 | void video_drawLineBlend(struct Video* v, int16_t x1, int16_t x2, uint8_t color) { | ||
361 | /* silence warnings without XWORLD_DEBUG */ | ||
362 | (void) color; | ||
363 | debug(DBG_VIDEO, "drawLineBlend(%d, %d, %d)", x1, x2, color); | ||
364 | int16_t xmax = MAX(x1, x2); | ||
365 | int16_t xmin = MIN(x1, x2); | ||
366 | uint8_t *p = v->_curPagePtr1 + v->_hliney * 160 + xmin / 2; | ||
367 | |||
368 | uint16_t w = xmax / 2 - xmin / 2 + 1; | ||
369 | uint8_t cmaske = 0; | ||
370 | uint8_t cmasks = 0; | ||
371 | if (xmin & 1) { | ||
372 | --w; | ||
373 | cmasks = 0xF7; | ||
374 | } | ||
375 | if (!(xmax & 1)) { | ||
376 | --w; | ||
377 | cmaske = 0x7F; | ||
378 | } | ||
379 | |||
380 | if (cmasks != 0) { | ||
381 | *p = (*p & cmasks) | 0x08; | ||
382 | ++p; | ||
383 | } | ||
384 | while (w--) { | ||
385 | *p = (*p & 0x77) | 0x88; | ||
386 | ++p; | ||
387 | } | ||
388 | if (cmaske != 0) { | ||
389 | *p = (*p & cmaske) | 0x80; | ||
390 | ++p; | ||
391 | } | ||
392 | |||
393 | |||
394 | } | ||
395 | |||
396 | void video_drawLineN(struct Video* v, int16_t x1, int16_t x2, uint8_t color) { | ||
397 | debug(DBG_VIDEO, "drawLineN(%d, %d, %d)", x1, x2, color); | ||
398 | int16_t xmax = MAX(x1, x2); | ||
399 | int16_t xmin = MIN(x1, x2); | ||
400 | uint8_t *p = v->_curPagePtr1 + v->_hliney * 160 + xmin / 2; | ||
401 | |||
402 | uint16_t w = xmax / 2 - xmin / 2 + 1; | ||
403 | uint8_t cmaske = 0; | ||
404 | uint8_t cmasks = 0; | ||
405 | if (xmin & 1) { | ||
406 | --w; | ||
407 | cmasks = 0xF0; | ||
408 | } | ||
409 | if (!(xmax & 1)) { | ||
410 | --w; | ||
411 | cmaske = 0x0F; | ||
412 | } | ||
413 | |||
414 | uint8_t colb = ((color & 0xF) << 4) | (color & 0xF); | ||
415 | if (cmasks != 0) { | ||
416 | *p = (*p & cmasks) | (colb & 0x0F); | ||
417 | ++p; | ||
418 | } | ||
419 | while (w--) { | ||
420 | *p++ = colb; | ||
421 | } | ||
422 | if (cmaske != 0) { | ||
423 | *p = (*p & cmaske) | (colb & 0xF0); | ||
424 | ++p; | ||
425 | } | ||
426 | |||
427 | |||
428 | } | ||
429 | |||
430 | void video_drawLineP(struct Video* v, int16_t x1, int16_t x2, uint8_t color) { | ||
431 | /* silence warnings without XWORLD_DEBUG */ | ||
432 | (void) color; | ||
433 | debug(DBG_VIDEO, "drawLineP(%d, %d, %d)", x1, x2, color); | ||
434 | int16_t xmax = MAX(x1, x2); | ||
435 | int16_t xmin = MIN(x1, x2); | ||
436 | uint16_t off = v->_hliney * 160 + xmin / 2; | ||
437 | uint8_t *p = v->_curPagePtr1 + off; | ||
438 | uint8_t *q = v->_pagePtrs[0] + off; | ||
439 | |||
440 | uint8_t w = xmax / 2 - xmin / 2 + 1; | ||
441 | uint8_t cmaske = 0; | ||
442 | uint8_t cmasks = 0; | ||
443 | if (xmin & 1) { | ||
444 | --w; | ||
445 | cmasks = 0xF0; | ||
446 | } | ||
447 | if (!(xmax & 1)) { | ||
448 | --w; | ||
449 | cmaske = 0x0F; | ||
450 | } | ||
451 | |||
452 | if (cmasks != 0) { | ||
453 | *p = (*p & cmasks) | (*q & 0x0F); | ||
454 | ++p; | ||
455 | ++q; | ||
456 | } | ||
457 | while (w--) { | ||
458 | *p++ = *q++; | ||
459 | } | ||
460 | if (cmaske != 0) { | ||
461 | *p = (*p & cmaske) | (*q & 0xF0); | ||
462 | ++p; | ||
463 | ++q; | ||
464 | } | ||
465 | |||
466 | } | ||
467 | |||
468 | uint8_t *video_getPagePtr(struct Video* v, uint8_t page) { | ||
469 | uint8_t *p; | ||
470 | if (page <= 3) { | ||
471 | p = v->_pagePtrs[page]; | ||
472 | } else { | ||
473 | switch (page) { | ||
474 | case 0xFF: | ||
475 | p = v->_curPagePtr3; | ||
476 | break; | ||
477 | case 0xFE: | ||
478 | p = v->_curPagePtr2; | ||
479 | break; | ||
480 | default: | ||
481 | p = v->_pagePtrs[0]; // XXX check | ||
482 | warning("video_getPagePtr() p != [0,1,2,3,0xFF,0xFE] == 0x%X", page); | ||
483 | break; | ||
484 | } | ||
485 | } | ||
486 | return p; | ||
487 | } | ||
488 | |||
489 | |||
490 | |||
491 | void video_changePagePtr1(struct Video* v, uint8_t page) { | ||
492 | debug(DBG_VIDEO, "video_changePagePtr1(%d)", page); | ||
493 | v->_curPagePtr1 = video_getPagePtr(v, page); | ||
494 | } | ||
495 | |||
496 | |||
497 | |||
498 | void video_fillPage(struct Video* v, uint8_t pageId, uint8_t color) { | ||
499 | debug(DBG_VIDEO, "video_fillPage(%d, %d)", pageId, color); | ||
500 | uint8_t *p = video_getPagePtr(v, pageId); | ||
501 | |||
502 | // Since a palette indice is coded on 4 bits, we need to duplicate the | ||
503 | // clearing color to the upper part of the byte. | ||
504 | uint8_t c = (color << 4) | color; | ||
505 | |||
506 | rb->memset(p, c, VID_PAGE_SIZE); | ||
507 | |||
508 | #if TRACE_FRAMEBUFFER | ||
509 | video_dumpFrameBuffers(v, "-fillPage"); | ||
510 | #endif | ||
511 | #if TRACE_BG_BUFFER | ||
512 | |||
513 | video_dumpBackGroundBuffer(v); | ||
514 | #endif | ||
515 | } | ||
516 | |||
517 | |||
518 | |||
519 | |||
520 | |||
521 | #if TRACE_FRAMEBUFFER | ||
522 | #define SCREENSHOT_BPP 3 | ||
523 | int traceFrameBufferCounter = 0; | ||
524 | uint8_t allFrameBuffers[640 * 400 * SCREENSHOT_BPP]; | ||
525 | |||
526 | #endif | ||
527 | |||
528 | |||
529 | |||
530 | |||
531 | |||
532 | |||
533 | /* This opcode is used once the background of a scene has been drawn in one of the framebuffer: | ||
534 | it is copied in the current framebuffer at the start of a new frame in order to improve performances. */ | ||
535 | void video_copyPage(struct Video* v, uint8_t srcPageId, uint8_t dstPageId, int16_t vscroll) { | ||
536 | |||
537 | debug(DBG_VIDEO, "video_copyPage(%d, %d)", srcPageId, dstPageId); | ||
538 | |||
539 | if (srcPageId == dstPageId) | ||
540 | return; | ||
541 | |||
542 | uint8_t *p; | ||
543 | uint8_t *q; | ||
544 | |||
545 | if (srcPageId >= 0xFE || !((srcPageId &= 0xBF) & 0x80)) { | ||
546 | p = video_getPagePtr(v, srcPageId); | ||
547 | q = video_getPagePtr(v, dstPageId); | ||
548 | memcpy(q, p, VID_PAGE_SIZE); | ||
549 | |||
550 | } else { | ||
551 | p = video_getPagePtr(v, srcPageId & 3); | ||
552 | q = video_getPagePtr(v, dstPageId); | ||
553 | if (vscroll >= -199 && vscroll <= 199) { | ||
554 | uint16_t h = 200; | ||
555 | if (vscroll < 0) { | ||
556 | h += vscroll; | ||
557 | p += -vscroll * 160; | ||
558 | } else { | ||
559 | h -= vscroll; | ||
560 | q += vscroll * 160; | ||
561 | } | ||
562 | memcpy(q, p, h * 160); | ||
563 | } | ||
564 | } | ||
565 | |||
566 | |||
567 | #if TRACE_FRAMEBUFFER | ||
568 | char name[256]; | ||
569 | rb->memset(name, 0, sizeof(name)); | ||
570 | sprintf(name, "copyPage_0x%X_to_0x%X", (p - v->_pagePtrs[0]) / VID_PAGE_SIZE, (q - v->_pagePtrs[0]) / VID_PAGE_SIZE); | ||
571 | dumpFrameBuffers(name); | ||
572 | #endif | ||
573 | } | ||
574 | |||
575 | |||
576 | |||
577 | |||
578 | void video_copyPagePtr(struct Video* v, const uint8_t *src) { | ||
579 | debug(DBG_VIDEO, "video_copyPagePtr()"); | ||
580 | uint8_t *dst = v->_pagePtrs[0]; | ||
581 | int h = 200; | ||
582 | while (h--) { | ||
583 | int w = 40; | ||
584 | while (w--) { | ||
585 | uint8_t p[] = { | ||
586 | *(src + 8000 * 3), | ||
587 | *(src + 8000 * 2), | ||
588 | *(src + 8000 * 1), | ||
589 | *(src + 8000 * 0) | ||
590 | }; | ||
591 | for(int j = 0; j < 4; ++j) { | ||
592 | uint8_t acc = 0; | ||
593 | for (int i = 0; i < 8; ++i) { | ||
594 | acc <<= 1; | ||
595 | acc |= (p[i & 3] & 0x80) ? 1 : 0; | ||
596 | p[i & 3] <<= 1; | ||
597 | } | ||
598 | *dst++ = acc; | ||
599 | } | ||
600 | ++src; | ||
601 | } | ||
602 | } | ||
603 | |||
604 | |||
605 | } | ||
606 | |||
607 | /* | ||
608 | uint8_t *video_allocPage() { | ||
609 | uint8_t *buf = (uint8_t *)malloc(VID_PAGE_SIZE); | ||
610 | rb->memset(buf, 0, VID_PAGE_SIZE); | ||
611 | return buf; | ||
612 | } | ||
613 | */ | ||
614 | |||
615 | |||
616 | |||
617 | #if TRACE_FRAMEBUFFER | ||
618 | int dumpPaletteCursor = 0; | ||
619 | #endif | ||
620 | |||
621 | /* | ||
622 | Note: The palettes set used to be allocated on the stack but I moved it to | ||
623 | the heap so I could dump the four framebuffer and follow how | ||
624 | frames are generated. | ||
625 | */ | ||
626 | uint8_t pal[NUM_COLORS * 3]; //3 = BYTES_PER_PIXEL | ||
627 | void video_changePal(struct Video* v, uint8_t palNum) { | ||
628 | debug(DBG_VIDEO, "video_changePal(v=0x%08x, palNum=%d", v, palNum); | ||
629 | if (palNum >= 32) | ||
630 | return; | ||
631 | |||
632 | uint8_t *p = v->res->segPalettes + palNum * 32; //colors are coded on 2bytes (565) for 16 colors = 32 | ||
633 | debug(DBG_VIDEO, "segPalettes: 0x%08x", v->res->segPalettes); | ||
634 | // Moved to the heap, legacy code used to allocate the palette | ||
635 | // on the stack. | ||
636 | //uint8_t pal[NUM_COLORS * 3]; //3 = BYTES_PER_PIXEL | ||
637 | |||
638 | for (int i = 0; i < NUM_COLORS; ++i) | ||
639 | { | ||
640 | debug(DBG_VIDEO, "i: %d", i); | ||
641 | debug(DBG_VIDEO, "p: 0x%08x", p); | ||
642 | uint8_t c1 = *(p + 0); | ||
643 | uint8_t c2 = *(p + 1); | ||
644 | p += 2; | ||
645 | pal[i * 3 + 0] = ((c1 & 0x0F) << 2) | ((c1 & 0x0F) >> 2); // r | ||
646 | pal[i * 3 + 1] = ((c2 & 0xF0) >> 2) | ((c2 & 0xF0) >> 6); // g | ||
647 | pal[i * 3 + 2] = ((c2 & 0x0F) >> 2) | ((c2 & 0x0F) << 2); // b | ||
648 | } | ||
649 | |||
650 | sys_setPalette(v->sys, 0, NUM_COLORS, pal); | ||
651 | v->currentPaletteId = palNum; | ||
652 | |||
653 | |||
654 | #if TRACE_PALETTE | ||
655 | printf("\nuint8_t dumpPalette[48] = {\n"); | ||
656 | for (int i = 0; i < NUM_COLORS; ++i) | ||
657 | { | ||
658 | printf("0x%X,0x%X,0x%X,", pal[i * 3 + 0], pal[i * 3 + 1], pal[i * 3 + 2]); | ||
659 | } | ||
660 | printf("\n};\n"); | ||
661 | #endif | ||
662 | |||
663 | |||
664 | #if TRACE_FRAMEBUFFER | ||
665 | dumpPaletteCursor++; | ||
666 | #endif | ||
667 | } | ||
668 | |||
669 | void video_updateDisplay(struct Video* v, uint8_t pageId) { | ||
670 | |||
671 | debug(DBG_VIDEO, "video_updateDisplay(%d)", pageId); | ||
672 | |||
673 | if (pageId != 0xFE) { | ||
674 | if (pageId == 0xFF) { | ||
675 | /* swap ptrs 2 and 3 */ | ||
676 | uint8_t* temp = v->_curPagePtr3; | ||
677 | v->_curPagePtr3 = v->_curPagePtr2; | ||
678 | v->_curPagePtr2 = temp; | ||
679 | } else { | ||
680 | v->_curPagePtr2 = video_getPagePtr(v, pageId); | ||
681 | } | ||
682 | } | ||
683 | |||
684 | //Check if we need to change the palette | ||
685 | if (v->paletteIdRequested != NO_PALETTE_CHANGE_REQUESTED) { | ||
686 | video_changePal(v, v->paletteIdRequested); | ||
687 | v->paletteIdRequested = NO_PALETTE_CHANGE_REQUESTED; | ||
688 | } | ||
689 | |||
690 | //Q: Why 160 ? | ||
691 | //A: Because one byte gives two palette indices so | ||
692 | // we only need to move 320/2 per line. | ||
693 | sys_copyRect(v->sys, 0, 0, 320, 200, v->_curPagePtr2, 160); | ||
694 | |||
695 | #if TRACE_FRAMEBUFFER | ||
696 | dumpFrameBuffer(v->_curPagePtr2, allFrameBuffers, 320, 200); | ||
697 | #endif | ||
698 | } | ||
699 | |||
700 | void video_saveOrLoad(struct Video* v, struct Serializer *ser) { | ||
701 | uint8_t mask = 0; | ||
702 | if (ser->_mode == SM_SAVE) { | ||
703 | for (int i = 0; i < 4; ++i) { | ||
704 | if (v->_pagePtrs[i] == v->_curPagePtr1) | ||
705 | mask |= i << 4; | ||
706 | if (v->_pagePtrs[i] == v->_curPagePtr2) | ||
707 | mask |= i << 2; | ||
708 | if (v->_pagePtrs[i] == v->_curPagePtr3) | ||
709 | mask |= i << 0; | ||
710 | } | ||
711 | } | ||
712 | struct Entry entries[] = { | ||
713 | SE_INT(&v->currentPaletteId, SES_INT8, VER(1)), | ||
714 | SE_INT(&v->paletteIdRequested, SES_INT8, VER(1)), | ||
715 | SE_INT(&mask, SES_INT8, VER(1)), | ||
716 | SE_ARRAY(v->_pagePtrs[0], VID_PAGE_SIZE, SES_INT8, VER(1)), | ||
717 | SE_ARRAY(v->_pagePtrs[1], VID_PAGE_SIZE, SES_INT8, VER(1)), | ||
718 | SE_ARRAY(v->_pagePtrs[2], VID_PAGE_SIZE, SES_INT8, VER(1)), | ||
719 | SE_ARRAY(v->_pagePtrs[3], VID_PAGE_SIZE, SES_INT8, VER(1)), | ||
720 | SE_END() | ||
721 | }; | ||
722 | ser_saveOrLoadEntries(ser, entries); | ||
723 | |||
724 | if (ser->_mode == SM_LOAD) { | ||
725 | v->_curPagePtr1 = v->_pagePtrs[(mask >> 4) & 0x3]; | ||
726 | v->_curPagePtr2 = v->_pagePtrs[(mask >> 2) & 0x3]; | ||
727 | v->_curPagePtr3 = v->_pagePtrs[(mask >> 0) & 0x3]; | ||
728 | video_changePal(v, v->currentPaletteId); | ||
729 | } | ||
730 | } | ||
731 | |||
732 | |||
733 | |||
734 | #if TRACE_FRAMEBUFFER | ||
735 | |||
736 | |||
737 | uint8_t allPalettesDump[][48] = { | ||
738 | |||
739 | |||
740 | { | ||
741 | 0x4, 0x4, 0x4, 0x22, 0x0, 0x0, 0x4, 0x8, 0x11, 0x4, 0xC, 0x15, 0x8, 0x11, 0x19, 0xC, 0x15, 0x1D, 0x15, 0x1D, 0x26, 0x1D, 0x2A, 0x2E, 0x2E, 0x22, 0x0, 0x3F, 0x0, 0x0, 0x33, 0x26, 0x0, 0x37, 0x2A, 0x0, 0x3B, 0x33, 0x0, 0x3F, 0x3B, 0x0, 0x3F, 0x3F, 0x1D, 0x3F, 0x3F, 0x2A, | ||
742 | }, | ||
743 | |||
744 | { | ||
745 | 0x4, 0x4, 0x4, 0xC, 0xC, 0x11, 0x4, 0x8, 0x11, 0x4, 0xC, 0x15, 0x8, 0x11, 0x19, 0xC, 0x15, 0x1D, 0x15, 0x1D, 0x26, 0x1D, 0x2A, 0x2E, 0x2E, 0x22, 0x0, 0x15, 0x11, 0x11, 0x33, 0x26, 0x0, 0x37, 0x2A, 0x0, 0x3B, 0x33, 0x0, 0x3F, 0x3B, 0x0, 0x3F, 0x3F, 0x1D, 0x3F, 0x3F, 0x2A, | ||
746 | } | ||
747 | |||
748 | , { | ||
749 | 0x0, 0x0, 0x0, 0x22, 0x0, 0x0, 0x4, 0x8, 0x11, 0x4, 0xC, 0x15, 0x8, 0x11, 0x19, 0xC, 0x15, 0x1D, 0x15, 0x1D, 0x26, 0x1D, 0x2A, 0x2E, 0x1D, 0x1D, 0x1D, 0x15, 0x15, 0x15, 0xC, 0x8, 0xC, 0x11, 0x11, 0x15, 0x1D, 0x15, 0x15, 0x15, 0x0, 0x0, 0x0, 0x4, 0xC, 0x3F, 0x3F, 0x2A, | ||
750 | } | ||
751 | |||
752 | , { | ||
753 | 0x0, 0x0, 0x0, 0x22, 0x0, 0x0, 0x4, 0x8, 0x11, 0x4, 0xC, 0x15, 0x8, 0x11, 0x19, 0xC, 0x15, 0x1D, 0x15, 0x1D, 0x26, 0x1D, 0x2A, 0x2E, 0x1D, 0x1D, 0x1D, 0x15, 0x15, 0x15, 0xC, 0x8, 0xC, 0x11, 0x11, 0x15, 0x1D, 0x15, 0x15, 0x15, 0x0, 0x0, 0x0, 0x4, 0xC, 0x3F, 0x3F, 0x2A, | ||
754 | } | ||
755 | |||
756 | , { | ||
757 | 0x0, 0x0, 0x0, 0x1D, 0x0, 0x0, 0x4, 0x8, 0x11, 0x4, 0xC, 0x15, 0x8, 0x11, 0x19, 0xC, 0x15, 0x1D, 0x15, 0x1D, 0x26, 0x1D, 0x2A, 0x2E, 0x1D, 0x1D, 0x1D, 0x15, 0x15, 0x15, 0xC, 0x8, 0xC, 0x15, 0x11, 0x19, 0x1D, 0x15, 0x15, 0x15, 0x0, 0x0, 0x0, 0x4, 0xC, 0x3F, 0x3F, 0x2A, | ||
758 | } | ||
759 | |||
760 | , { | ||
761 | 0x0, 0x4, 0x8, 0x15, 0x1D, 0x1D, 0x0, 0x8, 0x11, 0x4, 0xC, 0x15, 0x8, 0x11, 0x19, 0xC, 0x15, 0x1D, 0xC, 0x19, 0x22, 0x11, 0x1D, 0x26, 0x8, 0x8, 0x8, 0x0, 0x0, 0x0, 0x2E, 0x2E, 0x2E, 0xC, 0xC, 0xC, 0x15, 0xC, 0x15, 0xC, 0x15, 0x15, 0x11, 0x19, 0x19, 0x1D, 0x26, 0x26, | ||
762 | } | ||
763 | |||
764 | , { | ||
765 | 0x0, 0x0, 0x0, 0x0, 0x4, 0xC, 0x4, 0x8, 0x11, 0x4, 0xC, 0x15, 0x8, 0x11, 0x19, 0xC, 0x15, 0x1D, 0x15, 0x1D, 0x26, 0x1D, 0x2A, 0x2E, 0x15, 0x0, 0x0, 0xC, 0xC, 0xC, 0xC, 0xC, 0xC, 0xC, 0x11, 0x11, 0x11, 0x11, 0x15, 0x26, 0x0, 0x0, 0x33, 0x26, 0x0, 0x3B, 0x33, 0x11, | ||
766 | } | ||
767 | |||
768 | , { | ||
769 | 0x0, 0x0, 0x0, 0x0, 0x4, 0xC, 0x0, 0x8, 0x11, 0x4, 0xC, 0x15, 0x8, 0x11, 0x19, 0xC, 0x15, 0x1D, 0x15, 0x1D, 0x26, 0x1D, 0x2A, 0x2E, 0x15, 0x0, 0x0, 0xC, 0xC, 0xC, 0xC, 0xC, 0xC, 0xC, 0x11, 0x11, 0x11, 0x11, 0x15, 0x26, 0x0, 0x0, 0x26, 0x15, 0x0, 0x26, 0x1D, 0x0, | ||
770 | } | ||
771 | |||
772 | , { | ||
773 | 0x0, 0x0, 0x0, 0x0, 0x4, 0xC, 0x0, 0x8, 0x11, 0x4, 0xC, 0x15, 0x8, 0x11, 0x19, 0xC, 0x15, 0x1D, 0x15, 0x1D, 0x26, 0x1D, 0x2A, 0x2E, 0x15, 0x0, 0x0, 0xC, 0xC, 0xC, 0xC, 0xC, 0xC, 0xC, 0x11, 0x11, 0x11, 0x11, 0x15, 0x26, 0x0, 0x0, 0x3F, 0x0, 0x0, 0x26, 0x1D, 0x0, | ||
774 | } | ||
775 | |||
776 | , { | ||
777 | 0x0, 0x0, 0x0, 0x8, 0x4, 0xC, 0x15, 0xC, 0x11, 0x1D, 0x11, 0x0, 0xC, 0x8, 0x11, 0x2E, 0x1D, 0x0, 0x37, 0x26, 0x8, 0x3F, 0x2E, 0x0, 0x0, 0x0, 0x0, 0x11, 0xC, 0x15, 0x26, 0x15, 0x0, 0x15, 0x11, 0x19, 0x1D, 0x15, 0x1D, 0x26, 0x19, 0x19, 0x0, 0x0, 0x0, 0x3F, 0x3F, 0x3F, | ||
778 | } | ||
779 | |||
780 | , { | ||
781 | 0x0, 0x0, 0x0, 0x8, 0x4, 0xC, 0x37, 0x1D, 0x1D, 0x3B, 0x2A, 0x22, 0x11, 0xC, 0x15, 0x2A, 0x0, 0x0, 0x33, 0x11, 0x0, 0x3F, 0x33, 0x1D, 0x3B, 0x19, 0x0, 0x11, 0x11, 0x19, 0x19, 0x15, 0x1D, 0x22, 0x19, 0x22, 0x2A, 0x1D, 0x26, 0x33, 0x22, 0x26, 0x37, 0x26, 0x22, 0x1D, 0x37, 0x3F, | ||
782 | } | ||
783 | |||
784 | , { | ||
785 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x1D, 0x4, 0x8, 0xC, 0x2A, 0x1D, 0xC, 0x3F, 0x3B, 0x26, 0x3B, 0x2A, 0x11, 0x2A, 0x0, 0x0, 0x0, 0x11, 0x15, 0x2A, 0x1D, 0x26, 0xC, 0x8, 0xC, 0x8, 0x15, 0x1D, 0x37, 0x26, 0x22, 0x33, 0x11, 0x0, 0x2E, 0x1D, 0x19, 0x22, 0x0, 0x0, 0x3F, 0x33, 0x1D, | ||
786 | } | ||
787 | |||
788 | , { | ||
789 | 0x0, 0x0, 0x0, 0x0, 0x15, 0x0, 0x4, 0x8, 0xC, 0x0, 0xC, 0x11, 0x8, 0x11, 0x19, 0x0, 0x19, 0x22, 0x2A, 0x0, 0x0, 0x19, 0x15, 0x22, 0x2E, 0x26, 0x2E, 0xC, 0x8, 0xC, 0x0, 0x2E, 0x0, 0x37, 0x26, 0x22, 0x33, 0x11, 0x0, 0x2E, 0x1D, 0x19, 0x1D, 0x0, 0x0, 0x3F, 0x3F, 0x19, | ||
790 | } | ||
791 | |||
792 | , { | ||
793 | 0x0, 0x0, 0x0, 0x8, 0xC, 0x11, 0x11, 0x11, 0x15, 0x19, 0x15, 0x22, 0x26, 0x19, 0x2A, 0x2E, 0x26, 0x2E, 0x4, 0x4, 0xC, 0x0, 0xC, 0x15, 0x2A, 0x1D, 0x26, 0x0, 0x19, 0x0, 0x0, 0x2A, 0x0, 0x37, 0x26, 0x22, 0x0, 0x15, 0x1D, 0x37, 0x2E, 0x1D, 0x3F, 0x3F, 0x2E, 0x37, 0x37, 0x26, | ||
794 | } | ||
795 | |||
796 | , { | ||
797 | 0x0, 0x0, 0x0, 0x0, 0x15, 0x0, 0x4, 0x8, 0xC, 0x0, 0xC, 0x11, 0x8, 0x11, 0x19, 0x0, 0x19, 0x22, 0x2A, 0x0, 0x0, 0x19, 0x15, 0x22, 0x2E, 0x26, 0x2E, 0xC, 0x8, 0xC, 0x0, 0x2E, 0x0, 0x37, 0x26, 0x22, 0x33, 0x11, 0x0, 0x2E, 0x1D, 0x19, 0x1D, 0x0, 0x0, 0x3F, 0x3F, 0x19, | ||
798 | } | ||
799 | |||
800 | , { | ||
801 | 0x0, 0x0, 0x0, 0x0, 0xC, 0x0, 0x8, 0x0, 0x4, 0xC, 0x8, 0xC, 0x19, 0x11, 0x11, 0x3F, 0x3F, 0x19, 0x0, 0x1D, 0x0, 0x0, 0x3F, 0x0, 0x0, 0x4, 0x8, 0x11, 0x19, 0x11, 0x19, 0x0, 0x0, 0x2E, 0x0, 0x0, 0x3F, 0x0, 0x0, 0x37, 0x1D, 0x15, 0x0, 0x11, 0x11, 0x0, 0x3F, 0x2A, | ||
802 | } | ||
803 | |||
804 | , { | ||
805 | 0x0, 0x0, 0x0, 0x0, 0xC, 0x0, 0x0, 0x11, 0x0, 0x0, 0x19, 0x0, 0xC, 0x26, 0x0, 0x15, 0x2E, 0x0, 0x4, 0x8, 0x0, 0x26, 0x11, 0x0, 0x2E, 0x3F, 0x0, 0x0, 0x15, 0x0, 0xC, 0x1D, 0x0, 0x15, 0x2E, 0x0, 0x15, 0x37, 0x0, 0x1D, 0x3F, 0x0, 0xC, 0x1D, 0xC, 0x\ | ||
806 | 1D, 0x26, 0x15, | ||
807 | } | ||
808 | }; | ||
809 | |||
810 | |||
811 | |||
812 | #include "png.h" | ||
813 | int GL_FCS_SaveAsSpecifiedPNG(char* path, uint8_t* pixels, int depth = 8, int format = PNG_COLOR_TYPE_RGB) | ||
814 | { | ||
815 | png_structp png_ptr = NULL; | ||
816 | png_infop info_ptr = NULL; | ||
817 | png_byte ** row_pointers = NULL; | ||
818 | int status = -1; | ||
819 | int bytePerPixel = 0; | ||
820 | int y; | ||
821 | |||
822 | int fd = open (path, "wb"); | ||
823 | if (fd < 0) { | ||
824 | goto fopen_failed; | ||
825 | } | ||
826 | |||
827 | png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); | ||
828 | if (png_ptr == NULL) { | ||
829 | goto png_create_write_struct_failed; | ||
830 | } | ||
831 | |||
832 | info_ptr = png_create_info_struct (png_ptr); | ||
833 | if (info_ptr == NULL) { | ||
834 | goto png_create_info_struct_failed; | ||
835 | } | ||
836 | |||
837 | if (setjmp (png_jmpbuf (png_ptr))) { | ||
838 | goto png_failure; | ||
839 | } | ||
840 | |||
841 | /* Set image attributes. */ | ||
842 | |||
843 | png_set_IHDR (png_ptr, | ||
844 | info_ptr, | ||
845 | 640, | ||
846 | 400, | ||
847 | depth, | ||
848 | format, | ||
849 | PNG_INTERLACE_NONE, | ||
850 | PNG_COMPRESSION_TYPE_DEFAULT, | ||
851 | PNG_FILTER_TYPE_DEFAULT); | ||
852 | |||
853 | if (format == PNG_COLOR_TYPE_GRAY ) | ||
854 | bytePerPixel = depth / 8 * 1; | ||
855 | else | ||
856 | bytePerPixel = depth / 8 * 3; | ||
857 | |||
858 | row_pointers = (png_byte **)png_malloc (png_ptr, 400 * sizeof (png_byte *)); | ||
859 | //for (y = vid.height-1; y >=0; --y) | ||
860 | for (y = 0; y < 400; y++) | ||
861 | { | ||
862 | row_pointers[y] = (png_byte*)&pixels[640 * (400 - y) * bytePerPixel]; | ||
863 | } | ||
864 | |||
865 | png_init_io (png_ptr, fp); | ||
866 | png_set_rows (png_ptr, info_ptr, row_pointers); | ||
867 | png_write_png (png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL); | ||
868 | //png_read_image (png_ptr, info_ptr);// | ||
869 | |||
870 | status = 0; | ||
871 | |||
872 | png_free (png_ptr, row_pointers); | ||
873 | |||
874 | |||
875 | png_failure: | ||
876 | png_create_info_struct_failed: | ||
877 | png_destroy_write_struct (&png_ptr, &info_ptr); | ||
878 | |||
879 | png_create_write_struct_failed: | ||
880 | fclose (fp); | ||
881 | fopen_failed: | ||
882 | return status; | ||
883 | } | ||
884 | |||
885 | void writeLine(uint8_t *dst, uint8_t *src, int size) | ||
886 | { | ||
887 | uint8_t* dumpPalette; | ||
888 | |||
889 | if (!dumpPaletteCursor) | ||
890 | dumpPalette = allPalettesDump[dumpPaletteCursor]; | ||
891 | else | ||
892 | dumpPalette = pal; | ||
893 | |||
894 | for( uint8_t twoPixels = 0 ; twoPixels < size ; twoPixels++) | ||
895 | { | ||
896 | int pixelIndex0 = (*src & 0xF0) >> 4; | ||
897 | pixelIndex0 &= 0x10 - 1; | ||
898 | |||
899 | int pixelIndex1 = (*src & 0xF); | ||
900 | pixelIndex1 &= 0x10 - 1; | ||
901 | |||
902 | //We need to write those two pixels | ||
903 | dst[0] = dumpPalette[pixelIndex0 * 3] << 2 | dumpPalette[pixelIndex0 * 3]; | ||
904 | dst[1] = dumpPalette[pixelIndex0 * 3 + 1] << 2 | dumpPalette[pixelIndex0 * 3 + 1]; | ||
905 | dst[2] = dumpPalette[pixelIndex0 * 3 + 2] << 2 | dumpPalette[pixelIndex0 * 3 + 2]; | ||
906 | //dst[3] = 0xFF; | ||
907 | dst += SCREENSHOT_BPP; | ||
908 | |||
909 | dst[0] = dumpPalette[pixelIndex1 * 3] << 2 | dumpPalette[pixelIndex1 * 3]; | ||
910 | dst[1] = dumpPalette[pixelIndex1 * 3 + 1] << 2 | dumpPalette[pixelIndex1 * 3 + 1]; | ||
911 | dst[2] = dumpPalette[pixelIndex1 * 3 + 2] << 2 | dumpPalette[pixelIndex1 * 3 + 2]; | ||
912 | //dst[3] = 0xFF; | ||
913 | dst += SCREENSHOT_BPP; | ||
914 | |||
915 | src++; | ||
916 | } | ||
917 | } | ||
918 | |||
919 | void video_dumpFrameBuffer(uint8_t *src, uint8_t *dst, int x, int y) | ||
920 | { | ||
921 | |||
922 | for (int line = 199 ; line >= 0 ; line--) | ||
923 | { | ||
924 | writeLine(dst + x * SCREENSHOT_BPP + y * 640 * SCREENSHOT_BPP , src + line * 160, 160); | ||
925 | dst += 640 * SCREENSHOT_BPP; | ||
926 | } | ||
927 | } | ||
928 | |||
929 | void video_dumpFrameBuffers(char* comment) | ||
930 | { | ||
931 | |||
932 | if (!traceFrameBufferCounter) | ||
933 | { | ||
934 | rb->memset(allFrameBuffers, 0, sizeof(allFrameBuffers)); | ||
935 | } | ||
936 | |||
937 | |||
938 | dumpFrameBuffer(v->_pagePtrs[1], allFrameBuffers, 0, 0); | ||
939 | dumpFrameBuffer(v->_pagePtrs[0], allFrameBuffers, 0, 200); | ||
940 | dumpFrameBuffer(v->_pagePtrs[2], allFrameBuffers, 320, 0); | ||
941 | //dumpFrameBuffer(v->_pagePtrs[3],allFrameBuffers,320,200); | ||
942 | |||
943 | |||
944 | //if (v->_curPagePtr1 == v->_pagePtrs[3]) | ||
945 | // | ||
946 | |||
947 | /* | ||
948 | uint8_t* offScreen = sys->getOffScreenFramebuffer(); | ||
949 | for(int i=0 ; i < 200 ; i++) | ||
950 | writeLine(allFrameBuffers+320*3+640*i*3 + 200*640*3, offScreen+320*i/2 , 160); | ||
951 | */ | ||
952 | |||
953 | |||
954 | int frameId = traceFrameBufferCounter++; | ||
955 | //Write bitmap to disk. | ||
956 | |||
957 | |||
958 | |||
959 | // Filling TGA header information | ||
960 | /* | ||
961 | char path[256]; | ||
962 | sprintf(path,"test%d.tga",traceFrameBufferCounter); | ||
963 | |||
964 | #define IMAGE_WIDTH 640 | ||
965 | #define IMAGE_HEIGHT 400 | ||
966 | |||
967 | uint8_t tga_header[18]; | ||
968 | rb->memset(tga_header, 0, 18); | ||
969 | tga_header[2] = 2; | ||
970 | tga_header[12] = (IMAGE_WIDTH & 0x00FF); | ||
971 | tga_header[13] = (IMAGE_WIDTH & 0xFF00) / 256; | ||
972 | tga_header[14] = (IMAGE_HEIGHT & 0x00FF) ; | ||
973 | tga_header[15] =(IMAGE_HEIGHT & 0xFF00) / 256; | ||
974 | tga_header[16] = 32 ; | ||
975 | |||
976 | |||
977 | |||
978 | // Open the file, write both header and payload, close, done. | ||
979 | char path[256]; | ||
980 | sprintf(path,"test%d.tga",traceFrameBufferCounter); | ||
981 | FILE* pScreenshot = fopen(path, "wb"); | ||
982 | fwrite(&tga_header, 18, sizeof(uint8_t), pScreenshot); | ||
983 | fwrite(allFrameBuffers, IMAGE_WIDTH * IMAGE_HEIGHT,SCREENSHOT_BPP * sizeof(uint8_t),pScreenshot); | ||
984 | fclose(pScreenshot); | ||
985 | */ | ||
986 | |||
987 | |||
988 | char path[256]; | ||
989 | //sprintf(path,"%4d%s.png",traceFrameBufferCounter,comment); | ||
990 | sprintf(path, "%4d.png", traceFrameBufferCounter); | ||
991 | |||
992 | GL_FCS_SaveAsSpecifiedPNG(path, allFrameBuffers); | ||
993 | } | ||
994 | #endif | ||
995 | |||
996 | #if TRACE_BG_BUFFER | ||
997 | |||
998 | |||
999 | |||
1000 | uint8_t bgPalette[48] = { | ||
1001 | 0x8, 0x8, 0xC, 0xC, 0xC, 0x15, 0xC, 0x11, 0x1D, 0x15, 0x2A, 0x3F, 0x1D, 0x19, 0x19, 0x37, 0x2E, 0x2A, 0x26, 0x1D, 0x1D, 0x37, 0x26, 0x22, 0x22, 0xC, 0x0, 0x26, 0x33, 0x3F, 0x11, 0x11, 0x15, 0x11, 0x15, 0x1D, 0x15, 0x19, 0x26, 0x15, 0x1D, 0x37, 0x0, 0x26, 0x3F, 0x2E, 0x15, 0x0, | ||
1002 | }; | ||
1003 | void bgWriteLine(uint8_t *dst, uint8_t *src, int size) | ||
1004 | { | ||
1005 | uint8_t* dumpPalette; | ||
1006 | |||
1007 | // if (!dumpPaletteCursor) | ||
1008 | // dumpPalette = allPalettesDump[dumpPaletteCursor]; | ||
1009 | // else | ||
1010 | dumpPalette = bgPalette; | ||
1011 | |||
1012 | for( uint8_t twoPixels = 0 ; twoPixels < size ; twoPixels++) | ||
1013 | { | ||
1014 | int pixelIndex0 = (*src & 0xF0) >> 4; | ||
1015 | pixelIndex0 &= 0x10 - 1; | ||
1016 | |||
1017 | int pixelIndex1 = (*src & 0xF); | ||
1018 | pixelIndex1 &= 0x10 - 1; | ||
1019 | |||
1020 | //We need to write those two pixels | ||
1021 | dst[0] = dumpPalette[pixelIndex0 * 3] << 2 | dumpPalette[pixelIndex0 * 3]; | ||
1022 | dst[1] = dumpPalette[pixelIndex0 * 3 + 1] << 2 | dumpPalette[pixelIndex0 * 3 + 1]; | ||
1023 | dst[2] = dumpPalette[pixelIndex0 * 3 + 2] << 2 | dumpPalette[pixelIndex0 * 3 + 2]; | ||
1024 | //dst[3] = 0xFF; | ||
1025 | dst += 3; | ||
1026 | |||
1027 | dst[0] = dumpPalette[pixelIndex1 * 3] << 2 | dumpPalette[pixelIndex1 * 3]; | ||
1028 | dst[1] = dumpPalette[pixelIndex1 * 3 + 1] << 2 | dumpPalette[pixelIndex1 * 3 + 1]; | ||
1029 | dst[2] = dumpPalette[pixelIndex1 * 3 + 2] << 2 | dumpPalette[pixelIndex1 * 3 + 2]; | ||
1030 | //dst[3] = 0xFF; | ||
1031 | dst += 3; | ||
1032 | |||
1033 | src++; | ||
1034 | } | ||
1035 | } | ||
1036 | |||
1037 | void bgDumpFrameBuffer(uint8_t *src, uint8_t *dst, int x, int y) | ||
1038 | { | ||
1039 | |||
1040 | for (int line = 199 ; line >= 0 ; line--) | ||
1041 | { | ||
1042 | bgWriteLine(dst + x * 3 + y * 320 * 3 , src + line * 160, 160); | ||
1043 | dst += 320 * 3; | ||
1044 | } | ||
1045 | } | ||
1046 | |||
1047 | #include "png.h" | ||
1048 | int bgSaveAsSpecifiedPNG(char* path, uint8_t* pixels, int depth = 8, int format = PNG_COLOR_TYPE_RGB) | ||
1049 | { | ||
1050 | #if 0 | ||
1051 | FILE * fp; | ||
1052 | png_structp png_ptr = NULL; | ||
1053 | png_infop info_ptr = NULL; | ||
1054 | png_byte ** row_pointers = NULL; | ||
1055 | int status = -1; | ||
1056 | int bytePerPixel = 0; | ||
1057 | int y; | ||
1058 | |||
1059 | fp = fopen (path, "wb"); | ||
1060 | if (! fp) { | ||
1061 | goto fopen_failed; | ||
1062 | } | ||
1063 | |||
1064 | png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); | ||
1065 | if (png_ptr == NULL) { | ||
1066 | goto png_create_write_struct_failed; | ||
1067 | } | ||
1068 | |||
1069 | info_ptr = png_create_info_struct (png_ptr); | ||
1070 | if (info_ptr == NULL) { | ||
1071 | goto png_create_info_struct_failed; | ||
1072 | } | ||
1073 | |||
1074 | if (setjmp (png_jmpbuf (png_ptr))) { | ||
1075 | goto png_failure; | ||
1076 | } | ||
1077 | |||
1078 | /* Set image attributes. */ | ||
1079 | |||
1080 | png_set_IHDR (png_ptr, | ||
1081 | info_ptr, | ||
1082 | 320, | ||
1083 | 200, | ||
1084 | depth, | ||
1085 | format, | ||
1086 | PNG_INTERLACE_NONE, | ||
1087 | PNG_COMPRESSION_TYPE_DEFAULT, | ||
1088 | PNG_FILTER_TYPE_DEFAULT); | ||
1089 | |||
1090 | if (format == PNG_COLOR_TYPE_GRAY ) | ||
1091 | bytePerPixel = depth / 8 * 1; | ||
1092 | else | ||
1093 | bytePerPixel = depth / 8 * 3; | ||
1094 | |||
1095 | row_pointers = (png_byte **)png_malloc (png_ptr, 200 * sizeof (png_byte *)); | ||
1096 | //for (y = vid.height-1; y >=0; --y) | ||
1097 | for (y = 0; y < 200; y++) | ||
1098 | { | ||
1099 | row_pointers[y] = (png_byte*)&pixels[320 * (200 - y) * bytePerPixel]; | ||
1100 | } | ||
1101 | |||
1102 | png_init_io (png_ptr, fp); | ||
1103 | png_set_rows (png_ptr, info_ptr, row_pointers); | ||
1104 | png_write_png (png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL); | ||
1105 | //png_read_image (png_ptr, info_ptr);// | ||
1106 | |||
1107 | status = 0; | ||
1108 | |||
1109 | png_free (png_ptr, row_pointers); | ||
1110 | |||
1111 | |||
1112 | png_failure: | ||
1113 | png_create_info_struct_failed: | ||
1114 | png_destroy_write_struct (&png_ptr, &info_ptr); | ||
1115 | |||
1116 | png_create_write_struct_failed: | ||
1117 | fclose (fp); | ||
1118 | fopen_failed: | ||
1119 | return status; | ||
1120 | #endif | ||
1121 | } | ||
1122 | |||
1123 | int bgFrameBufferCounter = 0; | ||
1124 | |||
1125 | void video_dumpBackGroundBuffer() | ||
1126 | { | ||
1127 | if (v->_curPagePtr1 != v->_pagePtrs[0]) | ||
1128 | return; | ||
1129 | |||
1130 | uint8_t bgBuffer[320 * 200 * 3]; | ||
1131 | bgDumpFrameBuffer(v->_curPagePtr1, bgBuffer, 0, 0); | ||
1132 | |||
1133 | |||
1134 | char path[256]; | ||
1135 | //sprintf(path,"%4d%s.png",traceFrameBufferCounter,comment); | ||
1136 | sprintf(path, "bg%4d.png", bgFrameBufferCounter++); | ||
1137 | |||
1138 | bgSaveAsSpecifiedPNG(path, bgBuffer); | ||
1139 | } | ||
1140 | |||
1141 | #endif | ||