1142 lines
33 KiB
C
1142 lines
33 KiB
C
|
/***************************************************************************
|
||
|
* __________ __ ___.
|
||
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||
|
* \/ \/ \/ \/ \/
|
||
|
* $Id$
|
||
|
*
|
||
|
* Copyright (C) 2014 Franklin Wei, Benjamin Brown
|
||
|
* Copyright (C) 2004 Gregory Montoir
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or
|
||
|
* modify it under the terms of the GNU General Public License
|
||
|
* as published by the Free Software Foundation; either version 2
|
||
|
* of the License, or (at your option) any later version.
|
||
|
*
|
||
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||
|
* KIND, either express or implied.
|
||
|
*
|
||
|
***************************************************************************/
|
||
|
|
||
|
#include "video.h"
|
||
|
#include "video_data.h"
|
||
|
#include "resource.h"
|
||
|
#include "serializer.h"
|
||
|
#include "sys.h"
|
||
|
#include "file.h"
|
||
|
|
||
|
void polygon_readVertices(struct Polygon* g, const uint8_t *p, uint16_t zoom) {
|
||
|
g->bbw = (*p++) * zoom / 64;
|
||
|
g->bbh = (*p++) * zoom / 64;
|
||
|
g->numPoints = *p++;
|
||
|
assert((g->numPoints & 1) == 0 && g->numPoints < MAX_POINTS);
|
||
|
|
||
|
//Read all points, directly from bytecode segment
|
||
|
for (int i = 0; i < g->numPoints; ++i) {
|
||
|
struct Point *pt = &g->points[i];
|
||
|
pt->x = (*p++) * zoom / 64;
|
||
|
pt->y = (*p++) * zoom / 64;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void video_create(struct Video* v, struct Resource* res, struct System* sys)
|
||
|
{
|
||
|
v->res = res;
|
||
|
v->sys = sys;
|
||
|
}
|
||
|
|
||
|
void video_init(struct Video* v) {
|
||
|
v->paletteIdRequested = NO_PALETTE_CHANGE_REQUESTED;
|
||
|
v->page_data = v->res->_memPtrStart + MEM_BLOCK_SIZE;
|
||
|
|
||
|
rb->memset(v->page_data, 0, 4 * VID_PAGE_SIZE);
|
||
|
|
||
|
for (int i = 0; i < 4; ++i) {
|
||
|
v->_pagePtrs[i] = v->page_data + i * VID_PAGE_SIZE;
|
||
|
}
|
||
|
|
||
|
v->_curPagePtr3 = video_getPagePtr(v, 1);
|
||
|
v->_curPagePtr2 = video_getPagePtr(v, 2);
|
||
|
|
||
|
video_changePagePtr1(v, 0xFE);
|
||
|
|
||
|
v->_interpTable[0] = 0x4000;
|
||
|
|
||
|
for (int i = 1; i < 0x400; ++i) {
|
||
|
v->_interpTable[i] = 0x4000 / i;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void video_setDataBuffer(struct Video* v, uint8_t *dataBuf, uint16_t offset) {
|
||
|
|
||
|
v->_dataBuf = dataBuf;
|
||
|
v->_pData.pc = dataBuf + offset;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* A shape can be given in two different ways:
|
||
|
|
||
|
- A list of screenspace vertices.
|
||
|
- A list of objectspace vertices, based on a delta from the first vertex.
|
||
|
|
||
|
This is a recursive function. */
|
||
|
void video_readAndDrawPolygon(struct Video* v, uint8_t color, uint16_t zoom, const struct Point *pt) {
|
||
|
|
||
|
uint8_t i = scriptPtr_fetchByte(&v->_pData);
|
||
|
|
||
|
//This is
|
||
|
if (i >= 0xC0) { // 0xc0 = 192
|
||
|
|
||
|
// WTF ?
|
||
|
if (color & 0x80) { //0x80 = 128 (1000 0000)
|
||
|
color = i & 0x3F; //0x3F = 63 (0011 1111)
|
||
|
}
|
||
|
|
||
|
// pc is misleading here since we are not reading bytecode but only
|
||
|
// vertices informations.
|
||
|
polygon_readVertices(&v->polygon, v->_pData.pc, zoom);
|
||
|
|
||
|
video_fillPolygon(v, color, zoom, pt);
|
||
|
|
||
|
|
||
|
|
||
|
} else {
|
||
|
i &= 0x3F; //0x3F = 63
|
||
|
if (i == 1) {
|
||
|
warning("video_readAndDrawPolygon() ec=0x%X (i != 2)", 0xF80);
|
||
|
} else if (i == 2) {
|
||
|
video_readAndDrawPolygonHierarchy(v, zoom, pt);
|
||
|
|
||
|
} else {
|
||
|
warning("video_readAndDrawPolygon() ec=0x%X (i != 2)", 0xFBB);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
void video_fillPolygon(struct Video* v, uint16_t color, uint16_t zoom, const struct Point *pt) {
|
||
|
|
||
|
(void) zoom;
|
||
|
|
||
|
if (v->polygon.bbw == 0 && v->polygon.bbh == 1 && v->polygon.numPoints == 4) {
|
||
|
video_drawPoint(v, color, pt->x, pt->y);
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
int16_t x1 = pt->x - v->polygon.bbw / 2;
|
||
|
int16_t x2 = pt->x + v->polygon.bbw / 2;
|
||
|
int16_t y1 = pt->y - v->polygon.bbh / 2;
|
||
|
int16_t y2 = pt->y + v->polygon.bbh / 2;
|
||
|
|
||
|
if (x1 > 319 || x2 < 0 || y1 > 199 || y2 < 0)
|
||
|
return;
|
||
|
|
||
|
v->_hliney = y1;
|
||
|
|
||
|
uint16_t i, j;
|
||
|
i = 0;
|
||
|
j = v->polygon.numPoints - 1;
|
||
|
|
||
|
x2 = v->polygon.points[i].x + x1;
|
||
|
x1 = v->polygon.points[j].x + x1;
|
||
|
|
||
|
++i;
|
||
|
--j;
|
||
|
|
||
|
drawLine drawFct;
|
||
|
if (color < 0x10) {
|
||
|
drawFct = &video_drawLineN;
|
||
|
} else if (color > 0x10) {
|
||
|
drawFct = &video_drawLineP;
|
||
|
} else {
|
||
|
drawFct = &video_drawLineBlend;
|
||
|
}
|
||
|
|
||
|
uint32_t cpt1 = x1 << 16;
|
||
|
uint32_t cpt2 = x2 << 16;
|
||
|
|
||
|
while (1) {
|
||
|
v->polygon.numPoints -= 2;
|
||
|
if (v->polygon.numPoints == 0) {
|
||
|
#if TRACE_FRAMEBUFFER
|
||
|
video_dumpFrameBuffers(v, "fillPolygonEnd");
|
||
|
#endif
|
||
|
#if TRACE_BG_BUFFER
|
||
|
video_dumpBackGroundBuffer(v);
|
||
|
#endif
|
||
|
break;
|
||
|
}
|
||
|
uint16_t h;
|
||
|
int32_t step1 = video_calcStep(v, &v->polygon.points[j + 1], &v->polygon.points[j], &h);
|
||
|
int32_t step2 = video_calcStep(v, &v->polygon.points[i - 1], &v->polygon.points[i], &h);
|
||
|
|
||
|
++i;
|
||
|
--j;
|
||
|
|
||
|
cpt1 = (cpt1 & 0xFFFF0000) | 0x7FFF;
|
||
|
cpt2 = (cpt2 & 0xFFFF0000) | 0x8000;
|
||
|
|
||
|
if (h == 0) {
|
||
|
cpt1 += step1;
|
||
|
cpt2 += step2;
|
||
|
} else {
|
||
|
for (; h != 0; --h) {
|
||
|
if (v->_hliney >= 0) {
|
||
|
x1 = cpt1 >> 16;
|
||
|
x2 = cpt2 >> 16;
|
||
|
if (x1 <= 319 && x2 >= 0) {
|
||
|
if (x1 < 0) x1 = 0;
|
||
|
if (x2 > 319) x2 = 319;
|
||
|
(*drawFct)(v, x1, x2, color);
|
||
|
}
|
||
|
}
|
||
|
cpt1 += step1;
|
||
|
cpt2 += step2;
|
||
|
++v->_hliney;
|
||
|
if (v->_hliney > 199) return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if TRACE_FRAMEBUFFER
|
||
|
video_dumpFrameBuffers(v, "fillPolygonChild");
|
||
|
#endif
|
||
|
#if TRACE_BG_BUFFER
|
||
|
|
||
|
video_dumpBackGroundBuffer(v);
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
What is read from the bytecode is not a pure screnspace polygon but a polygonspace polygon.
|
||
|
|
||
|
*/
|
||
|
void video_readAndDrawPolygonHierarchy(struct Video* v, uint16_t zoom, const struct Point *pgc) {
|
||
|
|
||
|
struct Point pt = *pgc;
|
||
|
pt.x -= scriptPtr_fetchByte(&v->_pData) * zoom / 64;
|
||
|
pt.y -= scriptPtr_fetchByte(&v->_pData) * zoom / 64;
|
||
|
|
||
|
int16_t childs = scriptPtr_fetchByte(&v->_pData);
|
||
|
debug(DBG_VIDEO, "video_readAndDrawPolygonHierarchy childs=%d", childs);
|
||
|
|
||
|
for ( ; childs >= 0; --childs) {
|
||
|
|
||
|
uint16_t off = scriptPtr_fetchWord(&v->_pData);
|
||
|
|
||
|
struct Point po;
|
||
|
po = pt;
|
||
|
po.x += scriptPtr_fetchByte(&v->_pData) * zoom / 64;
|
||
|
po.y += scriptPtr_fetchByte(&v->_pData) * zoom / 64;
|
||
|
|
||
|
uint16_t color = 0xFF;
|
||
|
uint16_t _bp = off;
|
||
|
off &= 0x7FFF;
|
||
|
|
||
|
if (_bp & 0x8000) {
|
||
|
color = *v->_pData.pc & 0x7F;
|
||
|
v->_pData.pc += 2;
|
||
|
}
|
||
|
|
||
|
uint8_t *bak = v->_pData.pc;
|
||
|
v->_pData.pc = v->_dataBuf + off * 2;
|
||
|
|
||
|
|
||
|
video_readAndDrawPolygon(v, color, zoom, &po);
|
||
|
|
||
|
|
||
|
v->_pData.pc = bak;
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
int32_t video_calcStep(struct Video* v, const struct Point *p1, const struct Point *p2, uint16_t *dy) {
|
||
|
(void) v;
|
||
|
*dy = p2->y - p1->y;
|
||
|
return (p2->x - p1->x) * v->_interpTable[*dy] * 4;
|
||
|
}
|
||
|
|
||
|
void video_drawString(struct Video* v, uint8_t color, uint16_t x, uint16_t y, uint16_t stringId) {
|
||
|
|
||
|
const struct StrEntry *se = video_stringsTableEng;
|
||
|
|
||
|
//Search for the location where the string is located.
|
||
|
while (se->id != END_OF_STRING_DICTIONARY && se->id != stringId)
|
||
|
++se;
|
||
|
|
||
|
debug(DBG_VIDEO, "video_drawString(%d, %d, %d, '%s')", color, x, y, se->str);
|
||
|
|
||
|
//Not found
|
||
|
if (se->id == END_OF_STRING_DICTIONARY)
|
||
|
return;
|
||
|
|
||
|
|
||
|
//Used if the string contains a return carriage.
|
||
|
uint16_t xOrigin = x;
|
||
|
int len = rb->strlen(se->str);
|
||
|
for (int i = 0; i < len; ++i) {
|
||
|
|
||
|
if (se->str[i] == '\n') {
|
||
|
y += 8;
|
||
|
x = xOrigin;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
video_drawChar(v, se->str[i], x, y, color, v->_curPagePtr1);
|
||
|
x++;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void video_drawChar(struct Video* v, uint8_t character, uint16_t x, uint16_t y, uint8_t color, uint8_t *buf) {
|
||
|
(void) v;
|
||
|
if (x <= 39 && y <= 192) {
|
||
|
|
||
|
/* each character is 8x8 */
|
||
|
const uint8_t *ft = video_font + (character - ' ') * 8;
|
||
|
|
||
|
/* x is multiplied by 4 and not 8 because there are two pixels per byte */
|
||
|
uint8_t *p = buf + x * 4 + y * 160;
|
||
|
|
||
|
for (int j = 0; j < 8; ++j) {
|
||
|
uint8_t ch = *(ft + j);
|
||
|
for (int i = 0; i < 4; ++i) {
|
||
|
uint8_t b = *(p + i);
|
||
|
uint8_t cmask = 0xFF;
|
||
|
uint8_t colb = 0;
|
||
|
if (ch & 0x80) {
|
||
|
colb |= color << 4;
|
||
|
cmask &= 0x0F;
|
||
|
}
|
||
|
ch <<= 1;
|
||
|
if (ch & 0x80) {
|
||
|
colb |= color;
|
||
|
cmask &= 0xF0;
|
||
|
}
|
||
|
ch <<= 1;
|
||
|
*(p + i) = (b & cmask) | colb;
|
||
|
}
|
||
|
/* skip to the next line (320 pixels = 160 bytes) */
|
||
|
p += 160;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void video_drawPoint(struct Video* v, uint8_t color, int16_t x, int16_t y) {
|
||
|
debug(DBG_VIDEO, "drawPoint(%d, %d, %d)", color, x, y);
|
||
|
if (x >= 0 && x <= 319 && y >= 0 && y <= 199) {
|
||
|
uint16_t off = y * 160 + x / 2;
|
||
|
|
||
|
uint8_t cmasko, cmaskn;
|
||
|
if (x & 1) {
|
||
|
cmaskn = 0x0F;
|
||
|
cmasko = 0xF0;
|
||
|
} else {
|
||
|
cmaskn = 0xF0;
|
||
|
cmasko = 0x0F;
|
||
|
}
|
||
|
|
||
|
uint8_t colb = (color << 4) | color;
|
||
|
if (color == 0x10) {
|
||
|
cmaskn &= 0x88;
|
||
|
cmasko = ~cmaskn;
|
||
|
colb = 0x88;
|
||
|
} else if (color == 0x11) {
|
||
|
colb = *(v->_pagePtrs[0] + off);
|
||
|
}
|
||
|
uint8_t b = *(v->_curPagePtr1 + off);
|
||
|
*(v->_curPagePtr1 + off) = (b & cmasko) | (colb & cmaskn);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Blend a line in the current framebuffer (v->_curPagePtr1)
|
||
|
*/
|
||
|
void video_drawLineBlend(struct Video* v, int16_t x1, int16_t x2, uint8_t color) {
|
||
|
/* silence warnings without XWORLD_DEBUG */
|
||
|
(void) color;
|
||
|
debug(DBG_VIDEO, "drawLineBlend(%d, %d, %d)", x1, x2, color);
|
||
|
int16_t xmax = MAX(x1, x2);
|
||
|
int16_t xmin = MIN(x1, x2);
|
||
|
uint8_t *p = v->_curPagePtr1 + v->_hliney * 160 + xmin / 2;
|
||
|
|
||
|
uint16_t w = xmax / 2 - xmin / 2 + 1;
|
||
|
uint8_t cmaske = 0;
|
||
|
uint8_t cmasks = 0;
|
||
|
if (xmin & 1) {
|
||
|
--w;
|
||
|
cmasks = 0xF7;
|
||
|
}
|
||
|
if (!(xmax & 1)) {
|
||
|
--w;
|
||
|
cmaske = 0x7F;
|
||
|
}
|
||
|
|
||
|
if (cmasks != 0) {
|
||
|
*p = (*p & cmasks) | 0x08;
|
||
|
++p;
|
||
|
}
|
||
|
while (w--) {
|
||
|
*p = (*p & 0x77) | 0x88;
|
||
|
++p;
|
||
|
}
|
||
|
if (cmaske != 0) {
|
||
|
*p = (*p & cmaske) | 0x80;
|
||
|
++p;
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
void video_drawLineN(struct Video* v, int16_t x1, int16_t x2, uint8_t color) {
|
||
|
debug(DBG_VIDEO, "drawLineN(%d, %d, %d)", x1, x2, color);
|
||
|
int16_t xmax = MAX(x1, x2);
|
||
|
int16_t xmin = MIN(x1, x2);
|
||
|
uint8_t *p = v->_curPagePtr1 + v->_hliney * 160 + xmin / 2;
|
||
|
|
||
|
uint16_t w = xmax / 2 - xmin / 2 + 1;
|
||
|
uint8_t cmaske = 0;
|
||
|
uint8_t cmasks = 0;
|
||
|
if (xmin & 1) {
|
||
|
--w;
|
||
|
cmasks = 0xF0;
|
||
|
}
|
||
|
if (!(xmax & 1)) {
|
||
|
--w;
|
||
|
cmaske = 0x0F;
|
||
|
}
|
||
|
|
||
|
uint8_t colb = ((color & 0xF) << 4) | (color & 0xF);
|
||
|
if (cmasks != 0) {
|
||
|
*p = (*p & cmasks) | (colb & 0x0F);
|
||
|
++p;
|
||
|
}
|
||
|
while (w--) {
|
||
|
*p++ = colb;
|
||
|
}
|
||
|
if (cmaske != 0) {
|
||
|
*p = (*p & cmaske) | (colb & 0xF0);
|
||
|
++p;
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
void video_drawLineP(struct Video* v, int16_t x1, int16_t x2, uint8_t color) {
|
||
|
/* silence warnings without XWORLD_DEBUG */
|
||
|
(void) color;
|
||
|
debug(DBG_VIDEO, "drawLineP(%d, %d, %d)", x1, x2, color);
|
||
|
int16_t xmax = MAX(x1, x2);
|
||
|
int16_t xmin = MIN(x1, x2);
|
||
|
uint16_t off = v->_hliney * 160 + xmin / 2;
|
||
|
uint8_t *p = v->_curPagePtr1 + off;
|
||
|
uint8_t *q = v->_pagePtrs[0] + off;
|
||
|
|
||
|
uint8_t w = xmax / 2 - xmin / 2 + 1;
|
||
|
uint8_t cmaske = 0;
|
||
|
uint8_t cmasks = 0;
|
||
|
if (xmin & 1) {
|
||
|
--w;
|
||
|
cmasks = 0xF0;
|
||
|
}
|
||
|
if (!(xmax & 1)) {
|
||
|
--w;
|
||
|
cmaske = 0x0F;
|
||
|
}
|
||
|
|
||
|
if (cmasks != 0) {
|
||
|
*p = (*p & cmasks) | (*q & 0x0F);
|
||
|
++p;
|
||
|
++q;
|
||
|
}
|
||
|
while (w--) {
|
||
|
*p++ = *q++;
|
||
|
}
|
||
|
if (cmaske != 0) {
|
||
|
*p = (*p & cmaske) | (*q & 0xF0);
|
||
|
++p;
|
||
|
++q;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
uint8_t *video_getPagePtr(struct Video* v, uint8_t page) {
|
||
|
uint8_t *p;
|
||
|
if (page <= 3) {
|
||
|
p = v->_pagePtrs[page];
|
||
|
} else {
|
||
|
switch (page) {
|
||
|
case 0xFF:
|
||
|
p = v->_curPagePtr3;
|
||
|
break;
|
||
|
case 0xFE:
|
||
|
p = v->_curPagePtr2;
|
||
|
break;
|
||
|
default:
|
||
|
p = v->_pagePtrs[0]; // XXX check
|
||
|
warning("video_getPagePtr() p != [0,1,2,3,0xFF,0xFE] == 0x%X", page);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return p;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void video_changePagePtr1(struct Video* v, uint8_t page) {
|
||
|
debug(DBG_VIDEO, "video_changePagePtr1(%d)", page);
|
||
|
v->_curPagePtr1 = video_getPagePtr(v, page);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void video_fillPage(struct Video* v, uint8_t pageId, uint8_t color) {
|
||
|
debug(DBG_VIDEO, "video_fillPage(%d, %d)", pageId, color);
|
||
|
uint8_t *p = video_getPagePtr(v, pageId);
|
||
|
|
||
|
// Since a palette indice is coded on 4 bits, we need to duplicate the
|
||
|
// clearing color to the upper part of the byte.
|
||
|
uint8_t c = (color << 4) | color;
|
||
|
|
||
|
rb->memset(p, c, VID_PAGE_SIZE);
|
||
|
|
||
|
#if TRACE_FRAMEBUFFER
|
||
|
video_dumpFrameBuffers(v, "-fillPage");
|
||
|
#endif
|
||
|
#if TRACE_BG_BUFFER
|
||
|
|
||
|
video_dumpBackGroundBuffer(v);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
#if TRACE_FRAMEBUFFER
|
||
|
#define SCREENSHOT_BPP 3
|
||
|
int traceFrameBufferCounter = 0;
|
||
|
uint8_t allFrameBuffers[640 * 400 * SCREENSHOT_BPP];
|
||
|
|
||
|
#endif
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/* This opcode is used once the background of a scene has been drawn in one of the framebuffer:
|
||
|
it is copied in the current framebuffer at the start of a new frame in order to improve performances. */
|
||
|
void video_copyPage(struct Video* v, uint8_t srcPageId, uint8_t dstPageId, int16_t vscroll) {
|
||
|
|
||
|
debug(DBG_VIDEO, "video_copyPage(%d, %d)", srcPageId, dstPageId);
|
||
|
|
||
|
if (srcPageId == dstPageId)
|
||
|
return;
|
||
|
|
||
|
uint8_t *p;
|
||
|
uint8_t *q;
|
||
|
|
||
|
if (srcPageId >= 0xFE || !((srcPageId &= 0xBF) & 0x80)) {
|
||
|
p = video_getPagePtr(v, srcPageId);
|
||
|
q = video_getPagePtr(v, dstPageId);
|
||
|
memcpy(q, p, VID_PAGE_SIZE);
|
||
|
|
||
|
} else {
|
||
|
p = video_getPagePtr(v, srcPageId & 3);
|
||
|
q = video_getPagePtr(v, dstPageId);
|
||
|
if (vscroll >= -199 && vscroll <= 199) {
|
||
|
uint16_t h = 200;
|
||
|
if (vscroll < 0) {
|
||
|
h += vscroll;
|
||
|
p += -vscroll * 160;
|
||
|
} else {
|
||
|
h -= vscroll;
|
||
|
q += vscroll * 160;
|
||
|
}
|
||
|
memcpy(q, p, h * 160);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
#if TRACE_FRAMEBUFFER
|
||
|
char name[256];
|
||
|
rb->memset(name, 0, sizeof(name));
|
||
|
sprintf(name, "copyPage_0x%X_to_0x%X", (p - v->_pagePtrs[0]) / VID_PAGE_SIZE, (q - v->_pagePtrs[0]) / VID_PAGE_SIZE);
|
||
|
dumpFrameBuffers(name);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
void video_copyPagePtr(struct Video* v, const uint8_t *src) {
|
||
|
debug(DBG_VIDEO, "video_copyPagePtr()");
|
||
|
uint8_t *dst = v->_pagePtrs[0];
|
||
|
int h = 200;
|
||
|
while (h--) {
|
||
|
int w = 40;
|
||
|
while (w--) {
|
||
|
uint8_t p[] = {
|
||
|
*(src + 8000 * 3),
|
||
|
*(src + 8000 * 2),
|
||
|
*(src + 8000 * 1),
|
||
|
*(src + 8000 * 0)
|
||
|
};
|
||
|
for(int j = 0; j < 4; ++j) {
|
||
|
uint8_t acc = 0;
|
||
|
for (int i = 0; i < 8; ++i) {
|
||
|
acc <<= 1;
|
||
|
acc |= (p[i & 3] & 0x80) ? 1 : 0;
|
||
|
p[i & 3] <<= 1;
|
||
|
}
|
||
|
*dst++ = acc;
|
||
|
}
|
||
|
++src;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
uint8_t *video_allocPage() {
|
||
|
uint8_t *buf = (uint8_t *)malloc(VID_PAGE_SIZE);
|
||
|
rb->memset(buf, 0, VID_PAGE_SIZE);
|
||
|
return buf;
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
|
||
|
|
||
|
#if TRACE_FRAMEBUFFER
|
||
|
int dumpPaletteCursor = 0;
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
Note: The palettes set used to be allocated on the stack but I moved it to
|
||
|
the heap so I could dump the four framebuffer and follow how
|
||
|
frames are generated.
|
||
|
*/
|
||
|
uint8_t pal[NUM_COLORS * 3]; //3 = BYTES_PER_PIXEL
|
||
|
void video_changePal(struct Video* v, uint8_t palNum) {
|
||
|
debug(DBG_VIDEO, "video_changePal(v=0x%08x, palNum=%d", v, palNum);
|
||
|
if (palNum >= 32)
|
||
|
return;
|
||
|
|
||
|
uint8_t *p = v->res->segPalettes + palNum * 32; //colors are coded on 2bytes (565) for 16 colors = 32
|
||
|
debug(DBG_VIDEO, "segPalettes: 0x%08x", v->res->segPalettes);
|
||
|
// Moved to the heap, legacy code used to allocate the palette
|
||
|
// on the stack.
|
||
|
//uint8_t pal[NUM_COLORS * 3]; //3 = BYTES_PER_PIXEL
|
||
|
|
||
|
for (int i = 0; i < NUM_COLORS; ++i)
|
||
|
{
|
||
|
debug(DBG_VIDEO, "i: %d", i);
|
||
|
debug(DBG_VIDEO, "p: 0x%08x", p);
|
||
|
uint8_t c1 = *(p + 0);
|
||
|
uint8_t c2 = *(p + 1);
|
||
|
p += 2;
|
||
|
pal[i * 3 + 0] = ((c1 & 0x0F) << 2) | ((c1 & 0x0F) >> 2); // r
|
||
|
pal[i * 3 + 1] = ((c2 & 0xF0) >> 2) | ((c2 & 0xF0) >> 6); // g
|
||
|
pal[i * 3 + 2] = ((c2 & 0x0F) >> 2) | ((c2 & 0x0F) << 2); // b
|
||
|
}
|
||
|
|
||
|
sys_setPalette(v->sys, 0, NUM_COLORS, pal);
|
||
|
v->currentPaletteId = palNum;
|
||
|
|
||
|
|
||
|
#if TRACE_PALETTE
|
||
|
printf("\nuint8_t dumpPalette[48] = {\n");
|
||
|
for (int i = 0; i < NUM_COLORS; ++i)
|
||
|
{
|
||
|
printf("0x%X,0x%X,0x%X,", pal[i * 3 + 0], pal[i * 3 + 1], pal[i * 3 + 2]);
|
||
|
}
|
||
|
printf("\n};\n");
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#if TRACE_FRAMEBUFFER
|
||
|
dumpPaletteCursor++;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void video_updateDisplay(struct Video* v, uint8_t pageId) {
|
||
|
|
||
|
debug(DBG_VIDEO, "video_updateDisplay(%d)", pageId);
|
||
|
|
||
|
if (pageId != 0xFE) {
|
||
|
if (pageId == 0xFF) {
|
||
|
/* swap ptrs 2 and 3 */
|
||
|
uint8_t* temp = v->_curPagePtr3;
|
||
|
v->_curPagePtr3 = v->_curPagePtr2;
|
||
|
v->_curPagePtr2 = temp;
|
||
|
} else {
|
||
|
v->_curPagePtr2 = video_getPagePtr(v, pageId);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//Check if we need to change the palette
|
||
|
if (v->paletteIdRequested != NO_PALETTE_CHANGE_REQUESTED) {
|
||
|
video_changePal(v, v->paletteIdRequested);
|
||
|
v->paletteIdRequested = NO_PALETTE_CHANGE_REQUESTED;
|
||
|
}
|
||
|
|
||
|
//Q: Why 160 ?
|
||
|
//A: Because one byte gives two palette indices so
|
||
|
// we only need to move 320/2 per line.
|
||
|
sys_copyRect(v->sys, 0, 0, 320, 200, v->_curPagePtr2, 160);
|
||
|
|
||
|
#if TRACE_FRAMEBUFFER
|
||
|
dumpFrameBuffer(v->_curPagePtr2, allFrameBuffers, 320, 200);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void video_saveOrLoad(struct Video* v, struct Serializer *ser) {
|
||
|
uint8_t mask = 0;
|
||
|
if (ser->_mode == SM_SAVE) {
|
||
|
for (int i = 0; i < 4; ++i) {
|
||
|
if (v->_pagePtrs[i] == v->_curPagePtr1)
|
||
|
mask |= i << 4;
|
||
|
if (v->_pagePtrs[i] == v->_curPagePtr2)
|
||
|
mask |= i << 2;
|
||
|
if (v->_pagePtrs[i] == v->_curPagePtr3)
|
||
|
mask |= i << 0;
|
||
|
}
|
||
|
}
|
||
|
struct Entry entries[] = {
|
||
|
SE_INT(&v->currentPaletteId, SES_INT8, VER(1)),
|
||
|
SE_INT(&v->paletteIdRequested, SES_INT8, VER(1)),
|
||
|
SE_INT(&mask, SES_INT8, VER(1)),
|
||
|
SE_ARRAY(v->_pagePtrs[0], VID_PAGE_SIZE, SES_INT8, VER(1)),
|
||
|
SE_ARRAY(v->_pagePtrs[1], VID_PAGE_SIZE, SES_INT8, VER(1)),
|
||
|
SE_ARRAY(v->_pagePtrs[2], VID_PAGE_SIZE, SES_INT8, VER(1)),
|
||
|
SE_ARRAY(v->_pagePtrs[3], VID_PAGE_SIZE, SES_INT8, VER(1)),
|
||
|
SE_END()
|
||
|
};
|
||
|
ser_saveOrLoadEntries(ser, entries);
|
||
|
|
||
|
if (ser->_mode == SM_LOAD) {
|
||
|
v->_curPagePtr1 = v->_pagePtrs[(mask >> 4) & 0x3];
|
||
|
v->_curPagePtr2 = v->_pagePtrs[(mask >> 2) & 0x3];
|
||
|
v->_curPagePtr3 = v->_pagePtrs[(mask >> 0) & 0x3];
|
||
|
video_changePal(v, v->currentPaletteId);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
#if TRACE_FRAMEBUFFER
|
||
|
|
||
|
|
||
|
uint8_t allPalettesDump[][48] = {
|
||
|
|
||
|
|
||
|
{
|
||
|
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,
|
||
|
},
|
||
|
|
||
|
{
|
||
|
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,
|
||
|
}
|
||
|
|
||
|
, {
|
||
|
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,
|
||
|
}
|
||
|
|
||
|
, {
|
||
|
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,
|
||
|
}
|
||
|
|
||
|
, {
|
||
|
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,
|
||
|
}
|
||
|
|
||
|
, {
|
||
|
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,
|
||
|
}
|
||
|
|
||
|
, {
|
||
|
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,
|
||
|
}
|
||
|
|
||
|
, {
|
||
|
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,
|
||
|
}
|
||
|
|
||
|
, {
|
||
|
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,
|
||
|
}
|
||
|
|
||
|
, {
|
||
|
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,
|
||
|
}
|
||
|
|
||
|
, {
|
||
|
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,
|
||
|
}
|
||
|
|
||
|
, {
|
||
|
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,
|
||
|
}
|
||
|
|
||
|
, {
|
||
|
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,
|
||
|
}
|
||
|
|
||
|
, {
|
||
|
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,
|
||
|
}
|
||
|
|
||
|
, {
|
||
|
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,
|
||
|
}
|
||
|
|
||
|
, {
|
||
|
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,
|
||
|
}
|
||
|
|
||
|
, {
|
||
|
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\
|
||
|
1D, 0x26, 0x15,
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
#include "png.h"
|
||
|
int GL_FCS_SaveAsSpecifiedPNG(char* path, uint8_t* pixels, int depth = 8, int format = PNG_COLOR_TYPE_RGB)
|
||
|
{
|
||
|
png_structp png_ptr = NULL;
|
||
|
png_infop info_ptr = NULL;
|
||
|
png_byte ** row_pointers = NULL;
|
||
|
int status = -1;
|
||
|
int bytePerPixel = 0;
|
||
|
int y;
|
||
|
|
||
|
int fd = open (path, "wb");
|
||
|
if (fd < 0) {
|
||
|
goto fopen_failed;
|
||
|
}
|
||
|
|
||
|
png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
||
|
if (png_ptr == NULL) {
|
||
|
goto png_create_write_struct_failed;
|
||
|
}
|
||
|
|
||
|
info_ptr = png_create_info_struct (png_ptr);
|
||
|
if (info_ptr == NULL) {
|
||
|
goto png_create_info_struct_failed;
|
||
|
}
|
||
|
|
||
|
if (setjmp (png_jmpbuf (png_ptr))) {
|
||
|
goto png_failure;
|
||
|
}
|
||
|
|
||
|
/* Set image attributes. */
|
||
|
|
||
|
png_set_IHDR (png_ptr,
|
||
|
info_ptr,
|
||
|
640,
|
||
|
400,
|
||
|
depth,
|
||
|
format,
|
||
|
PNG_INTERLACE_NONE,
|
||
|
PNG_COMPRESSION_TYPE_DEFAULT,
|
||
|
PNG_FILTER_TYPE_DEFAULT);
|
||
|
|
||
|
if (format == PNG_COLOR_TYPE_GRAY )
|
||
|
bytePerPixel = depth / 8 * 1;
|
||
|
else
|
||
|
bytePerPixel = depth / 8 * 3;
|
||
|
|
||
|
row_pointers = (png_byte **)png_malloc (png_ptr, 400 * sizeof (png_byte *));
|
||
|
//for (y = vid.height-1; y >=0; --y)
|
||
|
for (y = 0; y < 400; y++)
|
||
|
{
|
||
|
row_pointers[y] = (png_byte*)&pixels[640 * (400 - y) * bytePerPixel];
|
||
|
}
|
||
|
|
||
|
png_init_io (png_ptr, fp);
|
||
|
png_set_rows (png_ptr, info_ptr, row_pointers);
|
||
|
png_write_png (png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
|
||
|
//png_read_image (png_ptr, info_ptr);//
|
||
|
|
||
|
status = 0;
|
||
|
|
||
|
png_free (png_ptr, row_pointers);
|
||
|
|
||
|
|
||
|
png_failure:
|
||
|
png_create_info_struct_failed:
|
||
|
png_destroy_write_struct (&png_ptr, &info_ptr);
|
||
|
|
||
|
png_create_write_struct_failed:
|
||
|
fclose (fp);
|
||
|
fopen_failed:
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
void writeLine(uint8_t *dst, uint8_t *src, int size)
|
||
|
{
|
||
|
uint8_t* dumpPalette;
|
||
|
|
||
|
if (!dumpPaletteCursor)
|
||
|
dumpPalette = allPalettesDump[dumpPaletteCursor];
|
||
|
else
|
||
|
dumpPalette = pal;
|
||
|
|
||
|
for( uint8_t twoPixels = 0 ; twoPixels < size ; twoPixels++)
|
||
|
{
|
||
|
int pixelIndex0 = (*src & 0xF0) >> 4;
|
||
|
pixelIndex0 &= 0x10 - 1;
|
||
|
|
||
|
int pixelIndex1 = (*src & 0xF);
|
||
|
pixelIndex1 &= 0x10 - 1;
|
||
|
|
||
|
//We need to write those two pixels
|
||
|
dst[0] = dumpPalette[pixelIndex0 * 3] << 2 | dumpPalette[pixelIndex0 * 3];
|
||
|
dst[1] = dumpPalette[pixelIndex0 * 3 + 1] << 2 | dumpPalette[pixelIndex0 * 3 + 1];
|
||
|
dst[2] = dumpPalette[pixelIndex0 * 3 + 2] << 2 | dumpPalette[pixelIndex0 * 3 + 2];
|
||
|
//dst[3] = 0xFF;
|
||
|
dst += SCREENSHOT_BPP;
|
||
|
|
||
|
dst[0] = dumpPalette[pixelIndex1 * 3] << 2 | dumpPalette[pixelIndex1 * 3];
|
||
|
dst[1] = dumpPalette[pixelIndex1 * 3 + 1] << 2 | dumpPalette[pixelIndex1 * 3 + 1];
|
||
|
dst[2] = dumpPalette[pixelIndex1 * 3 + 2] << 2 | dumpPalette[pixelIndex1 * 3 + 2];
|
||
|
//dst[3] = 0xFF;
|
||
|
dst += SCREENSHOT_BPP;
|
||
|
|
||
|
src++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void video_dumpFrameBuffer(uint8_t *src, uint8_t *dst, int x, int y)
|
||
|
{
|
||
|
|
||
|
for (int line = 199 ; line >= 0 ; line--)
|
||
|
{
|
||
|
writeLine(dst + x * SCREENSHOT_BPP + y * 640 * SCREENSHOT_BPP , src + line * 160, 160);
|
||
|
dst += 640 * SCREENSHOT_BPP;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void video_dumpFrameBuffers(char* comment)
|
||
|
{
|
||
|
|
||
|
if (!traceFrameBufferCounter)
|
||
|
{
|
||
|
rb->memset(allFrameBuffers, 0, sizeof(allFrameBuffers));
|
||
|
}
|
||
|
|
||
|
|
||
|
dumpFrameBuffer(v->_pagePtrs[1], allFrameBuffers, 0, 0);
|
||
|
dumpFrameBuffer(v->_pagePtrs[0], allFrameBuffers, 0, 200);
|
||
|
dumpFrameBuffer(v->_pagePtrs[2], allFrameBuffers, 320, 0);
|
||
|
//dumpFrameBuffer(v->_pagePtrs[3],allFrameBuffers,320,200);
|
||
|
|
||
|
|
||
|
//if (v->_curPagePtr1 == v->_pagePtrs[3])
|
||
|
//
|
||
|
|
||
|
/*
|
||
|
uint8_t* offScreen = sys->getOffScreenFramebuffer();
|
||
|
for(int i=0 ; i < 200 ; i++)
|
||
|
writeLine(allFrameBuffers+320*3+640*i*3 + 200*640*3, offScreen+320*i/2 , 160);
|
||
|
*/
|
||
|
|
||
|
|
||
|
int frameId = traceFrameBufferCounter++;
|
||
|
//Write bitmap to disk.
|
||
|
|
||
|
|
||
|
|
||
|
// Filling TGA header information
|
||
|
/*
|
||
|
char path[256];
|
||
|
sprintf(path,"test%d.tga",traceFrameBufferCounter);
|
||
|
|
||
|
#define IMAGE_WIDTH 640
|
||
|
#define IMAGE_HEIGHT 400
|
||
|
|
||
|
uint8_t tga_header[18];
|
||
|
rb->memset(tga_header, 0, 18);
|
||
|
tga_header[2] = 2;
|
||
|
tga_header[12] = (IMAGE_WIDTH & 0x00FF);
|
||
|
tga_header[13] = (IMAGE_WIDTH & 0xFF00) / 256;
|
||
|
tga_header[14] = (IMAGE_HEIGHT & 0x00FF) ;
|
||
|
tga_header[15] =(IMAGE_HEIGHT & 0xFF00) / 256;
|
||
|
tga_header[16] = 32 ;
|
||
|
|
||
|
|
||
|
|
||
|
// Open the file, write both header and payload, close, done.
|
||
|
char path[256];
|
||
|
sprintf(path,"test%d.tga",traceFrameBufferCounter);
|
||
|
FILE* pScreenshot = fopen(path, "wb");
|
||
|
fwrite(&tga_header, 18, sizeof(uint8_t), pScreenshot);
|
||
|
fwrite(allFrameBuffers, IMAGE_WIDTH * IMAGE_HEIGHT,SCREENSHOT_BPP * sizeof(uint8_t),pScreenshot);
|
||
|
fclose(pScreenshot);
|
||
|
*/
|
||
|
|
||
|
|
||
|
char path[256];
|
||
|
//sprintf(path,"%4d%s.png",traceFrameBufferCounter,comment);
|
||
|
sprintf(path, "%4d.png", traceFrameBufferCounter);
|
||
|
|
||
|
GL_FCS_SaveAsSpecifiedPNG(path, allFrameBuffers);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#if TRACE_BG_BUFFER
|
||
|
|
||
|
|
||
|
|
||
|
uint8_t bgPalette[48] = {
|
||
|
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,
|
||
|
};
|
||
|
void bgWriteLine(uint8_t *dst, uint8_t *src, int size)
|
||
|
{
|
||
|
uint8_t* dumpPalette;
|
||
|
|
||
|
// if (!dumpPaletteCursor)
|
||
|
// dumpPalette = allPalettesDump[dumpPaletteCursor];
|
||
|
// else
|
||
|
dumpPalette = bgPalette;
|
||
|
|
||
|
for( uint8_t twoPixels = 0 ; twoPixels < size ; twoPixels++)
|
||
|
{
|
||
|
int pixelIndex0 = (*src & 0xF0) >> 4;
|
||
|
pixelIndex0 &= 0x10 - 1;
|
||
|
|
||
|
int pixelIndex1 = (*src & 0xF);
|
||
|
pixelIndex1 &= 0x10 - 1;
|
||
|
|
||
|
//We need to write those two pixels
|
||
|
dst[0] = dumpPalette[pixelIndex0 * 3] << 2 | dumpPalette[pixelIndex0 * 3];
|
||
|
dst[1] = dumpPalette[pixelIndex0 * 3 + 1] << 2 | dumpPalette[pixelIndex0 * 3 + 1];
|
||
|
dst[2] = dumpPalette[pixelIndex0 * 3 + 2] << 2 | dumpPalette[pixelIndex0 * 3 + 2];
|
||
|
//dst[3] = 0xFF;
|
||
|
dst += 3;
|
||
|
|
||
|
dst[0] = dumpPalette[pixelIndex1 * 3] << 2 | dumpPalette[pixelIndex1 * 3];
|
||
|
dst[1] = dumpPalette[pixelIndex1 * 3 + 1] << 2 | dumpPalette[pixelIndex1 * 3 + 1];
|
||
|
dst[2] = dumpPalette[pixelIndex1 * 3 + 2] << 2 | dumpPalette[pixelIndex1 * 3 + 2];
|
||
|
//dst[3] = 0xFF;
|
||
|
dst += 3;
|
||
|
|
||
|
src++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void bgDumpFrameBuffer(uint8_t *src, uint8_t *dst, int x, int y)
|
||
|
{
|
||
|
|
||
|
for (int line = 199 ; line >= 0 ; line--)
|
||
|
{
|
||
|
bgWriteLine(dst + x * 3 + y * 320 * 3 , src + line * 160, 160);
|
||
|
dst += 320 * 3;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#include "png.h"
|
||
|
int bgSaveAsSpecifiedPNG(char* path, uint8_t* pixels, int depth = 8, int format = PNG_COLOR_TYPE_RGB)
|
||
|
{
|
||
|
#if 0
|
||
|
FILE * fp;
|
||
|
png_structp png_ptr = NULL;
|
||
|
png_infop info_ptr = NULL;
|
||
|
png_byte ** row_pointers = NULL;
|
||
|
int status = -1;
|
||
|
int bytePerPixel = 0;
|
||
|
int y;
|
||
|
|
||
|
fp = fopen (path, "wb");
|
||
|
if (! fp) {
|
||
|
goto fopen_failed;
|
||
|
}
|
||
|
|
||
|
png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
||
|
if (png_ptr == NULL) {
|
||
|
goto png_create_write_struct_failed;
|
||
|
}
|
||
|
|
||
|
info_ptr = png_create_info_struct (png_ptr);
|
||
|
if (info_ptr == NULL) {
|
||
|
goto png_create_info_struct_failed;
|
||
|
}
|
||
|
|
||
|
if (setjmp (png_jmpbuf (png_ptr))) {
|
||
|
goto png_failure;
|
||
|
}
|
||
|
|
||
|
/* Set image attributes. */
|
||
|
|
||
|
png_set_IHDR (png_ptr,
|
||
|
info_ptr,
|
||
|
320,
|
||
|
200,
|
||
|
depth,
|
||
|
format,
|
||
|
PNG_INTERLACE_NONE,
|
||
|
PNG_COMPRESSION_TYPE_DEFAULT,
|
||
|
PNG_FILTER_TYPE_DEFAULT);
|
||
|
|
||
|
if (format == PNG_COLOR_TYPE_GRAY )
|
||
|
bytePerPixel = depth / 8 * 1;
|
||
|
else
|
||
|
bytePerPixel = depth / 8 * 3;
|
||
|
|
||
|
row_pointers = (png_byte **)png_malloc (png_ptr, 200 * sizeof (png_byte *));
|
||
|
//for (y = vid.height-1; y >=0; --y)
|
||
|
for (y = 0; y < 200; y++)
|
||
|
{
|
||
|
row_pointers[y] = (png_byte*)&pixels[320 * (200 - y) * bytePerPixel];
|
||
|
}
|
||
|
|
||
|
png_init_io (png_ptr, fp);
|
||
|
png_set_rows (png_ptr, info_ptr, row_pointers);
|
||
|
png_write_png (png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
|
||
|
//png_read_image (png_ptr, info_ptr);//
|
||
|
|
||
|
status = 0;
|
||
|
|
||
|
png_free (png_ptr, row_pointers);
|
||
|
|
||
|
|
||
|
png_failure:
|
||
|
png_create_info_struct_failed:
|
||
|
png_destroy_write_struct (&png_ptr, &info_ptr);
|
||
|
|
||
|
png_create_write_struct_failed:
|
||
|
fclose (fp);
|
||
|
fopen_failed:
|
||
|
return status;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
int bgFrameBufferCounter = 0;
|
||
|
|
||
|
void video_dumpBackGroundBuffer()
|
||
|
{
|
||
|
if (v->_curPagePtr1 != v->_pagePtrs[0])
|
||
|
return;
|
||
|
|
||
|
uint8_t bgBuffer[320 * 200 * 3];
|
||
|
bgDumpFrameBuffer(v->_curPagePtr1, bgBuffer, 0, 0);
|
||
|
|
||
|
|
||
|
char path[256];
|
||
|
//sprintf(path,"%4d%s.png",traceFrameBufferCounter,comment);
|
||
|
sprintf(path, "bg%4d.png", bgFrameBufferCounter++);
|
||
|
|
||
|
bgSaveAsSpecifiedPNG(path, bgBuffer);
|
||
|
}
|
||
|
|
||
|
#endif
|