local variables ALSO work better when declared
[divverent/netradiant.git] / plugins / image / pcx.cpp
1 /*
2 Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5 This file is part of GtkRadiant.
6
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 */
21
22 #include "pcx.h"
23
24 #include "ifilesystem.h"
25
26 typedef unsigned char byte;
27
28 #include <stdlib.h>
29
30 #include "imagelib.h"
31 #include "bytestreamutils.h"
32
33 /*
34 =================================================================
35
36 PCX LOADING
37
38 =================================================================
39 */
40
41 typedef struct
42 {
43   unsigned char manufacturer;
44   unsigned char version;
45   unsigned char encoding;
46   unsigned char bits_per_pixel;
47   unsigned short xmin, ymin, xmax, ymax;
48   unsigned short hres, vres;
49   unsigned char palette[48];
50   unsigned char reserved;
51   unsigned char color_planes;
52   unsigned short bytes_per_line;
53   unsigned short palette_type;
54   unsigned char filler[58];
55   unsigned char data;           // unbounded
56 } pcx_t;
57
58 /*
59 ==============
60 LoadPCX
61 ==============
62 */
63
64 struct PCXRLEPacket
65 {
66   byte data;
67   int length;
68 };
69
70 inline void ByteStream_readPCXRLEPacket(PointerInputStream& inputStream, PCXRLEPacket& packet)
71 {
72   byte d;
73   inputStream.read(&d, 1);
74   if((d & 0xC0) == 0xC0)
75   {
76     packet.length = d & 0x3F;
77     inputStream.read(&packet.data, 1); 
78   }
79   else
80   {
81     packet.length = 1;
82     packet.data = d;
83   }
84 }
85
86 void LoadPCXBuff(byte* buffer, std::size_t len, byte **pic, byte **palette, int *width, int *height )
87 {
88   *pic = 0;
89
90   pcx_t pcx;
91   int           x, y, lsize;
92   byte  *out, *pix;
93
94   /* parse the PCX file */
95
96   PointerInputStream inputStream(buffer);
97   
98   pcx.manufacturer = istream_read_byte(inputStream);
99   pcx.version = istream_read_byte(inputStream);
100   pcx.encoding = istream_read_byte(inputStream);
101   pcx.bits_per_pixel = istream_read_byte(inputStream);
102   pcx.xmin = istream_read_int16_le(inputStream);
103   pcx.ymin = istream_read_int16_le(inputStream);
104   pcx.xmax = istream_read_int16_le(inputStream);
105   pcx.ymax = istream_read_int16_le(inputStream);
106   pcx.hres = istream_read_int16_le(inputStream);
107   pcx.vres = istream_read_int16_le(inputStream);
108   inputStream.read(pcx.palette, 48);
109   pcx.reserved = istream_read_byte(inputStream);
110   pcx.color_planes = istream_read_byte(inputStream);
111   pcx.bytes_per_line = istream_read_int16_le(inputStream);
112   pcx.palette_type = istream_read_int16_le(inputStream);
113   inputStream.read(pcx.filler, 58);
114
115   
116   if (pcx.manufacturer != 0x0a
117     || pcx.version != 5
118     || pcx.encoding != 1
119     || pcx.bits_per_pixel != 8)
120     return;
121
122   if (width)
123     *width = pcx.xmax+1;
124   if (height)
125     *height = pcx.ymax+1;
126
127   if (!pic)
128     return;
129
130   out = (byte *)malloc ( (pcx.ymax+1) * (pcx.xmax+1) );
131
132   *pic = out;
133   pix = out;
134         
135   /* RR2DO2: pcx fix  */
136   lsize = pcx.color_planes * pcx.bytes_per_line;
137         
138   /* go scanline by scanline */
139   for( y = 0; y <= pcx.ymax; y++, pix += pcx.xmax + 1 )
140   {
141     /* do a scanline */
142     for( x=0; x <= pcx.xmax; )
143     {
144       /* RR2DO2 */
145       PCXRLEPacket packet;
146       ByteStream_readPCXRLEPacket(inputStream, packet);
147       
148       while(packet.length-- > 0)
149       {
150         pix[ x++ ] = packet.data;
151       }
152     }
153
154     /* RR2DO2: discard any other data */
155     PCXRLEPacket packet;
156     while( x < lsize )
157     {
158       ByteStream_readPCXRLEPacket(inputStream, packet);
159       x++;
160     }
161     while( packet.length-- > 0 )
162     {
163       x++;
164     }
165   }
166         
167   /* validity check */
168   if( std::size_t(inputStream.get() - buffer) > len)
169   {
170     *pic = 0;
171   }
172
173   if (palette)
174   {
175     *palette = (byte *)malloc(768);
176     memcpy (*palette, buffer + len - 768, 768);
177   }
178 }
179
180 /*
181 ==============
182 LoadPCX32
183 ==============
184 */
185 Image* LoadPCX32Buff(byte* buffer, std::size_t length)
186 {
187   byte *palette;
188   byte *pic8;
189   int i, c, p, width, height;
190   byte *pic32;
191
192   LoadPCXBuff(buffer, length, &pic8, &palette, &width, &height);
193   if (!pic8)
194   {
195     return 0;
196   }
197
198   RGBAImage* image = new RGBAImage(width, height);
199   c = (width) * (height);
200   pic32 = image->getRGBAPixels();
201   for (i = 0; i < c; i++)
202   {
203     p = pic8[i];
204     pic32[0] = palette[p * 3];
205     pic32[1] = palette[p * 3 + 1];
206     pic32[2] = palette[p * 3 + 2];
207     pic32[3] = 255;
208     pic32 += 4;
209   }
210
211   free (pic8);
212   free (palette);
213
214   return image;
215 }
216
217 Image* LoadPCX32(ArchiveFile& file)
218 {
219   ScopedArchiveBuffer buffer(file);
220   return LoadPCX32Buff(buffer.buffer, buffer.length);
221 }
222