1 //**************************************************************************
2 //**
3 //**	##   ##    ##    ##   ##   ####     ####   ###     ###
4 //**	##   ##  ##  ##  ##   ##  ##  ##   ##  ##  ####   ####
5 //**	 ## ##  ##    ##  ## ##  ##    ## ##    ## ## ## ## ##
6 //**	 ## ##  ########  ## ##  ##    ## ##    ## ##  ###  ##
7 //**	  ###   ##    ##   ###    ##  ##   ##  ##  ##       ##
8 //**	   #    ##    ##    #      ####     ####   ##       ##
9 //**
10 //**	$Id: r_tex_pcx.cpp 4297 2010-06-03 22:49:00Z firebrand_kh $
11 //**
12 //**	Copyright (C) 1999-2006 Jānis Legzdiņš
13 //**
14 //**	This program is free software; you can redistribute it and/or
15 //**  modify it under the terms of the GNU General Public License
16 //**  as published by the Free Software Foundation; either version 2
17 //**  of the License, or (at your option) any later version.
18 //**
19 //**	This program is distributed in the hope that it will be useful,
20 //**  but WITHOUT ANY WARRANTY; without even the implied warranty of
21 //**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 //**  GNU General Public License for more details.
23 //**
24 //**************************************************************************
25 
26 // HEADER FILES ------------------------------------------------------------
27 
28 #include "gamedefs.h"
29 #include "r_tex.h"
30 
31 // MACROS ------------------------------------------------------------------
32 
33 // TYPES -------------------------------------------------------------------
34 
35 struct pcx_t
36 {
37 	vint8		manufacturer;
38 	vint8		version;
39 	vint8		encoding;
40 	vint8		bits_per_pixel;
41 
42 	vuint16		xmin;
43 	vuint16		ymin;
44 	vuint16		xmax;
45 	vuint16		ymax;
46 
47 	vuint16		hres;
48 	vuint16		vres;
49 
50 	vuint8		palette[48];
51 
52 	vint8		reserved;
53 	vint8		colour_planes;
54 	vuint16		bytes_per_line;
55 	vuint16		palette_type;
56 
57 	vuint16		horz_screen_size;
58 	vuint16		vert_screen_size;
59 
60 	vint8		filler[54];
61 
operator <<(VStream & Strm,pcx_t & h)62 	friend VStream& operator<<(VStream& Strm, pcx_t& h)
63 	{
64 		Strm << h.manufacturer << h.version << h.encoding << h.bits_per_pixel
65 			<< h.xmin << h.ymin << h.xmax << h.ymax << h.hres << h.vres;
66 		Strm.Serialise(h.palette, 48);
67 		Strm << h.reserved << h.colour_planes << h.bytes_per_line
68 			<< h.palette_type << h.horz_screen_size << h.vert_screen_size;
69 		Strm.Serialise(h.filler, 54);
70 		return Strm;
71 	}
72 };
73 
74 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
75 
76 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
77 
78 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
79 
80 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
81 
82 // PUBLIC DATA DEFINITIONS -------------------------------------------------
83 
84 // PRIVATE DATA DEFINITIONS ------------------------------------------------
85 
86 // CODE --------------------------------------------------------------------
87 
88 //==========================================================================
89 //
90 //	VPcxTexture::Create
91 //
92 //==========================================================================
93 
Create(VStream & Strm,int LumpNum)94 VTexture* VPcxTexture::Create(VStream& Strm, int LumpNum)
95 {
96 	guard(VPcxTexture::Create);
97 	if (Strm.TotalSize() < 128)
98 	{
99 		//	File is too small.
100 		return NULL;
101 	}
102 
103 	pcx_t Hdr;
104 	Strm.Seek(0);
105 	Strm << Hdr;
106 
107 	if (Hdr.manufacturer != 0x0a || Hdr.encoding != 1 ||
108 		Hdr.version == 1 || Hdr.version > 5 || Hdr.reserved != 0 ||
109 		(Hdr.bits_per_pixel != 1 && Hdr.bits_per_pixel != 8) ||
110 		(Hdr.bits_per_pixel == 1 && Hdr.colour_planes != 1 && Hdr.colour_planes != 4) ||
111 		(Hdr.bits_per_pixel == 8 && Hdr.bytes_per_line != (Hdr.xmax - Hdr.xmin + 1)) ||
112 		(Hdr.palette_type != 1 && Hdr.palette_type != 2))
113 	{
114 		return NULL;
115 	}
116 	for (int i = 0; i < 54; i++)
117 	{
118 		if (Hdr.filler[i] != 0)
119 		{
120 			return NULL;
121 		}
122 	}
123 
124 	return new VPcxTexture(LumpNum, Hdr);
125 	unguard;
126 }
127 
128 //==========================================================================
129 //
130 //	VPcxTexture::VPcxTexture
131 //
132 //==========================================================================
133 
VPcxTexture(int ALumpNum,pcx_t & Hdr)134 VPcxTexture::VPcxTexture(int ALumpNum, pcx_t& Hdr)
135 : Pixels(0)
136 , Palette(0)
137 {
138 	SourceLump = ALumpNum;
139 	Name = W_LumpName(SourceLump);
140 	Width = Hdr.xmax - Hdr.xmin + 1;
141 	Height = Hdr.ymax - Hdr.ymin + 1;
142 }
143 
144 //==========================================================================
145 //
146 //	VPcxTexture::~VPcxTexture
147 //
148 //==========================================================================
149 
~VPcxTexture()150 VPcxTexture::~VPcxTexture()
151 {
152 	guard(VPcxTexture::~VPcxTexture);
153 	if (Pixels)
154 	{
155 		delete[] Pixels;
156 		Pixels = NULL;
157 	}
158 	if (Palette)
159 	{
160 		delete[] Palette;
161 		Palette = NULL;
162 	}
163 	unguard;
164 }
165 
166 //==========================================================================
167 //
168 //	VPcxTexture::GetPixels
169 //
170 //==========================================================================
171 
GetPixels()172 vuint8* VPcxTexture::GetPixels()
173 {
174 	guard(VPcxTexture::GetPixels);
175 	int			c;
176 	int			bytes_per_line;
177 	vint8		ch;
178 
179 	//	If we already have loaded pixels, return them.
180 	if (Pixels)
181 	{
182 		return Pixels;
183 	}
184 
185 	//	Open stream.
186 	VStream* Strm = W_CreateLumpReaderNum(SourceLump);
187 
188 	//	Read header.
189 	pcx_t pcx;
190 	*Strm << pcx;
191 
192 	//	We only support 8-bit pcx files.
193 	if (pcx.bits_per_pixel != 8)
194 	{
195 		// we like 8 bit colour planes
196 		Sys_Error("No 8-bit planes\n");
197 	}
198 	if (pcx.colour_planes != 1)
199 	{
200 		Sys_Error("Not 8 bpp\n");
201 	}
202 
203 	Width = pcx.xmax - pcx.xmin + 1;
204 	Height = pcx.ymax - pcx.ymin + 1;
205 	Format = TEXFMT_8Pal;
206 
207 	bytes_per_line = pcx.bytes_per_line;
208 
209 	Pixels = new vuint8[Width * Height];
210 
211 	for (int y = 0; y < Height; y++)
212 	{
213 		// decompress RLE encoded PCX data
214 		int x = 0;
215 
216 		while (x < bytes_per_line)
217 		{
218 			*Strm << ch;
219 			if ((ch & 0xC0) == 0xC0)
220 			{
221 				c = (ch & 0x3F);
222 				*Strm << ch;
223 			}
224 			else
225 			{
226 				c = 1;
227 			}
228 
229 			while (c--)
230 			{
231 				if (x < Width)
232 					Pixels[y * Width + x] = ch;
233 				x++;
234 			}
235 		}
236 	}
237 
238 	//	If not followed by palette ID, assume palette is at the end of file.
239 	*Strm << ch;
240 	if (ch != 12)
241 	{
242 		Strm->Seek(Strm->TotalSize() - 768);
243 	}
244 
245 	//	Read palette.
246 	Palette = new rgba_t[256];
247 	for (c = 0; c < 256; c++)
248 	{
249 		*Strm << Palette[c].r
250 			<< Palette[c].g
251 			<< Palette[c].b;
252 		Palette[c].a = 255;
253 	}
254 	FixupPalette(Pixels, Palette);
255 
256 	delete Strm;
257 	Strm = NULL;
258 	return Pixels;
259 	unguard;
260 }
261 
262 //==========================================================================
263 //
264 //	VPcxTexture::GetPalette
265 //
266 //==========================================================================
267 
GetPalette()268 rgba_t* VPcxTexture::GetPalette()
269 {
270 	guardSlow(VPcxTexture::GetPalette);
271 	return Palette;
272 	unguardSlow;
273 }
274 
275 //==========================================================================
276 //
277 //	VPcxTexture::Unload
278 //
279 //==========================================================================
280 
Unload()281 void VPcxTexture::Unload()
282 {
283 	guard(VPcxTexture::Unload);
284 	if (Pixels)
285 	{
286 		delete[] Pixels;
287 		Pixels = NULL;
288 	}
289 	if (Palette)
290 	{
291 		delete[] Palette;
292 		Palette = NULL;
293 	}
294 	unguard;
295 }
296 
297 //==========================================================================
298 //
299 //  WritePCX
300 //
301 //==========================================================================
302 
303 #ifdef CLIENT
WritePCX(char * filename,void * data,int width,int height,int bpp,bool bot2top)304 void WritePCX(char* filename, void* data, int width, int height, int bpp,
305 	bool bot2top)
306 {
307 	guard(WritePCX);
308 	int i;
309 	int j;
310 
311 	VStream* Strm = FL_OpenFileWrite(filename);
312 	if (!Strm)
313 	{
314 		GCon->Log("Couldn't write pcx");
315 		return;
316 	}
317 
318 	pcx_t pcx;
319 	pcx.manufacturer = 0x0a;	// PCX id
320 	pcx.version = 5;			// 256 colour
321 	pcx.encoding = 1;			// uncompressed
322 	pcx.bits_per_pixel = 8;		// 256 colour
323 	pcx.xmin = 0;
324 	pcx.ymin = 0;
325 	pcx.xmax = width - 1;
326 	pcx.ymax = height - 1;
327 	pcx.hres = width;
328 	pcx.vres = height;
329 	memset(pcx.palette, 0, sizeof(pcx.palette));
330 	pcx.colour_planes = bpp == 8 ? 1 : 3;
331 	pcx.bytes_per_line = width;
332 	pcx.palette_type = 1;	// not a grey scale
333 	pcx.horz_screen_size = 0;
334 	pcx.vert_screen_size = 0;
335 	memset(pcx.filler, 0, sizeof(pcx.filler));
336 	*Strm << pcx;
337 
338 	// pack the image
339 	if (bpp == 8)
340 	{
341 		for (j = 0; j < height; j++)
342 		{
343 			byte *src = (byte*)data + j * width;
344 			for (i = 0; i < width; i++)
345 			{
346 				if ((src[i] & 0xc0) == 0xc0)
347 				{
348 					byte tmp = 0xc1;
349 					*Strm << tmp;
350 				}
351 				*Strm << src[i];
352 			}
353 		}
354 
355 		// write the palette
356 		byte PalId = 0x0c;	// palette ID byte
357 		*Strm << PalId;
358 		for (i = 0; i < 256; i++)
359 		{
360 			*Strm << r_palette[i].r
361 				<< r_palette[i].g
362 				<< r_palette[i].b;
363 		}
364 	}
365 	else if	(bpp == 24)
366 	{
367 		for (j = 0; j < height; j++)
368 		{
369 			rgb_t *src = (rgb_t*)data + (bot2top ? height - j - 1 : j) * width;
370 			for (i = 0; i < width; i++)
371 			{
372 				if ((src[i].r & 0xc0) == 0xc0)
373 				{
374 					byte tmp = 0xc1;
375 					*Strm << tmp;
376 				}
377 				*Strm << src[i].r;
378 			}
379 			for (i = 0; i < width; i++)
380 			{
381 				if ((src[i].g & 0xc0) == 0xc0)
382 				{
383 					byte tmp = 0xc1;
384 					*Strm << tmp;
385 				}
386 				*Strm << src[i].g;
387 			}
388 			for (i = 0; i < width; i++)
389 			{
390 				if ((src[i].b & 0xc0) == 0xc0)
391 				{
392 					byte tmp = 0xc1;
393 					*Strm << tmp;
394 				}
395 				*Strm << src[i].b;
396 			}
397 		}
398 	}
399 
400 	delete Strm;
401 	Strm = NULL;
402 	unguard;
403 }
404 #endif
405