Merge branch 'master' into divVerent/farplanedist-sky-fix
[xonotic/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         byte d;
72         inputStream.read( &d, 1 );
73         if ( ( d & 0xC0 ) == 0xC0 ) {
74                 packet.length = d & 0x3F;
75                 inputStream.read( &packet.data, 1 );
76         }
77         else
78         {
79                 packet.length = 1;
80                 packet.data = d;
81         }
82 }
83
84 void LoadPCXBuff( byte* buffer, std::size_t len, byte **pic, byte **palette, int *width, int *height ){
85         *pic = 0;
86
87         pcx_t pcx;
88         int x, y, lsize;
89         byte  *out, *pix;
90
91         /* parse the PCX file */
92
93         PointerInputStream inputStream( buffer );
94
95         pcx.manufacturer = istream_read_byte( inputStream );
96         pcx.version = istream_read_byte( inputStream );
97         pcx.encoding = istream_read_byte( inputStream );
98         pcx.bits_per_pixel = istream_read_byte( inputStream );
99         pcx.xmin = istream_read_int16_le( inputStream );
100         pcx.ymin = istream_read_int16_le( inputStream );
101         pcx.xmax = istream_read_int16_le( inputStream );
102         pcx.ymax = istream_read_int16_le( inputStream );
103         pcx.hres = istream_read_int16_le( inputStream );
104         pcx.vres = istream_read_int16_le( inputStream );
105         inputStream.read( pcx.palette, 48 );
106         pcx.reserved = istream_read_byte( inputStream );
107         pcx.color_planes = istream_read_byte( inputStream );
108         pcx.bytes_per_line = istream_read_int16_le( inputStream );
109         pcx.palette_type = istream_read_int16_le( inputStream );
110         inputStream.read( pcx.filler, 58 );
111
112
113         if ( pcx.manufacturer != 0x0a
114                  || pcx.version != 5
115                  || pcx.encoding != 1
116                  || pcx.bits_per_pixel != 8 ) {
117                 return;
118         }
119
120         if ( width ) {
121                 *width = pcx.xmax + 1;
122         }
123         if ( height ) {
124                 *height = pcx.ymax + 1;
125         }
126
127         if ( !pic ) {
128                 return;
129         }
130
131         out = (byte *)malloc( ( pcx.ymax + 1 ) * ( pcx.xmax + 1 ) );
132
133         *pic = out;
134         pix = out;
135
136         /* RR2DO2: pcx fix  */
137         lsize = pcx.color_planes * pcx.bytes_per_line;
138
139         /* go scanline by scanline */
140         for ( y = 0; y <= pcx.ymax; y++, pix += pcx.xmax + 1 )
141         {
142                 /* do a scanline */
143                 for ( x = 0; x <= pcx.xmax; )
144                 {
145                         /* RR2DO2 */
146                         PCXRLEPacket packet;
147                         ByteStream_readPCXRLEPacket( inputStream, packet );
148
149                         while ( packet.length-- > 0 )
150                         {
151                                 pix[ x++ ] = packet.data;
152                         }
153                 }
154
155                 /* RR2DO2: discard any other data */
156                 PCXRLEPacket packet;
157                 while ( x < lsize )
158                 {
159                         ByteStream_readPCXRLEPacket( inputStream, packet );
160                         x++;
161                 }
162                 while ( packet.length-- > 0 )
163                 {
164                         x++;
165                 }
166         }
167
168         /* validity check */
169         if ( std::size_t( inputStream.get() - buffer ) > len ) {
170                 *pic = 0;
171         }
172
173         if ( palette ) {
174                 *palette = (byte *)malloc( 768 );
175                 memcpy( *palette, buffer + len - 768, 768 );
176         }
177 }
178
179 /*
180    ==============
181    LoadPCX32
182    ==============
183  */
184 Image* LoadPCX32Buff( byte* buffer, std::size_t length ){
185         byte *palette;
186         byte *pic8;
187         int i, c, p, width, height;
188         byte *pic32;
189
190         LoadPCXBuff( buffer, length, &pic8, &palette, &width, &height );
191         if ( !pic8 ) {
192                 return 0;
193         }
194
195         RGBAImage* image = new RGBAImage( width, height );
196         c = ( width ) * ( height );
197         pic32 = image->getRGBAPixels();
198         for ( i = 0; i < c; i++ )
199         {
200                 p = pic8[i];
201                 pic32[0] = palette[p * 3];
202                 pic32[1] = palette[p * 3 + 1];
203                 pic32[2] = palette[p * 3 + 2];
204                 pic32[3] = 255;
205                 pic32 += 4;
206         }
207
208         free( pic8 );
209         free( palette );
210
211         return image;
212 }
213
214 Image* LoadPCX32( ArchiveFile& file ){
215         ScopedArchiveBuffer buffer( file );
216         return LoadPCX32Buff( buffer.buffer, buffer.length );
217 }