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