[7721] Fixed possible source crash at rogue stealth lost in some special cases.
[getmangos.git] / contrib / vmap_extractor_v2 / stormlib / GfxDecode.cpp
blob665e5d38e49c7626996b861aa2ade3fe7209ff47
1 /***********************************************************************
3 * Description: GfxDecode -- functions for reading Diablo's GFX files
4 * Author: Marko Friedemann <marko.friedemann@bmx-chemnitz.de>
5 * Created at: Son Jan 27 15:20:43 CET 2002
6 * Computer: hangloose.flachland-chemnitz.de
7 * System: Linux 2.4.16 on i686
8 *
9 * Copyright (c) 2002 BMX-Chemnitz.DE All rights reserved.
11 * ---------------------------------------------------------------------
12 * included are functions for getting:
13 * - the framecount of .CEL-files -> celGetFrameCount()
14 * - single frames of .CEL-files -> celGetFrameData()
15 * - the framecount of .CL2-files -> cl2GetFrameCount()
16 * - single directions of .CL2-files (all frames) -> cl2GetDirData()
17 * - single .PCX-files (256 color; v2, v5) -> pcxGetData()
18 ***********************************************************************/
20 #include <vector>
21 #include <cmath>
22 #include <iostream>
24 #include "StormLib.h"
26 #define TRANS_COL 256
28 using std::cerr;
29 using std::vector;
31 /****** RAMP stuff *****************************************************
32 * for a more detailed description/explanation see below
33 ***********************************************************************/
34 // two variations: one/two ramp(s)
35 static const uint16_t c_2RampSize = 544; // the frame size
36 static const uint16_t c_1RampSize = 800; // the frame size
38 // ramps (both variations) can be either left or right
39 static const uint16_t c_RampOffsetLeft[17] = {
40 0, // __
41 8, // + 8 note that this __--
42 24, // + 16 "drawing" is __--
43 48, // + 24 upside down! __-- this area
44 80, // + 32 __-- is always
45 120, // + 40 __-- colored
46 168, // + 48 __--
47 224, // + 56 __-- lower ramp ends here (+30 == 254)
48 288, // + 64 --__ upper ramp might be missing
49 348, // + 60 | --__
50 400, // + 52 | --__ this area
51 444, // + 44 | --__ is always
52 480, // + 36 | --__ colored
53 508, // + 28 | either trans- --__
54 528, // + 20 | parent or colored --__
55 540, // + 12 | --__ +2 Pixels = 544
56 542 // + 2 | this last one doesn't exist, it's those^ 2 pixels
59 static const uint16_t c_RampOffsetRight[17] = {
60 2, // __ before this, there are 2 Pixels
61 14, // + 12 --__ 4^2 - 2
62 34, // + 20 --__ 6^2 - 2
63 62, // + 28 this area --__ 8^2 - 2
64 98, // + 36 is always --__ 10^2 - 2 pattern anyone? ;)
65 142, // + 44 colored --__
66 194, // + 52 --__
67 254, // + 60 lower ramp ends here --__ (-30 == 224)
68 318, // + 64 upper ramp might be missing __--
69 374, // + 56 __-- |
70 422, // + 48 this area __-- | note that this
71 462, // + 40 is always __-- | "drawing"
72 494, // + 32 colored __-- eiter trans- | is upside down!
73 518, // + 24 __-- parent or colored |
74 534, // + 16 __-- |
75 542, // + 8 __-- +2 Startpixels = 544 |
76 542 // + 0 this last one doesn't exist, it | would be EOF
79 /****** FrameBuffer class **********************************************
80 * holds buffers and size information of the actual target image
81 * purpose: buffer management and avoidance of ugly globals
82 **********************************************************************/
83 class FrameBuf
85 protected:
86 vector <uint8_t *> vecData;
87 uint8_t *pCurrRow;
88 uint8_t *pPalette;
89 uint16_t uRows;
90 uint16_t *upYSize;
91 uint16_t *upMaxX;
92 public:
93 uint16_t uCols;
94 uint16_t uXSize;
95 uint16_t uMaxBlock;
96 uint16_t uFrameNum;
97 bool bHasBlocks;
98 bool bHasRamps;
99 FrameBuf(
100 uint8_t *pPal=NULL, uint16_t frame=0, uint16_t xsize=0,
101 uint16_t *pysize=NULL, uint16_t *pmaxx=NULL, uint16_t maxblock=0)
103 pCurrRow = new uint8_t[4*xsize];
104 pPalette = pPal;
105 uCols = 0;
106 uRows = 0;
107 uXSize = xsize;
108 uFrameNum = frame;
109 uMaxBlock = maxblock;
110 upYSize = pysize;
111 upMaxX = pmaxx;
112 bHasBlocks= false;
113 bHasRamps = false;
115 ~FrameBuf()
117 delete[] pCurrRow;
118 for (vector <uint8_t *>::iterator vi=vecData.begin(); vi!=vecData.end(); vi++)
119 delete[] *vi;
120 vecData.clear();
122 void addLine()
124 ++uRows;
125 uCols = 0;
126 vecData.push_back(pCurrRow);
127 pCurrRow = new uint8_t[4*uXSize];
129 void addPixel(uint16_t uColorNum)
131 if (uColorNum > TRANS_COL) {
132 cerr << "\n*** there seemed to be an error, illegal color index " << uColorNum;
133 cerr << "\n +++ at (" << uCols << "," << uRows << "), see for yourself *** \n\n";
134 uColorNum = TRANS_COL; // sane setting to avoid segfaults
137 memcpy(pCurrRow + 4*uCols, pPalette + 4*uColorNum, 4*sizeof(uint8_t));
138 if (++uCols == uXSize)
139 addLine();
140 else if ((uColorNum != TRANS_COL) && (upMaxX != NULL) && (uCols > *upMaxX))
141 *upMaxX = uCols;
143 // used to return the actual image data
144 uint8_t *getData()
146 uint16_t i;
147 vector <uint8_t *>::reverse_iterator vri;
148 // allocate a buffer to hold the actual image size
149 uint8_t *tmp = new uint8_t[4*uXSize*uRows];
151 // the lines are upside down inside the vector, use reverse iterator
152 for (i=0, vri=vecData.rbegin(); vri!=vecData.rend(); vri++, i++)
153 memcpy(tmp+4*uXSize*i, *vri, 4*uXSize*sizeof(uint8_t));
155 *upYSize = uRows; // set height
157 if (uCols > 0) {
158 cerr << "\n*** there seemed to be an error (last line does not match boundary, " << uCols << " pixels left)";
159 cerr << "\n +++ this is often caused by specifying an invalid width, see for yourself *** \n\n";
162 return tmp;
166 uint16_t WINAPI celGetFrameCount(uint8_t *pFileBuf)
168 uint32_t tmp;
169 memcpy(&tmp, pFileBuf, sizeof(uint32_t));
170 return (uint16_t)tmp;
173 /***** Block Decoder ***************************************************
174 * one of three possible decoding techniques necessary for .cel
175 * possible block contents are either colored (in that case the
176 * appropriate number of pixels are read) or transparent pixels
177 * there are neither ramps nor plain pixels allowed here
178 ***********************************************************************/
179 uint8_t *celDecodeBlocks(uint8_t *pFileBuf, FrameBuf *pFrame, uint32_t *framestart)
181 uint32_t uFilePos=framestart[pFrame->uFrameNum];
182 uint8_t cRead=0, i=0;
184 if (!pFrame->bHasBlocks) // sanity check
185 return NULL;
187 while (uFilePos < framestart[pFrame->uFrameNum+1]) {
188 cRead = pFileBuf[uFilePos++];
190 if ((uFilePos == framestart[pFrame->uFrameNum]+1))
191 // TODO: what is this 0x0A 0x00 stuff all about?
192 if ((cRead == 0x0A) && (pFileBuf[uFilePos] == 0x00)) {
193 uFilePos += 9;
194 cRead = pFileBuf[uFilePos++];
197 if (cRead > 0x7F)
198 // transparent block (complement, 256-val)
199 for (i=0; i<256-cRead; i++)
200 pFrame->addPixel(TRANS_COL);
201 else if (cRead < pFrame->uMaxBlock + 1)
202 // pixel block (block size pixels to be read!)
203 for (i=0; i<cRead; i++)
204 pFrame->addPixel(pFileBuf[uFilePos++]);
205 else
206 cerr << "\n*** block mode: illegal block (> max_size) ***\n\n";
208 return pFrame->getData();
211 /***** Ramp Decoder ****************************************************
212 * the second of three possible decoding techniques necessary for .cel
213 * each block save the first/last is enclosed by two 0x00 pairs
214 * those blocks affect _TWO_ rows with one being 2 colored pixels shorter
215 * the first/last "block" affects only one row
216 ***********************************************************************/
217 uint8_t *celDecodeRamps(uint8_t *pFileBuf, FrameBuf *pFrame, uint32_t *framestart, bool bLeft)
219 uint32_t uFrameLen = framestart[pFrame->uFrameNum+1]-framestart[pFrame->uFrameNum];
220 uint32_t uFilePos=0;
221 uint16_t uBlockLen=0, i=0, j=0;
222 bool bFirstLonger=false;
224 if (!pFrame->bHasRamps) // sanity check
225 return NULL;
227 if (pFrame->uXSize != 32) // only used in that case
228 return NULL;
230 if (!bLeft) { // get first two pixels for right side ramps
231 pFrame->addPixel(pFileBuf[framestart[pFrame->uFrameNum]]);
232 pFrame->addPixel(pFileBuf[framestart[pFrame->uFrameNum]+1]);
235 // do all the ramp blocks
236 for (i=0; i<(uFrameLen == c_2RampSize ? 15 : 7); i++) {
237 uBlockLen = (bLeft ? (c_RampOffsetLeft[i+1] - c_RampOffsetLeft[i]) : (c_RampOffsetRight[i+1] - c_RampOffsetRight[i]));
238 uFilePos = framestart[pFrame->uFrameNum] + (bLeft ? c_RampOffsetLeft[i] : c_RampOffsetRight[i]) + 2;
239 bFirstLonger = (i>(bLeft ? 7 : 6));
240 if (bLeft) {
241 // OK, first line, starting with transparency
242 for (j=0; j<pFrame->uXSize - uBlockLen/2 + (bFirstLonger ? 0 : 2); j++)
243 pFrame->addPixel(TRANS_COL);
244 // fill it up with the pixel block
245 for (j=0; j<uBlockLen/2 - (bFirstLonger ? 0 : 2); j++)
246 pFrame->addPixel(pFileBuf[uFilePos++]);
247 // second line, starting again with transparency
248 for (j=0; j<pFrame->uXSize - uBlockLen/2 + (bFirstLonger ? 2 : 0); j++)
249 pFrame->addPixel(TRANS_COL);
250 // fill the second line with the remaining pixels
251 for (j=0; j<uBlockLen/2 - (bFirstLonger ? 2 : 0); j++)
252 pFrame->addPixel(pFileBuf[uFilePos++]);
253 } else {
254 if (pFrame->uCols != 0) // fill current line with trans (if not empty)
255 for (j=pFrame->uXSize - pFrame->uCols; j>0; j--)
256 pFrame->addPixel(TRANS_COL);
257 // OK, insert the first pixels into a new line
258 for (j=0; j<uBlockLen/2 - (bFirstLonger ? 0 : 2); j++)
259 pFrame->addPixel(pFileBuf[uFilePos++]);
260 // fill the line with transparency
261 for (j=0; j<pFrame->uXSize - uBlockLen/2 + (bFirstLonger ? 0 : 2); j++)
262 pFrame->addPixel(TRANS_COL);
263 // start a second line with the remaining pixels
264 for (j=0; j<uBlockLen/2 - (bFirstLonger ? 2 : 0); j++)
265 pFrame->addPixel(pFileBuf[uFilePos++]);
269 // now read the last 0x00 pair and fill up
270 uBlockLen = (uFrameLen == c_2RampSize ? 30 : 2); // one or two ramps?
271 uFilePos = framestart[pFrame->uFrameNum] + (bLeft ? c_RampOffsetLeft[i] : c_RampOffsetRight[i]) + 2;
272 // the transparency for the last (single) 0x00 pair
273 for (j=0; j<uBlockLen; j++)
274 pFrame->addPixel(TRANS_COL);
275 if (bLeft) { // left side only: the remaining line
276 for (j=0; j<pFrame->uXSize - uBlockLen; j++)
277 pFrame->addPixel(pFileBuf[uFilePos++]);
280 // now the rest of the file (plain)
281 while (uFilePos < framestart[pFrame->uFrameNum+1])
282 pFrame->addPixel(pFileBuf[uFilePos++]);
284 // the uppermost line is emtpy when 2 ramps are used
285 if (uFrameLen == c_2RampSize)
286 for (j=0; j<pFrame->uXSize; j++)
287 pFrame->addPixel(TRANS_COL);
289 return pFrame->getData();
292 /***** celGetFrameData *************************************************
293 * decode .cel data for given frame and xsize
294 * Args:
295 * *vpFileBuf the buffer containing the filecontent
296 * *palette the palette (4 bytes for each of the 257 entries)
297 * 256 colors are needed + 1 for alpha
298 * uXSize this information must be given
299 * uFrameNume the frame to get
300 * *uYSize the actual value is returned therein
301 * *uMaxX this can be used (if != NULL) to get the column
302 * of the rightmost nontransparent pixel (useable
303 * eg for fonts)
305 * Returns: an array containing 4 Bytes (RGBA) for each pixel
307 * ---------------------------------------------------------------
308 * Comments: dirty hack, started from scratch @ 2000-10-11
309 * cleanly rewritten during incorporation into ladiks StormLib
310 * status: structured hack ;)
312 * It took me approx. 6 days to understand the format basics (hex viewer)
313 * For this I had a little help from a dos tool ("ddecode", from
314 * www.cowlevel.com, binary only, no sources) which, however, gave
315 * me the general idea what the pictures are actually supposed to look like.
317 * The fine adjustments, however, took quite some time and a little luck.
318 * After I had written to various people (mickyk and ladik), which could
319 * not help me, but wished best luck (thanks, btw, it helped ;)), I tried
320 * some reverse engineering which was not succesful in the end.
322 * I then had incidentally a new idea of what could be going on @ 2002-01-23.
323 * It just came to my mind that I could retry some actual painting in
324 * reverse order (had done that before to no avail) and when looking closer
325 * at it I realized the "ramp" stuff. This really is the trickiest part and
326 * it took me some eight days to implement it without breaking the other
327 * parts of the code. Very odd format indeed.
329 * TODO: learn what 0x0A 0x00 means
330 **********************************************************************/
331 uint8_t * WINAPI celGetFrameData(uint8_t *pFileBuf, uint8_t *palette, uint16_t uXSize, uint16_t uFrameNum, uint16_t *uYSize, uint16_t *uMaxX)
333 FrameBuf *pFrame;
334 uint32_t *framestart=NULL, frames=0, uFilePos=0;
335 uint16_t i, tmpWord=0;
336 uint8_t cRead=0, *data;
338 memcpy(&frames, pFileBuf, sizeof(uint32_t));
339 uFilePos += sizeof(uint32_t);
341 if (pFileBuf == NULL) {
342 SetLastError(ERROR_INVALID_PARAMETER);
343 return NULL;
346 if (palette == NULL) {
347 SetLastError(ERROR_INVALID_PARAMETER);
348 return NULL;
351 if (uFrameNum > frames-1) {
352 SetLastError(ERROR_INVALID_PARAMETER);
353 return NULL;
356 if (uYSize == NULL) {
357 SetLastError(ERROR_INVALID_PARAMETER);
358 return NULL;
361 // in case we want to know the rightmost pixels column (usable eg. for fonts)
362 if (uMaxX != NULL)
363 *uMaxX = 0;
365 // get the frame offsets
366 framestart = new uint32_t[frames+1];
367 for (i=0; i<frames+1; i++) {
368 memcpy(&framestart[i], pFileBuf+uFilePos, sizeof(uint32_t));
369 uFilePos += sizeof(uint32_t);
372 /****** block size *************************************************
373 * depends on the image width
374 ******************************/
376 double erg = rint(sqrt(pow(2, rint(log((double)(framestart[uFrameNum+1] - framestart[uFrameNum])) / log(2.0)))));
377 pFrame = new FrameBuf(palette, uFrameNum, uXSize, uYSize, uMaxX, max((uint16_t)min((int)erg, 0x7F), uXSize));
379 /****** ramp detection -- AFAIK only needed for 32x32 tiles ********
380 * here I use hard coded constants because this is the only simple
381 * way to get the detection done; plus this stuff is only to be
382 * found in such 32x32 (tile) files and so wont hurt anyone ;)
383 ******************************************************************/
385 uint32_t uFrameLen = framestart[uFrameNum+1] - framestart[uFrameNum];
386 if ((uXSize == 32) && ((uFrameLen == c_2RampSize) || (uFrameLen == c_1RampSize))) {
388 // use the static arrays for the check
389 for (i=0; i<(uFrameLen == c_2RampSize ? 16 : 8); i++) {
390 memcpy(&tmpWord, pFileBuf+framestart[uFrameNum]+c_RampOffsetLeft[i], sizeof(uint16_t));
391 if (tmpWord != 0)
392 break;
394 bool bRampsLeft = pFrame->bHasRamps = (i==(uFrameLen == c_2RampSize ? 16 : 8));
395 if (!pFrame->bHasRamps) { // only one can apply
396 for (i=0; i<(uFrameLen == c_2RampSize ? 16 : 8); i++) {
397 memcpy(&tmpWord, pFileBuf+framestart[uFrameNum]+c_RampOffsetRight[i], sizeof(uint16_t));
398 if (tmpWord != 0)
399 break;
401 pFrame->bHasRamps = (i==(uFrameLen == c_2RampSize ? 16 : 8)); // bRampsLeft stays false in this case
404 if (pFrame->bHasRamps) { // decode ramps and be off (if appropriate)
405 data = celDecodeRamps(pFileBuf, pFrame, framestart, bRampsLeft);
406 delete pFrame;
407 delete[] framestart;
408 return data;
412 /*********** block detection ***************************************
413 * 0x0A as start byte seems to be sufficient (though I still dunno
414 * what the trailing 10 bytes mean); in any other case we act as if
415 * blocks were to be used and check afterwards if the image looks
416 * OK (that is, the last line has no pixels in it)
417 ******************************************************************/
419 cRead = pFileBuf[framestart[uFrameNum]];
420 if (cRead == 0x0A) // sufficient
421 pFrame->bHasBlocks = true;
422 // if width == 32 && framelen == 32*32, assume plain
423 else if ((uXSize != 32) || (uFrameLen != 32*32)) { // check needed
424 uFilePos=framestart[uFrameNum];
425 i=0;
426 // rush through the frame
427 while (uFilePos < framestart[uFrameNum+1]) {
428 cRead = pFileBuf[uFilePos++];
430 // transparency blocks
431 while (cRead > 0x7F) {
432 i += 256-cRead;
433 i %= uXSize;
434 if (uFilePos < framestart[uFrameNum+1])
435 cRead = pFileBuf[uFilePos++];
436 else
437 cRead = 0;
440 // colored pixel block
441 if (uFilePos < framestart[uFrameNum+1]) {
442 if (cRead < pFrame->uMaxBlock + 1) {
443 i+=cRead;
444 i%=uXSize;
445 uFilePos+=cRead;
446 } else {
447 // when the value is out of valid blockrange
448 i=1; // trigger error (1%uXSize != 0)
449 break;
453 if (i%uXSize == 0) // looks as if we got it right
454 pFrame->bHasBlocks=true;
457 if (pFrame->bHasBlocks) { // use block decoder if appropriate
458 data = celDecodeBlocks(pFileBuf, pFrame, framestart);
459 delete pFrame;
460 delete[] framestart;
461 return data;
464 // plain mode (#3), read each color index and write the pixel
465 uFilePos=framestart[uFrameNum];
466 while (uFilePos < framestart[uFrameNum+1])
467 pFrame->addPixel(pFileBuf[uFilePos++]);
469 // cleanup, return image data and height
470 data = pFrame->getData();
471 delete pFrame;
472 delete[] framestart;
473 return data;
476 uint16_t WINAPI cl2GetFrameCount(uint8_t *pFileBuf)
478 uint32_t tmp;
479 memcpy(&tmp, pFileBuf, sizeof(uint32_t));
480 memcpy(&tmp, pFileBuf+tmp, sizeof(uint32_t));
481 return (uint16_t)tmp;
484 /***** cl2GetDirData ***************************************************
485 * decodes all frames of a .cl2 for given direction and xsize
486 * Args:
487 * *pFileBuf the buffer containing the filecontent
488 * *palette the palette (4 bytes for each of the 257 entries)
489 * 256 colors are needed + 1 for alpha
490 * uXSize this information must be given
491 * uDirNum the direction to get the frames from
492 * *uYSize the actual height is returned herein
494 * Returns: <frames> arrays containing 4 Bytes (RGBA) for each pixel
495 * where <frames> is read at runtime and handed back via *uFrames
497 * ---------------------------------------------------------------
498 * Comments: dirty hack, started from scratch @ 2000-10-12
500 * The format basics are similar to .cel, with the main difference
501 * that the values read have reverse interpretation. In .cel a value
502 * greater than 0x7F means transparency, while in .cl2 this means
503 * color and vice-versa. .cl2 has the additional understanding
504 * of blocks of the same color (0x80 .. 0xBF) where the one color is
505 * written multiple times.
507 * .cl2 only uses the block scheme, so there is no detection
508 * necessary in order to get it right. The only thing still unknown
509 * is that 0x0A 0x00 stuff...
511 * TODO: learn what 0x0A 0x00 means
512 ***********************************************************************/
513 BYTE ** WINAPI cl2GetDirData(BYTE *pFileBuf, BYTE *palette, WORD uXSize, WORD uDirNum, WORD *uYSize)
515 FrameBuf *pFrame;
516 uint32_t frames=0, *framestart=NULL, uFilePos=0;
517 uint16_t i, fc;
518 uint8_t cRead=0, **data=NULL;
520 if (pFileBuf == NULL) {
521 SetLastError(ERROR_INVALID_PARAMETER);
522 return NULL;
525 if (palette == NULL) {
526 SetLastError(ERROR_INVALID_PARAMETER);
527 return NULL;
530 if (uDirNum > 7) {
531 SetLastError(ERROR_INVALID_PARAMETER);
532 return NULL;
535 if (uYSize == NULL) {
536 SetLastError(ERROR_INVALID_PARAMETER);
537 return NULL;
540 // get direction offsets
541 uint32_t dirstart[8];
542 for (i=0; i<8; i++) {
543 memcpy(&dirstart[i], pFileBuf+uFilePos, sizeof(uint32_t));
544 uFilePos += sizeof(uint32_t);
547 uFilePos = dirstart[uDirNum];
549 memcpy(&frames, pFileBuf+uFilePos, sizeof(uint32_t));
550 uFilePos += sizeof(uint32_t);
552 data = new uint8_t*[frames];
554 // get frame offsets
555 framestart = new uint32_t[frames+1];
556 for (i=0; i<frames+1; i++) {
557 memcpy(&framestart[i], pFileBuf+uFilePos, sizeof(uint32_t));
558 uFilePos += sizeof(uint32_t);
561 // get frame data
562 for (fc=0; fc<frames; fc++) {
563 pFrame = new FrameBuf(palette, 0, uXSize, uYSize, NULL, 0);
565 uFilePos = dirstart[uDirNum] + framestart[fc];
566 while (uFilePos < dirstart[uDirNum] + framestart[fc+1]) {
568 cRead = pFileBuf[uFilePos++];
569 if (cRead < 0x80) { // transparency
570 // TODO: what is this 0x0A 0x00 stuff all about?
571 if ((cRead == 0x0A) && (pFileBuf[uFilePos] == 0) && (uFilePos == dirstart[uDirNum] + framestart[fc] + 1))
572 uFilePos += 9; // ignore the 9 bytes after 0x0A 0x00 at the framestart
573 else
574 for (i=0; i<cRead; i++)
575 pFrame->addPixel(TRANS_COL);
576 } else if (cRead < 0xC0) {
577 // read the next byte and write it <0xBF - cRead> times
578 for (i=0; i<0xBF - cRead; i++)
579 pFrame->addPixel(pFileBuf[uFilePos]);
580 ++uFilePos;
581 } else // cRead > 0xBF
582 // read a block of the given size and write it
583 for (i=0; i < 256-cRead; i++)
584 pFrame->addPixel(pFileBuf[uFilePos++]);
587 // got the frame data, save it
588 data[fc] = pFrame->getData();
589 delete pFrame;
592 delete[] framestart;
593 return data;
596 /****** pcxGetData *****************************************************
597 * decodes pcx files (256 color, as in diablo mpq)
598 * Args:
599 * *pFileBuf the buffer containing the filecontent
600 * uFileSize the size of the file buffer
601 * uTransColor the palette entry to be transparent
602 * *uXSize the actual width is returned herein
603 * *uYSize the actual height is returned herein
605 * Returns: an array containing 4 Bytes (RGBA) for each pixel
607 * ---------------------------------------------------------------
608 * Comments: format info and pseudocode taken from:
609 * Klaus Holtorf, "Das Handbuch der Grafikformate"
610 * ISBN 3-7723-6393-8
611 ***********************************************************************/
612 BYTE * WINAPI pcxGetData(BYTE *pFileBuf, DWORD uFileSize, BYTE uTransColor, WORD *uXSize, WORD *uYSize)
614 uint32_t uFilePos=0;
615 uint32_t uDataRead=0; // potentially big! (logo.pcx: 550 * 216 * 15 = 1,782,000)
616 uint16_t i=0;
617 uint8_t *data, *palette;
618 uint8_t uColorNum=0, uCount=0;
620 struct pcx_header_t {
621 uint8_t id;
622 uint8_t version;
623 uint8_t compressed;
624 uint8_t bpp;
625 uint16_t x0;
626 uint16_t y0;
627 uint16_t x1;
628 uint16_t y1;
629 uint16_t xdpi;
630 uint16_t ydpi;
631 uint8_t pal[16][3];
632 uint8_t reserved;
633 uint8_t layers;
634 uint16_t rowbytes;
635 uint16_t colortype;
636 uint8_t pad[58];
637 } pcxHeader;
639 if (pFileBuf == NULL) {
640 SetLastError(ERROR_INVALID_PARAMETER);
641 return NULL;
644 if (uXSize == NULL) {
645 SetLastError(ERROR_INVALID_PARAMETER);
646 return NULL;
649 if (uYSize == NULL) {
650 SetLastError(ERROR_INVALID_PARAMETER);
651 return NULL;
654 // get image information
655 memcpy(&pcxHeader, pFileBuf, sizeof(struct pcx_header_t));
656 *uXSize = (pcxHeader.x1 - pcxHeader.x0 + 1);
657 *uYSize = (pcxHeader.y1 - pcxHeader.y0 + 1);
659 if ((pcxHeader.version != 2) && (pcxHeader.version != 5)) {
660 cerr << "cannot handle pcx v" << pcxHeader.version << "\n";
661 return NULL;
664 // get palette
665 palette = new uint8_t[256*4];
666 if (pFileBuf[uFileSize - 768 - 1] != 0x0C) {
667 cerr << "palette error at " << uFileSize - 768 - 1 << "\n";
668 return NULL;
670 for (i=0; i<256; i++) {
671 memcpy(palette+i*4, pFileBuf+uFileSize-768+i*3, 3*sizeof(uint8_t));
672 palette[i*4+3] = 0xFF;
674 memset(palette+uTransColor*4, 0, 4*sizeof(uint8_t)); // transparent black
676 // start right after the header
677 uFilePos = sizeof(struct pcx_header_t);
678 data = new uint8_t[*uXSize * *uYSize * 4];
679 while (uDataRead < (uint32_t)(*uXSize * *uYSize)) {
680 // decompress
681 uColorNum = pFileBuf[uFilePos++];
682 if ((pcxHeader.compressed) && (uColorNum > 0xBF)) {
683 uCount = (uColorNum & 0x3F);
684 uColorNum = pFileBuf[uFilePos++];
685 } else
686 uCount = 1;
688 // draw count pixels with color val
689 for (i=0; i<uCount; i++)
690 memcpy(data+(uDataRead++)*4, palette+uColorNum*4, 4*sizeof(uint8_t));
693 // cleanup
694 delete[] palette;
696 return data;