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