Rearrange cell render code

This commit is contained in:
Garhoogin 2025-06-01 00:27:28 -05:00 committed by GitHub
parent 5f65aa38b1
commit e9aedce58d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 238 additions and 228 deletions

View File

@ -736,7 +736,7 @@ static void AnmViewerRenderGlyphListImage(NANRVIEWERDATA *data, int i) {
double rot = (frm.rotZ / 65536.0) * RAD_360DEG;
AnmViewerCalcTransformMatrix(0.0f, 0.0f, sx, sy, rot, (double) frm.px, (double) frm.py, &mtx[0][0], trans);
CellViewerRenderCell(data->cellRender, NULL, ncer, ncgr, nclr, frm.index, cell,
CellRender(data->cellRender, NULL, ncer, ncgr, nclr, frm.index, cell,
FloatToInt(trans[0]), FloatToInt(trans[1]),
mtx[0][0], mtx[0][1], mtx[1][0], mtx[1][1],
data->forceAffine, data->forceDoubleSize);
@ -1640,7 +1640,7 @@ static void AnmViewerRenderFrameFromCurrentSequence(NANRVIEWERDATA *data, COLOR3
double mtx[2][2] = { { 1.0, 0.0 }, { 0.0, 1.0 } }, trans[2] = { 0 };
AnmViewerCalcTransformMatrix(0.0, 0.0, sx, sy, rot, (double) frm.px, (double) frm.py, &mtx[0][0], trans);
CellViewerRenderCell(dest, NULL, ncer, ncgr, nclr, frm.index, cell,
CellRender(dest, NULL, ncer, ncgr, nclr, frm.index, cell,
FloatToInt(trans[0]), FloatToInt(trans[1]),
mtx[0][0], mtx[0][1], mtx[1][0], mtx[1][1],
data->forceAffine, data->forceDoubleSize);

View File

@ -863,6 +863,217 @@ int CellWriteFile(NCER *ncer, LPWSTR name) {
return ObjWriteFile(name, (OBJECT_HEADER *) ncer, (OBJECT_WRITER) CellWrite);
}
// ----- cell rendering
static int FloatToInt(double x) {
return (int) (x + (x < 0.0f ? -0.5f : 0.5f));
}
static void CellRenderOBJ(COLOR32 *out, NCER_CELL_INFO *info, NCGR *ncgr, NCLR *nclr, int mapping, CHAR_VRAM_TRANSFER *vramTransfer) {
int tilesX = info->width / 8;
int tilesY = info->height / 8;
if (ncgr == NULL) {
//null NCGR, render opaque coverage by OBJ
COLOR32 fill = 0xFF000000;
if (nclr != NULL && nclr->nColors >= 1) {
fill = 0xFF000000 | ColorConvertFromDS(nclr->colors[0]);
}
for (int i = 0; i < (tilesX * tilesY * 8 * 8); i++) out[i] = fill;
return;
}
int ncgrStart = NCGR_CHNAME(info->characterName, mapping, ncgr->nBits);
for (int y = 0; y < tilesY; y++) {
for (int x = 0; x < tilesX; x++) {
COLOR32 block[64];
int bitsOffset = x * 8 + (y * 8 * tilesX * 8);
int index;
if (NCGR_2D(mapping)) {
int ncx = x + ncgrStart % ncgr->tilesX;
int ncy = y + ncgrStart / ncgr->tilesX;
index = ncx + ncgr->tilesX * ncy;
} else {
index = ncgrStart + x + y * tilesX;
}
ChrRenderCharacterTransfer(ncgr, nclr, index, vramTransfer, block, info->palette, TRUE);
for (int i = 0; i < 8; i++) {
memcpy(out + bitsOffset + tilesX * 8 * i, block + i * 8, 32);
}
}
}
}
void CellRender(
COLOR32 *px,
int *covbuf,
NCER *ncer,
NCGR *ncgr,
NCLR *nclr,
int cellIndex,
NCER_CELL *cell,
int xOffs,
int yOffs,
double a,
double b,
double c,
double d,
int forceAffine,
int forceDoubleSize
) {
//adjust (X,Y) offset to center of preview
xOffs += 256;
yOffs += 128;
//get VRAM transfer entry
CHAR_VRAM_TRANSFER *vramTransfer = NULL;
if (ncer != NULL && ncer->vramTransfer != NULL && cellIndex != -1) vramTransfer = &ncer->vramTransfer[cellIndex];
//if cell is NULL, we use cell at cellInex.
if (cell == NULL) {
cell = &ncer->cells[cellIndex];
}
//compute inverse matrix parameters.
double invA = 1.0, invB = 0.0, invC = 0.0, invD = 1.0;
int isMtxIdentity = 1;
if (a != 1.0 || b != 0.0 || c != 0.0 || d != 1.0) {
//not identity matrix
double det = a * d - b * c; // DBCA
if (det != 0.0) {
invA = d / det;
invB = -b / det;
invC = -c / det;
invD = a / det;
} else {
//max scale identity
invA = 127.99609375;
invB = 0.0;
invC = 0.0;
invD = 127.99609375;
}
isMtxIdentity = 0; // not identity
}
COLOR32 *block = (COLOR32 *) calloc(64 * 64, sizeof(COLOR32));
for (int i = cell->nAttribs - 1; i >= 0; i--) {
NCER_CELL_INFO info;
CellDecodeOamAttributes(&info, cell, i);
//if OBJ is marked disabled, skip rendering
if (info.disable) continue;
CellRenderOBJ(block, &info, ncgr, nclr, ncer->mappingMode, vramTransfer);
//HV flip? Only if not affine!
if (!(info.rotateScale || forceAffine)) {
COLOR32 temp[64];
if (info.flipY) {
for (int i = 0; i < info.height / 2; i++) {
memcpy(temp, block + i * info.width, info.width * 4);
memcpy(block + i * info.width, block + (info.height - 1 - i) * info.width, info.width * 4);
memcpy(block + (info.height - 1 - i) * info.width, temp, info.width * 4);
}
}
if (info.flipX) {
for (int i = 0; i < info.width / 2; i++) {
for (int j = 0; j < info.height; j++) {
COLOR32 left = block[i + j * info.width];
block[i + j * info.width] = block[info.width - 1 - i + j * info.width];
block[info.width - 1 - i + j * info.width] = left;
}
}
}
}
int doubleSize = info.doubleSize;
if ((info.rotateScale || forceAffine) && forceDoubleSize) doubleSize = 1;
//apply transformation matrix to OBJ position
int x = SEXT9(info.x);
int y = SEXT8(info.y);
//when forcing double size on an OBJ that isn't naturally double size, we'll correct its position.
if ((forceDoubleSize && (info.rotateScale || forceAffine)) && !info.doubleSize) {
x -= info.width / 2;
y -= info.height / 2;
}
if (!isMtxIdentity) {
//adjust coordinates by correction for double-size
int realWidth = info.width << doubleSize;
int realHeight = info.height << doubleSize;
int movedX = x + realWidth / 2;
int movedY = y + realHeight / 2;
//un-correct moved position from center to top-left, un-correct for double-size
x = FloatToInt(movedX * a + movedY * b) - realWidth / 2;
y = FloatToInt(movedX * c + movedY * d) - realHeight / 2;
}
//copy data
if (!(info.rotateScale || forceAffine)) {
//adjust for double size
if (doubleSize) {
x += info.width / 2;
y += info.height / 2;
}
//no rotate/scale enabled, copy output directly.
for (int j = 0; j < info.height; j++) {
int _y = (y + j + yOffs) & 0xFF;
for (int k = 0; k < info.width; k++) {
int _x = (x + k + xOffs) & 0x1FF;
COLOR32 col = block[j * info.width + k];
if (col >> 24) {
px[_x + _y * 512] = col;
if (covbuf != NULL) covbuf[_x + _y * 512] = i + 1; // 0=no OBJ
}
}
}
} else {
//transform about center
int realWidth = info.width << doubleSize;
int realHeight = info.height << doubleSize;
double cx = (realWidth - 1) * 0.5; // rotation center X in OBJ
double cy = (realHeight - 1) * 0.5; // rotation center Y in OBJ
for (int j = 0; j < realHeight; j++) {
int destY = (y + j + yOffs) & 0xFF;
for (int k = 0; k < realWidth; k++) {
int destX = (x + k + xOffs) & 0x1FF;
int srcX = FloatToInt(((((double) k) - cx) * invA + (((double) j) - cy) * invB) + cx);
int srcY = FloatToInt(((((double) k) - cx) * invC + (((double) j) - cy) * invD) + cy);
//if double size, adjust source coordinate by the excess size
if (doubleSize) {
srcX -= realWidth / 4;
srcY -= realHeight / 4;
}
if (srcX >= 0 && srcY >= 0 && srcX < info.width && srcY < info.height) {
COLOR32 src = block[srcY * info.width + srcX];
if (src >> 24) {
px[destX + destY * 512] = src;
if (covbuf != NULL) covbuf[destX + destY * 512] = i + 1; // 0=no OBJ
}
}
}
}
}
}
free(block);
}
// ----- cell operations
void CellInsertOBJ(NCER_CELL *cell, int index, int nObj) {
int nMove = cell->nAttribs - index;

View File

@ -115,3 +115,25 @@ void CellMoveCellIndex(NCER *ncer, int iSrc, int iDst);
int CellWrite(NCER *ncer, BSTREAM *stream);
int CellWriteFile(NCER *ncer, LPWSTR name);
// ----- render cell
void CellRender(
COLOR32 *px, // output 512x256 pixel buffer
int *covbuf, // output coverage buffer (optional)
NCER *ncer, // cell data bank
NCGR *ncgr, // character graphics
NCLR *nclr, // color palette
int cellIndex, // cell index (required if cell is in the cell data bank)
NCER_CELL *cell, // cell data (required if not in the cell data bank)
int xOffs, // horizontal displacement of render
int yOffs, // vertical displacement of render
double a, // affine parameter A
double b, // affine parameter B
double c, // affine parameter C
double d, // affine parameter D
int forceAffine, // forces all OBJ to be in affine mode
int forceDoubleSize // forces all affine OBJ to be in double size mode
);

View File

@ -790,7 +790,7 @@ static void CellViewerCopyDIB(NCERVIEWERDATA *data) {
tmpCell->useEx2d = cell->useEx2d;
tmpCell->ex2dCharNames = exAttr;
CellViewerRenderCell(buf, NULL, &data->ncer, ncgr, nclr, data->cell, tmpCell, 0, 0, 1.0f, 0.0f, 0.0f, 1.0f, 0, 0);
CellRender(buf, NULL, &data->ncer, ncgr, nclr, data->cell, tmpCell, 0, 0, 1.0f, 0.0f, 0.0f, 1.0f, 0, 0);
free(tmpCell);
free(selAttr);
if (exAttr != NULL) free(exAttr);
@ -974,214 +974,8 @@ static void CellViewerPaste(NCERVIEWERDATA *data) {
// ----- rendering helper routines
static void CellViewerRenderObj(COLOR32 *out, NCER_CELL_INFO *info, NCGR *ncgr, NCLR *nclr, int mapping, CHAR_VRAM_TRANSFER *vramTransfer) {
int tilesX = info->width / 8;
int tilesY = info->height / 8;
if (ncgr == NULL) {
//null NCGR, render opaque coverage by OBJ
COLOR32 fill = 0xFF000000;
if (nclr != NULL && nclr->nColors >= 1) {
fill = 0xFF000000 | ColorConvertFromDS(nclr->colors[0]);
}
for (int i = 0; i < (tilesX * tilesY * 8 * 8); i++) out[i] = fill;
return;
}
int ncgrStart = NCGR_CHNAME(info->characterName, mapping, ncgr->nBits);
for (int y = 0; y < tilesY; y++) {
for (int x = 0; x < tilesX; x++) {
COLOR32 block[64];
int bitsOffset = x * 8 + (y * 8 * tilesX * 8);
int index;
if (NCGR_2D(mapping)) {
int ncx = x + ncgrStart % ncgr->tilesX;
int ncy = y + ncgrStart / ncgr->tilesX;
index = ncx + ncgr->tilesX * ncy;
} else {
index = ncgrStart + x + y * tilesX;
}
ChrRenderCharacterTransfer(ncgr, nclr, index, vramTransfer, block, info->palette, TRUE);
for (int i = 0; i < 8; i++) {
memcpy(out + bitsOffset + tilesX * 8 * i, block + i * 8, 32);
}
}
}
}
static int FloatToInt(double x) {
return (int) (x + (x < 0.0f ? -0.5f : 0.5f));
}
void CellViewerRenderCell(
COLOR32 *px,
int *covbuf,
NCER *ncer,
NCGR *ncgr,
NCLR *nclr,
int cellIndex,
NCER_CELL *cell,
int xOffs,
int yOffs,
double a,
double b,
double c,
double d,
int forceAffine,
int forceDoubleSize
) {
//adjust (X,Y) offset to center of preview
xOffs += 256;
yOffs += 128;
//get VRAM transfer entry
CHAR_VRAM_TRANSFER *vramTransfer = NULL;
if (ncer != NULL && ncer->vramTransfer != NULL && cellIndex != -1) vramTransfer = &ncer->vramTransfer[cellIndex];
//if cell is NULL, we use cell at cellInex.
if (cell == NULL) {
cell = &ncer->cells[cellIndex];
}
//compute inverse matrix parameters.
double invA = 1.0, invB = 0.0, invC = 0.0, invD = 1.0;
int isMtxIdentity = 1;
if (a != 1.0 || b != 0.0 || c != 0.0 || d != 1.0) {
//not identity matrix
double det = a * d - b * c; // DBCA
if (det != 0.0) {
invA = d / det;
invB = -b / det;
invC = -c / det;
invD = a / det;
} else {
//max scale identity
invA = 127.99609375;
invB = 0.0;
invC = 0.0;
invD = 127.99609375;
}
isMtxIdentity = 0; // not identity
}
COLOR32 *block = (COLOR32 *) calloc(64 * 64, sizeof(COLOR32));
for (int i = cell->nAttribs - 1; i >= 0; i--) {
NCER_CELL_INFO info;
CellDecodeOamAttributes(&info, cell, i);
//if OBJ is marked disabled, skip rendering
if (info.disable) continue;
CellViewerRenderObj(block, &info, ncgr, nclr, ncer->mappingMode, vramTransfer);
//HV flip? Only if not affine!
if (!(info.rotateScale || forceAffine)) {
COLOR32 temp[64];
if (info.flipY) {
for (int i = 0; i < info.height / 2; i++) {
memcpy(temp, block + i * info.width, info.width * 4);
memcpy(block + i * info.width, block + (info.height - 1 - i) * info.width, info.width * 4);
memcpy(block + (info.height - 1 - i) * info.width, temp, info.width * 4);
}
}
if (info.flipX) {
for (int i = 0; i < info.width / 2; i++) {
for (int j = 0; j < info.height; j++) {
COLOR32 left = block[i + j * info.width];
block[i + j * info.width] = block[info.width - 1 - i + j * info.width];
block[info.width - 1 - i + j * info.width] = left;
}
}
}
}
int doubleSize = info.doubleSize;
if ((info.rotateScale || forceAffine) && forceDoubleSize) doubleSize = 1;
//apply transformation matrix to OBJ position
int x = SEXT9(info.x);
int y = SEXT8(info.y);
//when forcing double size on an OBJ that isn't naturally double size, we'll correct its position.
if ((forceDoubleSize && (info.rotateScale || forceAffine)) && !info.doubleSize) {
x -= info.width / 2;
y -= info.height / 2;
}
if (!isMtxIdentity) {
//adjust coordinates by correction for double-size
int realWidth = info.width << doubleSize;
int realHeight = info.height << doubleSize;
int movedX = x + realWidth / 2;
int movedY = y + realHeight / 2;
//un-correct moved position from center to top-left, un-correct for double-size
x = FloatToInt(movedX * a + movedY * b) - realWidth / 2;
y = FloatToInt(movedX * c + movedY * d) - realHeight / 2;
}
//copy data
if (!(info.rotateScale || forceAffine)) {
//adjust for double size
if (doubleSize) {
x += info.width / 2;
y += info.height / 2;
}
//no rotate/scale enabled, copy output directly.
for (int j = 0; j < info.height; j++) {
int _y = (y + j + yOffs) & 0xFF;
for (int k = 0; k < info.width; k++) {
int _x = (x + k + xOffs) & 0x1FF;
COLOR32 col = block[j * info.width + k];
if (col >> 24) {
px[_x + _y * 512] = col;
if (covbuf != NULL) covbuf[_x + _y * 512] = i + 1; // 0=no OBJ
}
}
}
} else {
//transform about center
int realWidth = info.width << doubleSize;
int realHeight = info.height << doubleSize;
double cx = (realWidth - 1) * 0.5; // rotation center X in OBJ
double cy = (realHeight - 1) * 0.5; // rotation center Y in OBJ
for (int j = 0; j < realHeight; j++) {
int destY = (y + j + yOffs) & 0xFF;
for (int k = 0; k < realWidth; k++) {
int destX = (x + k + xOffs) & 0x1FF;
int srcX = FloatToInt(((((double) k) - cx) * invA + (((double) j) - cy) * invB) + cx);
int srcY = FloatToInt(((((double) k) - cx) * invC + (((double) j) - cy) * invD) + cy);
//if double size, adjust source coordinate by the excess size
if (doubleSize) {
srcX -= realWidth / 4;
srcY -= realHeight / 4;
}
if (srcX >= 0 && srcY >= 0 && srcX < info.width && srcY < info.height) {
COLOR32 src = block[srcY * info.width + srcX];
if (src >> 24) {
px[destX + destY * 512] = src;
if (covbuf != NULL) covbuf[destX + destY * 512] = i + 1; // 0=no OBJ
}
}
}
}
}
}
free(block);
}
static void CellViewerRenderCellByIndex(COLOR32 *buf, int *covbuf, NCER *ncer, NCGR *ncgr, NCLR *nclr, int cellno) {
CellViewerRenderCell(buf, covbuf, ncer, ncgr, nclr, cellno, NULL, 0, 0, 1.0f, 0.0f, 0.0f, 1.0f, 0, 0);
CellRender(buf, covbuf, ncer, ncgr, nclr, cellno, NULL, 0, 0, 1.0f, 0.0f, 0.0f, 1.0f, 0, 0);
}
static void CellViewerUpdateCellRender(NCERVIEWERDATA *data) {

View File

@ -90,22 +90,4 @@ HWND CreateNcerViewerImmediate(int x, int y, int width, int height, HWND hWndPar
void CellViewerRenderGridlines(FrameBuffer *fb, int scale, int scrollX, int scrollY);
void CellViewerRenderCell(
COLOR32 *px, // output 512x256 pixel buffer
int *covbuf, // output coverage buffer (optional)
NCER *ncer, // cell data bank
NCGR *ncgr, // character graphics
NCLR *nclr, // color palette
int cellIndex, // cell index (required if cell is in the cell data bank)
NCER_CELL *cell, // cell data (required if not in the cell data bank)
int xOffs, // horizontal displacement of render
int yOffs, // vertical displacement of render
double a, // affine parameter A
double b, // affine parameter B
double c, // affine parameter C
double d, // affine parameter D
int forceAffine, // forces all OBJ to be in affine mode
int forceDoubleSize // forces all affine OBJ to be in double size mode
);
COLOR32 *CellViewerCropRenderedCell(COLOR32 *px, int width, int height, int *pMinX, int *pMinY, int *outWidth, int *outHeight);

View File

@ -31,6 +31,7 @@ HBITMAP RenderNmcrFrame(NMCR *nmcr, NCLR *nclr, NCGR *ncgr, NCER *ncer, NANR *na
int seqId = entry->sequenceNumber;
//nanrDrawFrame(px, nclr, ncgr, ncer, nanr, seqId, frame, 0, x, y);
}
}