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