1 /*
2 ** pcxtexture.cpp
3 ** Texture class for PCX images
4 **
5 **---------------------------------------------------------------------------
6 ** Copyright 2005 David HENRY
7 ** Copyright 2006 Christoph Oelckers
8 ** All rights reserved.
9 **
10 ** Redistribution and use in source and binary forms, with or without
11 ** modification, are permitted provided that the following conditions
12 ** are met:
13 **
14 ** 1. Redistributions of source code must retain the above copyright
15 ** notice, this list of conditions and the following disclaimer.
16 ** 2. Redistributions in binary form must reproduce the above copyright
17 ** notice, this list of conditions and the following disclaimer in the
18 ** documentation and/or other materials provided with the distribution.
19 ** 3. The name of the author may not be used to endorse or promote products
20 ** derived from this software without specific prior written permission.
21 **
22 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 **---------------------------------------------------------------------------
33 **
34 **
35 */
36
37 #include "doomtype.h"
38 #include "files.h"
39 #include "w_wad.h"
40 #include "templates.h"
41 #include "bitmap.h"
42 #include "colormatcher.h"
43 #include "v_video.h"
44 #include "textures/textures.h"
45
46 //==========================================================================
47 //
48 // PCX file header
49 //
50 //==========================================================================
51
52 #pragma pack(1)
53
54 struct PCXHeader
55 {
56 BYTE manufacturer;
57 BYTE version;
58 BYTE encoding;
59 BYTE bitsPerPixel;
60
61 WORD xmin, ymin;
62 WORD xmax, ymax;
63 WORD horzRes, vertRes;
64
65 BYTE palette[48];
66 BYTE reserved;
67 BYTE numColorPlanes;
68
69 WORD bytesPerScanLine;
70 WORD paletteType;
71 WORD horzSize, vertSize;
72
73 BYTE padding[54];
74
75 };
76 #pragma pack()
77
78 //==========================================================================
79 //
80 // a PCX texture
81 //
82 //==========================================================================
83
84 class FPCXTexture : public FTexture
85 {
86 public:
87 FPCXTexture (int lumpnum, PCXHeader &);
88 ~FPCXTexture ();
89
90 const BYTE *GetColumn (unsigned int column, const Span **spans_out);
91 const BYTE *GetPixels ();
92 void Unload ();
93 FTextureFormat GetFormat ();
94
95 int CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf = NULL);
96 bool UseBasePalette();
97
98 protected:
99 BYTE *Pixels;
100 Span DummySpans[2];
101
102 void ReadPCX1bit (BYTE *dst, FileReader & lump, PCXHeader *hdr);
103 void ReadPCX4bits (BYTE *dst, FileReader & lump, PCXHeader *hdr);
104 void ReadPCX8bits (BYTE *dst, FileReader & lump, PCXHeader *hdr);
105 void ReadPCX24bits (BYTE *dst, FileReader & lump, PCXHeader *hdr, int planes);
106
107 virtual void MakeTexture ();
108
109 friend class FTexture;
110 };
111
112
113 //==========================================================================
114 //
115 //
116 //
117 //==========================================================================
118
PCXTexture_TryCreate(FileReader & file,int lumpnum)119 FTexture * PCXTexture_TryCreate(FileReader & file, int lumpnum)
120 {
121 PCXHeader hdr;
122
123 file.Seek(0, SEEK_SET);
124 if (file.Read(&hdr, sizeof(hdr)) != sizeof(hdr))
125 {
126 return NULL;
127 }
128
129 hdr.xmin = LittleShort(hdr.xmin);
130 hdr.xmax = LittleShort(hdr.xmax);
131 hdr.bytesPerScanLine = LittleShort(hdr.bytesPerScanLine);
132
133 if (hdr.manufacturer != 10 || hdr.encoding != 1) return NULL;
134 if (hdr.version != 0 && hdr.version != 2 && hdr.version != 3 && hdr.version != 4 && hdr.version != 5) return NULL;
135 if (hdr.bitsPerPixel != 1 && hdr.bitsPerPixel != 8) return NULL;
136 if (hdr.bitsPerPixel == 1 && hdr.numColorPlanes !=1 && hdr.numColorPlanes != 4) return NULL;
137 if (hdr.bitsPerPixel == 8 && hdr.bytesPerScanLine != ((hdr.xmax - hdr.xmin + 2)&~1)) return NULL;
138
139 for (int i = 0; i < 54; i++)
140 {
141 if (hdr.padding[i] != 0) return NULL;
142 }
143
144 file.Seek(0, SEEK_SET);
145 file.Read(&hdr, sizeof(hdr));
146
147 return new FPCXTexture(lumpnum, hdr);
148 }
149
150 //==========================================================================
151 //
152 //
153 //
154 //==========================================================================
155
FPCXTexture(int lumpnum,PCXHeader & hdr)156 FPCXTexture::FPCXTexture(int lumpnum, PCXHeader & hdr)
157 : FTexture(NULL, lumpnum), Pixels(0)
158 {
159 bMasked = false;
160 Width = LittleShort(hdr.xmax) - LittleShort(hdr.xmin) + 1;
161 Height = LittleShort(hdr.ymax) - LittleShort(hdr.ymin) + 1;
162 CalcBitSize();
163
164 DummySpans[0].TopOffset = 0;
165 DummySpans[0].Length = Height;
166 DummySpans[1].TopOffset = 0;
167 DummySpans[1].Length = 0;
168 }
169
170 //==========================================================================
171 //
172 //
173 //
174 //==========================================================================
175
~FPCXTexture()176 FPCXTexture::~FPCXTexture ()
177 {
178 Unload ();
179 }
180
181 //==========================================================================
182 //
183 //
184 //
185 //==========================================================================
186
Unload()187 void FPCXTexture::Unload ()
188 {
189 if (Pixels != NULL)
190 {
191 delete[] Pixels;
192 Pixels = NULL;
193 }
194 }
195
196 //==========================================================================
197 //
198 //
199 //
200 //==========================================================================
201
GetFormat()202 FTextureFormat FPCXTexture::GetFormat()
203 {
204 return TEX_RGB;
205 }
206
207 //==========================================================================
208 //
209 //
210 //
211 //==========================================================================
212
GetColumn(unsigned int column,const Span ** spans_out)213 const BYTE *FPCXTexture::GetColumn (unsigned int column, const Span **spans_out)
214 {
215 if (Pixels == NULL)
216 {
217 MakeTexture ();
218 }
219 if ((unsigned)column >= (unsigned)Width)
220 {
221 if (WidthMask + 1 == Width)
222 {
223 column &= WidthMask;
224 }
225 else
226 {
227 column %= Width;
228 }
229 }
230 if (spans_out != NULL)
231 {
232 *spans_out = DummySpans;
233 }
234 return Pixels + column*Height;
235 }
236
237 //==========================================================================
238 //
239 //
240 //
241 //==========================================================================
242
GetPixels()243 const BYTE *FPCXTexture::GetPixels ()
244 {
245 if (Pixels == NULL)
246 {
247 MakeTexture ();
248 }
249 return Pixels;
250 }
251
252 //==========================================================================
253 //
254 //
255 //
256 //==========================================================================
257
ReadPCX1bit(BYTE * dst,FileReader & lump,PCXHeader * hdr)258 void FPCXTexture::ReadPCX1bit (BYTE *dst, FileReader & lump, PCXHeader *hdr)
259 {
260 int y, i, bytes;
261 int rle_count = 0;
262 BYTE rle_value = 0;
263
264 BYTE * srcp = new BYTE[lump.GetLength() - sizeof(PCXHeader)];
265 lump.Read(srcp, lump.GetLength() - sizeof(PCXHeader));
266 BYTE * src = srcp;
267
268 for (y = 0; y < Height; ++y)
269 {
270 BYTE * ptr = &dst[y * Width];
271
272 bytes = hdr->bytesPerScanLine;
273
274 while (bytes--)
275 {
276 if (rle_count == 0)
277 {
278 if ( (rle_value = *src++) < 0xc0)
279 {
280 rle_count = 1;
281 }
282 else
283 {
284 rle_count = rle_value - 0xc0;
285 rle_value = *src++;
286 }
287 }
288
289 rle_count--;
290
291 for (i = 7; i >= 0; --i, ptr ++)
292 {
293 *ptr = ((rle_value & (1 << i)) > 0);
294 }
295 }
296 }
297 delete [] srcp;
298 }
299
300 //==========================================================================
301 //
302 //
303 //
304 //==========================================================================
305
ReadPCX4bits(BYTE * dst,FileReader & lump,PCXHeader * hdr)306 void FPCXTexture::ReadPCX4bits (BYTE *dst, FileReader & lump, PCXHeader *hdr)
307 {
308 int rle_count = 0, rle_value = 0;
309 int x, y, c;
310 int bytes;
311 BYTE * line = new BYTE[hdr->bytesPerScanLine];
312 BYTE * colorIndex = new BYTE[Width];
313
314 BYTE * srcp = new BYTE[lump.GetLength() - sizeof(PCXHeader)];
315 lump.Read(srcp, lump.GetLength() - sizeof(PCXHeader));
316 BYTE * src = srcp;
317
318 for (y = 0; y < Height; ++y)
319 {
320 BYTE * ptr = &dst[y * Width];
321 memset (ptr, 0, Width * sizeof (BYTE));
322
323 for (c = 0; c < 4; ++c)
324 {
325 BYTE * pLine = line;
326
327 bytes = hdr->bytesPerScanLine;
328
329 while (bytes--)
330 {
331 if (rle_count == 0)
332 {
333 if ( (rle_value = *src++) < 0xc0)
334 {
335 rle_count = 1;
336 }
337 else
338 {
339 rle_count = rle_value - 0xc0;
340 rle_value = *src++;
341 }
342 }
343
344 rle_count--;
345 *(pLine++) = rle_value;
346 }
347
348 /* compute line's color indexes */
349 for (x = 0; x < Width; ++x)
350 {
351 if (line[x / 8] & (128 >> (x % 8)))
352 ptr[x] += (1 << c);
353 }
354 }
355 }
356
357 /* release memory */
358 delete [] colorIndex;
359 delete [] line;
360 delete [] srcp;
361 }
362
363 //==========================================================================
364 //
365 //
366 //
367 //==========================================================================
368
ReadPCX8bits(BYTE * dst,FileReader & lump,PCXHeader * hdr)369 void FPCXTexture::ReadPCX8bits (BYTE *dst, FileReader & lump, PCXHeader *hdr)
370 {
371 int rle_count = 0, rle_value = 0;
372 int y, bytes;
373
374 BYTE * srcp = new BYTE[lump.GetLength() - sizeof(PCXHeader)];
375 lump.Read(srcp, lump.GetLength() - sizeof(PCXHeader));
376 BYTE * src = srcp;
377
378 for (y = 0; y < Height; ++y)
379 {
380 BYTE * ptr = &dst[y * Width];
381
382 bytes = hdr->bytesPerScanLine;
383 while (bytes--)
384 {
385 if (rle_count == 0)
386 {
387 if( (rle_value = *src++) < 0xc0)
388 {
389 rle_count = 1;
390 }
391 else
392 {
393 rle_count = rle_value - 0xc0;
394 rle_value = *src++;
395 }
396 }
397
398 rle_count--;
399 *ptr++ = rle_value;
400 }
401 }
402 delete [] srcp;
403 }
404
405 //==========================================================================
406 //
407 //
408 //
409 //==========================================================================
410
ReadPCX24bits(BYTE * dst,FileReader & lump,PCXHeader * hdr,int planes)411 void FPCXTexture::ReadPCX24bits (BYTE *dst, FileReader & lump, PCXHeader *hdr, int planes)
412 {
413 int rle_count = 0, rle_value = 0;
414 int y, c;
415 int bytes;
416
417 BYTE * srcp = new BYTE[lump.GetLength() - sizeof(PCXHeader)];
418 lump.Read(srcp, lump.GetLength() - sizeof(PCXHeader));
419 BYTE * src = srcp;
420
421 for (y = 0; y < Height; ++y)
422 {
423 /* for each color plane */
424 for (c = 0; c < planes; ++c)
425 {
426 BYTE * ptr = &dst[y * Width * planes];
427 bytes = hdr->bytesPerScanLine;
428
429 while (bytes--)
430 {
431 if (rle_count == 0)
432 {
433 if( (rle_value = *src++) < 0xc0)
434 {
435 rle_count = 1;
436 }
437 else
438 {
439 rle_count = rle_value - 0xc0;
440 rle_value = *src++;
441 }
442 }
443
444 rle_count--;
445 ptr[c] = (BYTE)rle_value;
446 ptr += planes;
447 }
448 }
449 }
450 delete [] srcp;
451 }
452
453 //==========================================================================
454 //
455 //
456 //
457 //==========================================================================
458
MakeTexture()459 void FPCXTexture::MakeTexture()
460 {
461 BYTE PaletteMap[256];
462 PCXHeader header;
463 int bitcount;
464
465 FWadLump lump = Wads.OpenLumpNum(SourceLump);
466
467 lump.Read(&header, sizeof(header));
468
469 bitcount = header.bitsPerPixel * header.numColorPlanes;
470 Pixels = new BYTE[Width*Height];
471
472 if (bitcount < 24)
473 {
474 if (bitcount < 8)
475 {
476 for (int i=0;i<16;i++)
477 {
478 PaletteMap[i] = ColorMatcher.Pick(header.palette[i*3],header.palette[i*3+1],header.palette[i*3+2]);
479 }
480
481 switch (bitcount)
482 {
483 default:
484 case 1:
485 ReadPCX1bit (Pixels, lump, &header);
486 break;
487
488 case 4:
489 ReadPCX4bits (Pixels, lump, &header);
490 break;
491 }
492 }
493 else if (bitcount == 8)
494 {
495 BYTE c;
496 lump.Seek(-769, SEEK_END);
497 lump >> c;
498 //if (c !=0x0c) memcpy(PaletteMap, GrayMap, 256); // Fallback for files without palette
499 //else
500 for(int i=0;i<256;i++)
501 {
502 BYTE r,g,b;
503 lump >> r >> g >> b;
504 PaletteMap[i] = ColorMatcher.Pick(r,g,b);
505 }
506 lump.Seek(sizeof(header), SEEK_SET);
507 ReadPCX8bits (Pixels, lump, &header);
508 }
509 if (Width == Height)
510 {
511 FlipSquareBlockRemap(Pixels, Width, Height, PaletteMap);
512 }
513 else
514 {
515 BYTE *newpix = new BYTE[Width*Height];
516 FlipNonSquareBlockRemap (newpix, Pixels, Width, Height, Width, PaletteMap);
517 BYTE *oldpix = Pixels;
518 Pixels = newpix;
519 delete[] oldpix;
520 }
521 }
522 else
523 {
524 BYTE * buffer = new BYTE[Width*Height * 3];
525 BYTE * row = buffer;
526 ReadPCX24bits (buffer, lump, &header, 3);
527 for(int y=0; y<Height; y++)
528 {
529 for(int x=0; x < Width; x++)
530 {
531 Pixels[y+Height*x] = RGB32k.RGB[row[0]>>3][row[1]>>3][row[2]>>3];
532 row+=3;
533 }
534 }
535 delete [] buffer;
536 }
537 }
538
539 //===========================================================================
540 //
541 // FPCXTexture::CopyTrueColorPixels
542 //
543 // Preserves the full color information (unlike software mode)
544 //
545 //===========================================================================
546
CopyTrueColorPixels(FBitmap * bmp,int x,int y,int rotate,FCopyInfo * inf)547 int FPCXTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf)
548 {
549 PalEntry pe[256];
550 PCXHeader header;
551 int bitcount;
552 BYTE * Pixels;
553
554 FWadLump lump = Wads.OpenLumpNum(SourceLump);
555
556 lump.Read(&header, sizeof(header));
557
558 bitcount = header.bitsPerPixel * header.numColorPlanes;
559
560 if (bitcount < 24)
561 {
562 Pixels = new BYTE[Width*Height];
563 if (bitcount < 8)
564 {
565 for (int i=0;i<16;i++)
566 {
567 pe[i] = PalEntry(header.palette[i*3],header.palette[i*3+1],header.palette[i*3+2]);
568 }
569
570 switch (bitcount)
571 {
572 default:
573 case 1:
574 ReadPCX1bit (Pixels, lump, &header);
575 break;
576
577 case 4:
578 ReadPCX4bits (Pixels, lump, &header);
579 break;
580 }
581 }
582 else if (bitcount == 8)
583 {
584 BYTE c;
585 lump.Seek(-769, SEEK_END);
586 lump >> c;
587 c=0x0c; // Apparently there's many non-compliant PCXs out there...
588 if (c !=0x0c)
589 {
590 for(int i=0;i<256;i++) pe[i]=PalEntry(255,i,i,i); // default to a gray map
591 }
592 else for(int i=0;i<256;i++)
593 {
594 BYTE r,g,b;
595 lump >> r >> g >> b;
596 pe[i] = PalEntry(255, r,g,b);
597 }
598 lump.Seek(sizeof(header), SEEK_SET);
599 ReadPCX8bits (Pixels, lump, &header);
600 }
601 bmp->CopyPixelData(x, y, Pixels, Width, Height, 1, Width, rotate, pe, inf);
602 }
603 else
604 {
605 Pixels = new BYTE[Width*Height * 3];
606 ReadPCX24bits (Pixels, lump, &header, 3);
607 bmp->CopyPixelDataRGB(x, y, Pixels, Width, Height, 3, Width*3, rotate, CF_RGB, inf);
608 }
609 delete [] Pixels;
610 return 0;
611 }
612
613
614 //===========================================================================
615 //
616 //
617 //===========================================================================
618
UseBasePalette()619 bool FPCXTexture::UseBasePalette()
620 {
621 return false;
622 }
623