1 /*
2 SDL_image: An example image loading library for use with SDL
3 Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20 */
21
22 /*
23 * PCX file reader:
24 * Supports:
25 * 1..4 bits/pixel in multiplanar format (1 bit/plane/pixel)
26 * 8 bits/pixel in single-planar format (8 bits/plane/pixel)
27 * 24 bits/pixel in 3-plane format (8 bits/plane/pixel)
28 *
29 * (The <8bpp formats are expanded to 8bpp surfaces)
30 *
31 * Doesn't support:
32 * single-planar packed-pixel formats other than 8bpp
33 * 4-plane 32bpp format with a fourth "intensity" plane
34 */
35
36 #include "SDL_endian.h"
37
38 #include "SDL_image.h"
39
40 #ifdef LOAD_PCX
41
42 struct PCXheader {
43 Uint8 Manufacturer;
44 Uint8 Version;
45 Uint8 Encoding;
46 Uint8 BitsPerPixel;
47 Sint16 Xmin, Ymin, Xmax, Ymax;
48 Sint16 HDpi, VDpi;
49 Uint8 Colormap[48];
50 Uint8 Reserved;
51 Uint8 NPlanes;
52 Sint16 BytesPerLine;
53 Sint16 PaletteInfo;
54 Sint16 HscreenSize;
55 Sint16 VscreenSize;
56 Uint8 Filler[54];
57 };
58
59 /* See if an image is contained in a data source */
IMG_isPCX(SDL_RWops * src)60 int IMG_isPCX(SDL_RWops *src)
61 {
62 Sint64 start;
63 int is_PCX;
64 const int ZSoft_Manufacturer = 10;
65 const int PC_Paintbrush_Version = 5;
66 const int PCX_Uncompressed_Encoding = 0;
67 const int PCX_RunLength_Encoding = 1;
68 struct PCXheader pcxh;
69
70 if ( !src )
71 return 0;
72 start = SDL_RWtell(src);
73 is_PCX = 0;
74 if ( SDL_RWread(src, &pcxh, sizeof(pcxh), 1) == 1 ) {
75 if ( (pcxh.Manufacturer == ZSoft_Manufacturer) &&
76 (pcxh.Version == PC_Paintbrush_Version) &&
77 (pcxh.Encoding == PCX_RunLength_Encoding ||
78 pcxh.Encoding == PCX_Uncompressed_Encoding) ) {
79 is_PCX = 1;
80 }
81 }
82 SDL_RWseek(src, start, RW_SEEK_SET);
83 return(is_PCX);
84 }
85
86 /* Load a PCX type image from an SDL datasource */
IMG_LoadPCX_RW(SDL_RWops * src)87 SDL_Surface *IMG_LoadPCX_RW(SDL_RWops *src)
88 {
89 Sint64 start;
90 struct PCXheader pcxh;
91 Uint32 Rmask;
92 Uint32 Gmask;
93 Uint32 Bmask;
94 Uint32 Amask;
95 SDL_Surface *surface = NULL;
96 int width, height;
97 int y, bpl;
98 Uint8 *row, *buf = NULL;
99 char *error = NULL;
100 int bits, src_bits;
101 int count = 0;
102 Uint8 ch;
103
104 if ( !src ) {
105 /* The error message has been set in SDL_RWFromFile */
106 return NULL;
107 }
108 start = SDL_RWtell(src);
109
110 if ( !SDL_RWread(src, &pcxh, sizeof(pcxh), 1) ) {
111 error = "file truncated";
112 goto done;
113 }
114 pcxh.Xmin = SDL_SwapLE16(pcxh.Xmin);
115 pcxh.Ymin = SDL_SwapLE16(pcxh.Ymin);
116 pcxh.Xmax = SDL_SwapLE16(pcxh.Xmax);
117 pcxh.Ymax = SDL_SwapLE16(pcxh.Ymax);
118 pcxh.BytesPerLine = SDL_SwapLE16(pcxh.BytesPerLine);
119
120 #if 0
121 printf("Manufacturer = %d\n", pcxh.Manufacturer);
122 printf("Version = %d\n", pcxh.Version);
123 printf("Encoding = %d\n", pcxh.Encoding);
124 printf("BitsPerPixel = %d\n", pcxh.BitsPerPixel);
125 printf("Xmin = %d, Ymin = %d, Xmax = %d, Ymax = %d\n", pcxh.Xmin, pcxh.Ymin, pcxh.Xmax, pcxh.Ymax);
126 printf("HDpi = %d, VDpi = %d\n", pcxh.HDpi, pcxh.VDpi);
127 printf("NPlanes = %d\n", pcxh.NPlanes);
128 printf("BytesPerLine = %d\n", pcxh.BytesPerLine);
129 printf("PaletteInfo = %d\n", pcxh.PaletteInfo);
130 printf("HscreenSize = %d\n", pcxh.HscreenSize);
131 printf("VscreenSize = %d\n", pcxh.VscreenSize);
132 #endif
133
134 /* Create the surface of the appropriate type */
135 width = (pcxh.Xmax - pcxh.Xmin) + 1;
136 height = (pcxh.Ymax - pcxh.Ymin) + 1;
137 Rmask = Gmask = Bmask = Amask = 0;
138 src_bits = pcxh.BitsPerPixel * pcxh.NPlanes;
139 if((pcxh.BitsPerPixel == 1 && pcxh.NPlanes >= 1 && pcxh.NPlanes <= 4)
140 || (pcxh.BitsPerPixel == 8 && pcxh.NPlanes == 1)) {
141 bits = 8;
142 } else if(pcxh.BitsPerPixel == 8 && pcxh.NPlanes == 3) {
143 bits = 24;
144 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
145 Rmask = 0x000000FF;
146 Gmask = 0x0000FF00;
147 Bmask = 0x00FF0000;
148 #else
149 Rmask = 0xFF0000;
150 Gmask = 0x00FF00;
151 Bmask = 0x0000FF;
152 #endif
153 } else {
154 error = "unsupported PCX format";
155 goto done;
156 }
157 surface = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height,
158 bits, Rmask, Gmask, Bmask, Amask);
159 if ( surface == NULL ) {
160 goto done;
161 }
162
163 bpl = pcxh.NPlanes * pcxh.BytesPerLine;
164 buf = (Uint8 *)SDL_calloc(bpl, 1);
165 if ( !buf ) {
166 error = "Out of memory";
167 goto done;
168 }
169 row = (Uint8 *)surface->pixels;
170 for ( y=0; y<surface->h; ++y ) {
171 /* decode a scan line to a temporary buffer first */
172 int i;
173 if ( pcxh.Encoding == 0 ) {
174 if ( !SDL_RWread(src, buf, bpl, 1) ) {
175 error = "file truncated";
176 goto done;
177 }
178 } else {
179 for ( i = 0; i < bpl; i++ ) {
180 if ( !count ) {
181 if ( !SDL_RWread(src, &ch, 1, 1) ) {
182 error = "file truncated";
183 goto done;
184 }
185 if ( ch < 0xc0 ) {
186 count = 1;
187 } else {
188 count = ch - 0xc0;
189 if( !SDL_RWread(src, &ch, 1, 1) ) {
190 error = "file truncated";
191 goto done;
192 }
193 }
194 }
195 buf[i] = ch;
196 count--;
197 }
198 }
199
200 if ( src_bits <= 4 ) {
201 /* expand planes to 1 byte/pixel */
202 Uint8 *innerSrc = buf;
203 int plane;
204 for ( plane = 0; plane < pcxh.NPlanes; plane++ ) {
205 int j, k, x = 0;
206 for( j = 0; j < pcxh.BytesPerLine; j++ ) {
207 Uint8 byte = *innerSrc++;
208 for( k = 7; k >= 0; k-- ) {
209 unsigned bit = (byte >> k) & 1;
210 /* skip padding bits */
211 if (j * 8 + k >= width)
212 continue;
213 row[x++] |= bit << plane;
214 }
215 }
216 }
217 } else if ( src_bits == 8 ) {
218 /* Copy the row directly */
219 SDL_memcpy(row, buf, SDL_min(width, bpl));
220 } else if ( src_bits == 24 ) {
221 /* de-interlace planes */
222 Uint8 *innerSrc = buf;
223 int plane;
224 for ( plane = 0; plane < pcxh.NPlanes; plane++ ) {
225 int x;
226 Uint8 *dst = row + plane;
227 for ( x = 0; x < width; x++ ) {
228 if ( dst >= row+surface->pitch ) {
229 error = "decoding out of bounds (corrupt?)";
230 goto done;
231 }
232 *dst = *innerSrc++;
233 dst += pcxh.NPlanes;
234 }
235 }
236 }
237
238 row += surface->pitch;
239 }
240
241 if ( bits == 8 ) {
242 SDL_Color *colors = surface->format->palette->colors;
243 int nc = 1 << src_bits;
244 int i;
245
246 surface->format->palette->ncolors = nc;
247 if ( src_bits == 8 ) {
248 Uint8 ch;
249 /* look for a 256-colour palette */
250 do {
251 if ( !SDL_RWread(src, &ch, 1, 1) ) {
252 /* Couldn't find the palette, try the end of the file */
253 SDL_RWseek(src, -768, RW_SEEK_END);
254 break;
255 }
256 } while ( ch != 12 );
257
258 for ( i = 0; i < 256; i++ ) {
259 SDL_RWread(src, &colors[i].r, 1, 1);
260 SDL_RWread(src, &colors[i].g, 1, 1);
261 SDL_RWread(src, &colors[i].b, 1, 1);
262 }
263 } else {
264 for ( i = 0; i < nc; i++ ) {
265 colors[i].r = pcxh.Colormap[i * 3];
266 colors[i].g = pcxh.Colormap[i * 3 + 1];
267 colors[i].b = pcxh.Colormap[i * 3 + 2];
268 }
269 }
270 }
271
272 done:
273 SDL_free(buf);
274 if ( error ) {
275 SDL_RWseek(src, start, RW_SEEK_SET);
276 if ( surface ) {
277 SDL_FreeSurface(surface);
278 surface = NULL;
279 }
280 IMG_SetError("%s", error);
281 }
282 return(surface);
283 }
284
285 #else
286
287 /* See if an image is contained in a data source */
IMG_isPCX(SDL_RWops * src)288 int IMG_isPCX(SDL_RWops *src)
289 {
290 return(0);
291 }
292
293 /* Load a PCX type image from an SDL datasource */
IMG_LoadPCX_RW(SDL_RWops * src)294 SDL_Surface *IMG_LoadPCX_RW(SDL_RWops *src)
295 {
296 return(NULL);
297 }
298
299 #endif /* LOAD_PCX */
300