New comit of SDL2
[supertux.git] / src / SDL2 / IMG_pcx.c
1 /*
2   SDL_image:  An example image loading library for use with SDL
3   Copyright (C) 1997-2013 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 #include <stdio.h>
36 #include <stdlib.h>
37
38 #include "SDL_endian.h"
39
40 #include "SDL_image.h"
41
42 #ifdef LOAD_PCX
43
44 struct PCXheader {
45     Uint8 Manufacturer;
46     Uint8 Version;
47     Uint8 Encoding;
48     Uint8 BitsPerPixel;
49     Sint16 Xmin, Ymin, Xmax, Ymax;
50     Sint16 HDpi, VDpi;
51     Uint8 Colormap[48];
52     Uint8 Reserved;
53     Uint8 NPlanes;
54     Sint16 BytesPerLine;
55     Sint16 PaletteInfo;
56     Sint16 HscreenSize;
57     Sint16 VscreenSize;
58     Uint8 Filler[54];
59 };
60
61 /* See if an image is contained in a data source */
62 int IMG_isPCX(SDL_RWops *src)
63 {
64     Sint64 start;
65     int is_PCX;
66     const int ZSoft_Manufacturer = 10;
67     const int PC_Paintbrush_Version = 5;
68     const int PCX_Uncompressed_Encoding = 0;
69     const int PCX_RunLength_Encoding = 1;
70     struct PCXheader pcxh;
71
72     if ( !src )
73         return 0;
74     start = SDL_RWtell(src);
75     is_PCX = 0;
76     if ( SDL_RWread(src, &pcxh, sizeof(pcxh), 1) == 1 ) {
77         if ( (pcxh.Manufacturer == ZSoft_Manufacturer) &&
78              (pcxh.Version == PC_Paintbrush_Version) &&
79              (pcxh.Encoding == PCX_RunLength_Encoding ||
80               pcxh.Encoding == PCX_Uncompressed_Encoding) ) {
81             is_PCX = 1;
82         }
83     }
84     SDL_RWseek(src, start, RW_SEEK_SET);
85     return(is_PCX);
86 }
87
88 /* Load a PCX type image from an SDL datasource */
89 SDL_Surface *IMG_LoadPCX_RW(SDL_RWops *src)
90 {
91     Sint64 start;
92     struct PCXheader pcxh;
93     Uint32 Rmask;
94     Uint32 Gmask;
95     Uint32 Bmask;
96     Uint32 Amask;
97     SDL_Surface *surface = NULL;
98     int width, height;
99     int y, bpl;
100     Uint8 *row, *buf = NULL;
101     char *error = NULL;
102     int bits, src_bits;
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     /* Create the surface of the appropriate type */
121     width = (pcxh.Xmax - pcxh.Xmin) + 1;
122     height = (pcxh.Ymax - pcxh.Ymin) + 1;
123     Rmask = Gmask = Bmask = Amask = 0;
124     src_bits = pcxh.BitsPerPixel * pcxh.NPlanes;
125     if((pcxh.BitsPerPixel == 1 && pcxh.NPlanes >= 1 && pcxh.NPlanes <= 4)
126        || (pcxh.BitsPerPixel == 8 && pcxh.NPlanes == 1)) {
127         bits = 8;
128     } else if(pcxh.BitsPerPixel == 8 && pcxh.NPlanes == 3) {
129         bits = 24;
130 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
131             Rmask = 0x000000FF;
132             Gmask = 0x0000FF00;
133             Bmask = 0x00FF0000;
134 #else
135             Rmask = 0xFF0000;
136             Gmask = 0x00FF00;
137             Bmask = 0x0000FF;
138 #endif
139     } else {
140         error = "unsupported PCX format";
141         goto done;
142     }
143     surface = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height,
144                    bits, Rmask, Gmask, Bmask, Amask);
145     if ( surface == NULL )
146         goto done;
147
148     bpl = pcxh.NPlanes * pcxh.BytesPerLine;
149     if (bpl > surface->pitch) {
150         error = "bytes per line is too large (corrupt?)";
151     }
152     buf = (Uint8 *)SDL_malloc(bpl);
153     row = (Uint8 *)surface->pixels;
154     for ( y=0; y<surface->h; ++y ) {
155         /* decode a scan line to a temporary buffer first */
156         int i, count = 0;
157         Uint8 ch;
158         Uint8 *dst = (src_bits == 8) ? row : buf;
159         if ( pcxh.Encoding == 0 ) {
160             if(!SDL_RWread(src, dst, bpl, 1)) {
161                 error = "file truncated";
162                 goto done;
163             }
164         } else {
165             for(i = 0; i < bpl; i++) {
166                 if(!count) {
167                     if(!SDL_RWread(src, &ch, 1, 1)) {
168                         error = "file truncated";
169                         goto done;
170                     }
171                     if( (ch & 0xc0) == 0xc0) {
172                         count = ch & 0x3f;
173                         if(!SDL_RWread(src, &ch, 1, 1)) {
174                             error = "file truncated";
175                             goto done;
176                         }
177                     } else
178                         count = 1;
179                 }
180                 dst[i] = ch;
181                 count--;
182             }
183         }
184
185         if(src_bits <= 4) {
186             /* expand planes to 1 byte/pixel */
187             Uint8 *innerSrc = buf;
188             int plane;
189             for(plane = 0; plane < pcxh.NPlanes; plane++) {
190                 int j, k, x = 0;
191                 for(j = 0; j < pcxh.BytesPerLine; j++) {
192                     Uint8 byte = *innerSrc++;
193                     for(k = 7; k >= 0; k--) {
194                         unsigned bit = (byte >> k) & 1;
195                         /* skip padding bits */
196                         if (j * 8 + k >= width)
197                             continue;
198                         row[x++] |= bit << plane;
199                     }
200                 }
201             }
202         } else if(src_bits == 24) {
203             /* de-interlace planes */
204             Uint8 *innerSrc = buf;
205             int plane;
206             for(plane = 0; plane < pcxh.NPlanes; plane++) {
207                 int x;
208                 dst = row + plane;
209                 for(x = 0; x < width; x++) {
210                     *dst = *innerSrc++;
211                     dst += pcxh.NPlanes;
212                 }
213             }
214         }
215
216         row += surface->pitch;
217     }
218
219     if(bits == 8) {
220         SDL_Color *colors = surface->format->palette->colors;
221         int nc = 1 << src_bits;
222         int i;
223
224         surface->format->palette->ncolors = nc;
225         if(src_bits == 8) {
226             Uint8 ch;
227             /* look for a 256-colour palette */
228             do {
229                 if ( !SDL_RWread(src, &ch, 1, 1)) {
230                     error = "file truncated";
231                     goto done;
232                 }
233             } while ( ch != 12 );
234
235             for(i = 0; i < 256; i++) {
236                 SDL_RWread(src, &colors[i].r, 1, 1);
237                 SDL_RWread(src, &colors[i].g, 1, 1);
238                 SDL_RWread(src, &colors[i].b, 1, 1);
239             }
240         } else {
241             for(i = 0; i < nc; i++) {
242                 colors[i].r = pcxh.Colormap[i * 3];
243                 colors[i].g = pcxh.Colormap[i * 3 + 1];
244                 colors[i].b = pcxh.Colormap[i * 3 + 2];
245             }
246         }
247     }
248
249 done:
250     SDL_free(buf);
251     if ( error ) {
252         SDL_RWseek(src, start, RW_SEEK_SET);
253         if ( surface ) {
254             SDL_FreeSurface(surface);
255             surface = NULL;
256         }
257         IMG_SetError(error);
258     }
259     return(surface);
260 }
261
262 #else
263
264 /* See if an image is contained in a data source */
265 int IMG_isPCX(SDL_RWops *src)
266 {
267     return(0);
268 }
269
270 /* Load a PCX type image from an SDL datasource */
271 SDL_Surface *IMG_LoadPCX_RW(SDL_RWops *src)
272 {
273     return(NULL);
274 }
275
276 #endif /* LOAD_PCX */