📄 gfxdecode.cpp
字号:
if (uFrameNum > frames-1) {
SetLastError(ERROR_INVALID_PARAMETER);
return NULL;
}
if (uYSize == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
return NULL;
}
// in case we want to know the rightmost pixels column (usable eg. for fonts)
if (uMaxX != NULL)
*uMaxX = 0;
// get the frame offsets
framestart = new uint32_t[frames+1];
for (i=0; i<frames+1; i++) {
memcpy(&framestart[i], pFileBuf+uFilePos, sizeof(uint32_t));
uFilePos += sizeof(uint32_t);
}
/****** block size *************************************************
* depends on the image width
******************************/
double erg = rint(sqrt(pow(2, rint(log((double)(framestart[uFrameNum+1] - framestart[uFrameNum])) / log(2.0)))));
pFrame = new FrameBuf(palette, uFrameNum, uXSize, uYSize, uMaxX, max((uint16_t)min((int)erg, 0x7F), uXSize));
/****** ramp detection -- AFAIK only needed for 32x32 tiles ********
* here I use hard coded constants because this is the only simple
* way to get the detection done; plus this stuff is only to be
* found in such 32x32 (tile) files and so wont hurt anyone ;)
******************************************************************/
uint32_t uFrameLen = framestart[uFrameNum+1] - framestart[uFrameNum];
if ((uXSize == 32) && ((uFrameLen == c_2RampSize) || (uFrameLen == c_1RampSize))) {
// use the static arrays for the check
for (i=0; i<(uFrameLen == c_2RampSize ? 16 : 8); i++) {
memcpy(&tmpWord, pFileBuf+framestart[uFrameNum]+c_RampOffsetLeft[i], sizeof(uint16_t));
if (tmpWord != 0)
break;
}
bool bRampsLeft = pFrame->bHasRamps = (i==(uFrameLen == c_2RampSize ? 16 : 8));
if (!pFrame->bHasRamps) { // only one can apply
for (i=0; i<(uFrameLen == c_2RampSize ? 16 : 8); i++) {
memcpy(&tmpWord, pFileBuf+framestart[uFrameNum]+c_RampOffsetRight[i], sizeof(uint16_t));
if (tmpWord != 0)
break;
}
pFrame->bHasRamps = (i==(uFrameLen == c_2RampSize ? 16 : 8)); // bRampsLeft stays false in this case
}
if (pFrame->bHasRamps) { // decode ramps and be off (if appropriate)
data = celDecodeRamps(pFileBuf, pFrame, framestart, bRampsLeft);
delete pFrame;
delete[] framestart;
return data;
}
}
/*********** block detection ***************************************
* 0x0A as start byte seems to be sufficient (though I still dunno
* what the trailing 10 bytes mean); in any other case we act as if
* blocks were to be used and check afterwards if the image looks
* OK (that is, the last line has no pixels in it)
******************************************************************/
cRead = pFileBuf[framestart[uFrameNum]];
if (cRead == 0x0A) // sufficient
pFrame->bHasBlocks = true;
// if width == 32 && framelen == 32*32, assume plain
else if ((uXSize != 32) || (uFrameLen != 32*32)) { // check needed
uFilePos=framestart[uFrameNum];
i=0;
// rush through the frame
while (uFilePos < framestart[uFrameNum+1]) {
cRead = pFileBuf[uFilePos++];
// transparency blocks
while (cRead > 0x7F) {
i += 256-cRead;
i %= uXSize;
if (uFilePos < framestart[uFrameNum+1])
cRead = pFileBuf[uFilePos++];
else
cRead = 0;
}
// colored pixel block
if (uFilePos < framestart[uFrameNum+1]) {
if (cRead < pFrame->uMaxBlock + 1) {
i+=cRead;
i%=uXSize;
uFilePos+=cRead;
} else {
// when the value is out of valid blockrange
i=1; // trigger error (1%uXSize != 0)
break;
}
}
}
if (i%uXSize == 0) // looks as if we got it right
pFrame->bHasBlocks=true;
}
if (pFrame->bHasBlocks) { // use block decoder if appropriate
data = celDecodeBlocks(pFileBuf, pFrame, framestart);
delete pFrame;
delete[] framestart;
return data;
}
// plain mode (#3), read each color index and write the pixel
uFilePos=framestart[uFrameNum];
while (uFilePos < framestart[uFrameNum+1])
pFrame->addPixel(pFileBuf[uFilePos++]);
// cleanup, return image data and height
data = pFrame->getData();
delete pFrame;
delete[] framestart;
return data;
}
uint16_t WINAPI cl2GetFrameCount(uint8_t *pFileBuf)
{
uint32_t tmp;
memcpy(&tmp, pFileBuf, sizeof(uint32_t));
memcpy(&tmp, pFileBuf+tmp, sizeof(uint32_t));
return (uint16_t)tmp;
}
/***** cl2GetDirData ***************************************************
* decodes all frames of a .cl2 for given direction and xsize
* Args:
* *pFileBuf the buffer containing the filecontent
* *palette the palette (4 bytes for each of the 257 entries)
* 256 colors are needed + 1 for alpha
* uXSize this information must be given
* uDirNum the direction to get the frames from
* *uYSize the actual height is returned herein
*
* Returns: <frames> arrays containing 4 Bytes (RGBA) for each pixel
* where <frames> is read at runtime and handed back via *uFrames
*
* ---------------------------------------------------------------
* Comments: dirty hack, started from scratch @ 2000-10-12
*
* The format basics are similar to .cel, with the main difference
* that the values read have reverse interpretation. In .cel a value
* greater than 0x7F means transparency, while in .cl2 this means
* color and vice-versa. .cl2 has the additional understanding
* of blocks of the same color (0x80 .. 0xBF) where the one color is
* written multiple times.
*
* .cl2 only uses the block scheme, so there is no detection
* necessary in order to get it right. The only thing still unknown
* is that 0x0A 0x00 stuff...
*
* TODO: learn what 0x0A 0x00 means
***********************************************************************/
BYTE ** WINAPI cl2GetDirData(BYTE *pFileBuf, BYTE *palette, WORD uXSize, WORD uDirNum, WORD *uYSize)
{
FrameBuf *pFrame;
uint32_t frames=0, *framestart=NULL, uFilePos=0;
uint16_t i, fc;
uint8_t cRead=0, **data=NULL;
if (pFileBuf == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
return NULL;
}
if (palette == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
return NULL;
}
if (uDirNum > 7) {
SetLastError(ERROR_INVALID_PARAMETER);
return NULL;
}
if (uYSize == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
return NULL;
}
// get direction offsets
uint32_t dirstart[8];
for (i=0; i<8; i++) {
memcpy(&dirstart[i], pFileBuf+uFilePos, sizeof(uint32_t));
uFilePos += sizeof(uint32_t);
}
uFilePos = dirstart[uDirNum];
memcpy(&frames, pFileBuf+uFilePos, sizeof(uint32_t));
uFilePos += sizeof(uint32_t);
data = new uint8_t*[frames];
// get frame offsets
framestart = new uint32_t[frames+1];
for (i=0; i<frames+1; i++) {
memcpy(&framestart[i], pFileBuf+uFilePos, sizeof(uint32_t));
uFilePos += sizeof(uint32_t);
}
// get frame data
for (fc=0; fc<frames; fc++) {
pFrame = new FrameBuf(palette, 0, uXSize, uYSize, NULL, 0);
uFilePos = dirstart[uDirNum] + framestart[fc];
while (uFilePos < dirstart[uDirNum] + framestart[fc+1]) {
cRead = pFileBuf[uFilePos++];
if (cRead < 0x80) { // transparency
// TODO: what is this 0x0A 0x00 stuff all about?
if ((cRead == 0x0A) && (pFileBuf[uFilePos] == 0) && (uFilePos == dirstart[uDirNum] + framestart[fc] + 1))
uFilePos += 9; // ignore the 9 bytes after 0x0A 0x00 at the framestart
else
for (i=0; i<cRead; i++)
pFrame->addPixel(TRANS_COL);
} else if (cRead < 0xC0) {
// read the next byte and write it <0xBF - cRead> times
for (i=0; i<0xBF - cRead; i++)
pFrame->addPixel(pFileBuf[uFilePos]);
++uFilePos;
} else // cRead > 0xBF
// read a block of the given size and write it
for (i=0; i < 256-cRead; i++)
pFrame->addPixel(pFileBuf[uFilePos++]);
}
// got the frame data, save it
data[fc] = pFrame->getData();
delete pFrame;
}
delete[] framestart;
return data;
}
/****** pcxGetData *****************************************************
* decodes pcx files (256 color, as in diablo mpq)
* Args:
* *pFileBuf the buffer containing the filecontent
* uFileSize the size of the file buffer
* uTransColor the palette entry to be transparent
* *uXSize the actual width is returned herein
* *uYSize the actual height is returned herein
*
* Returns: an array containing 4 Bytes (RGBA) for each pixel
*
* ---------------------------------------------------------------
* Comments: format info and pseudocode taken from:
* Klaus Holtorf, "Das Handbuch der Grafikformate"
* ISBN 3-7723-6393-8
***********************************************************************/
BYTE * WINAPI pcxGetData(BYTE *pFileBuf, DWORD uFileSize, BYTE uTransColor, WORD *uXSize, WORD *uYSize)
{
uint32_t uFilePos=0;
uint32_t uDataRead=0; // potentially big! (logo.pcx: 550 * 216 * 15 = 1,782,000)
uint16_t i=0;
uint8_t *data, *palette;
uint8_t uColorNum=0, uCount=0;
struct pcx_header_t {
uint8_t id;
uint8_t version;
uint8_t compressed;
uint8_t bpp;
uint16_t x0;
uint16_t y0;
uint16_t x1;
uint16_t y1;
uint16_t xdpi;
uint16_t ydpi;
uint8_t pal[16][3];
uint8_t reserved;
uint8_t layers;
uint16_t rowbytes;
uint16_t colortype;
uint8_t pad[58];
} pcxHeader;
if (pFileBuf == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
return NULL;
}
if (uXSize == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
return NULL;
}
if (uYSize == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
return NULL;
}
// get image information
memcpy(&pcxHeader, pFileBuf, sizeof(struct pcx_header_t));
*uXSize = (pcxHeader.x1 - pcxHeader.x0 + 1);
*uYSize = (pcxHeader.y1 - pcxHeader.y0 + 1);
if ((pcxHeader.version != 2) && (pcxHeader.version != 5)) {
cerr << "cannot handle pcx v" << pcxHeader.version << "\n";
return NULL;
}
// get palette
palette = new uint8_t[256*4];
if (pFileBuf[uFileSize - 768 - 1] != 0x0C) {
cerr << "palette error at " << uFileSize - 768 - 1 << "\n";
return NULL;
}
for (i=0; i<256; i++) {
memcpy(palette+i*4, pFileBuf+uFileSize-768+i*3, 3*sizeof(uint8_t));
palette[i*4+3] = 0xFF;
}
memset(palette+uTransColor*4, 0, 4*sizeof(uint8_t)); // transparent black
// start right after the header
uFilePos = sizeof(struct pcx_header_t);
data = new uint8_t[*uXSize * *uYSize * 4];
while (uDataRead < (uint32_t)(*uXSize * *uYSize)) {
// decompress
uColorNum = pFileBuf[uFilePos++];
if ((pcxHeader.compressed) && (uColorNum > 0xBF)) {
uCount = (uColorNum & 0x3F);
uColorNum = pFileBuf[uFilePos++];
} else
uCount = 1;
// draw count pixels with color val
for (i=0; i<uCount; i++)
memcpy(data+(uDataRead++)*4, palette+uColorNum*4, 4*sizeof(uint8_t));
}
// cleanup
delete[] palette;
return data;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -