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