1 /*         ______   ___    ___
2  *        /\  _  \ /\_ \  /\_ \
3  *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___
4  *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
5  *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
6  *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
7  *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
8  *                                           /\____/
9  *                                           \_/__/
10  *
11  *      PCX reader.
12  *
13  *      By Shawn Hargreaves.
14  *
15  *      See readme.txt for copyright information.
16  */
17 
18 
19 #include "allegro.h"
20 #include "allegro/internal/aintern.h"
21 
22 
23 
24 /* load_pcx:
25  *  Loads a 256 color PCX file, returning a bitmap structure and storing
26  *  the palette data in the specified palette (this should be an array of
27  *  at least 256 RGB structures).
28  */
load_pcx(AL_CONST char * filename,RGB * pal)29 BITMAP *load_pcx(AL_CONST char *filename, RGB *pal)
30 {
31    PACKFILE *f;
32    BITMAP *bmp;
33    ASSERT(filename);
34 
35    f = pack_fopen(filename, F_READ);
36    if (!f)
37       return NULL;
38 
39    bmp = load_pcx_pf(f, pal);
40 
41    pack_fclose(f);
42 
43    return bmp;
44 }
45 
46 
47 
48 /* load_pcx_pf:
49  *  Like load_pcx, but starts loading from the current place in the PACKFILE
50  *  specified. If successful the offset into the file will be left just after
51  *  the image data. If unsuccessful the offset into the file is unspecified,
52  *  i.e. you must either reset the offset to some known place or close the
53  *  packfile. The packfile is not closed by this function.
54  */
load_pcx_pf(PACKFILE * f,RGB * pal)55 BITMAP *load_pcx_pf(PACKFILE *f, RGB *pal)
56 {
57    BITMAP *b;
58    PALETTE tmppal;
59    int want_palette = TRUE;
60    int c;
61    int width, height;
62    int bpp, bytes_per_line;
63    int xx, po;
64    int x, y;
65    char ch;
66    int dest_depth;
67    ASSERT(f);
68 
69    /* we really need a palette */
70    if (!pal) {
71       want_palette = FALSE;
72       pal = tmppal;
73    }
74 
75    pack_getc(f);                    /* skip manufacturer ID */
76    pack_getc(f);                    /* skip version flag */
77    pack_getc(f);                    /* skip encoding flag */
78 
79    if (pack_getc(f) != 8) {         /* we like 8 bit color planes */
80       return NULL;
81    }
82 
83    width = -(pack_igetw(f));        /* xmin */
84    height = -(pack_igetw(f));       /* ymin */
85    width += pack_igetw(f) + 1;      /* xmax */
86    height += pack_igetw(f) + 1;     /* ymax */
87 
88    pack_igetl(f);                   /* skip DPI values */
89 
90    for (c=0; c<16; c++) {           /* read the 16 color palette */
91       pal[c].r = pack_getc(f) / 4;
92       pal[c].g = pack_getc(f) / 4;
93       pal[c].b = pack_getc(f) / 4;
94    }
95 
96    pack_getc(f);
97 
98    bpp = pack_getc(f) * 8;          /* how many color planes? */
99    if ((bpp != 8) && (bpp != 24)) {
100       return NULL;
101    }
102 
103    dest_depth = _color_load_depth(bpp, FALSE);
104    bytes_per_line = pack_igetw(f);
105 
106    for (c=0; c<60; c++)             /* skip some more junk */
107       pack_getc(f);
108 
109    b = create_bitmap_ex(bpp, width, height);
110    if (!b) {
111       return NULL;
112    }
113 
114    *allegro_errno = 0;
115 
116    for (y=0; y<height; y++) {       /* read RLE encoded PCX data */
117       x = xx = 0;
118 #ifdef ALLEGRO_LITTLE_ENDIAN
119       po = _rgb_r_shift_24/8;
120 #elif defined ALLEGRO_BIG_ENDIAN
121       po = 2 - _rgb_r_shift_24/8;
122 #elif !defined SCAN_DEPEND
123    #error endianess not defined
124 #endif
125 
126       while (x < bytes_per_line*bpp/8) {
127 	 ch = pack_getc(f);
128 	 if ((ch & 0xC0) == 0xC0) {
129 	    c = (ch & 0x3F);
130 	    ch = pack_getc(f);
131 	 }
132 	 else
133 	    c = 1;
134 
135 	 if (bpp == 8) {
136 	    while (c--) {
137 	       if (x < b->w)
138 		  b->line[y][x] = ch;
139 	       x++;
140 	    }
141 	 }
142 	 else {
143 	    while (c--) {
144 	       if (xx < b->w)
145 		  b->line[y][xx*3+po] = ch;
146 	       x++;
147 	       if (x == bytes_per_line) {
148 		  xx = 0;
149 #ifdef ALLEGRO_LITTLE_ENDIAN
150 		  po = _rgb_g_shift_24/8;
151 #elif defined ALLEGRO_BIG_ENDIAN
152 		  po = 2 - _rgb_g_shift_24/8;
153 #elif !defined SCAN_DEPEND
154    #error endianess not defined
155 #endif
156 	       }
157 	       else if (x == bytes_per_line*2) {
158 		  xx = 0;
159 #ifdef ALLEGRO_LITTLE_ENDIAN
160 		  po = _rgb_b_shift_24/8;
161 #elif defined ALLEGRO_BIG_ENDIAN
162 		  po = 2 - _rgb_b_shift_24/8;
163 #elif !defined SCAN_DEPEND
164    #error endianess not defined
165 #endif
166 	       }
167 	       else
168 		  xx++;
169 	    }
170 	 }
171       }
172    }
173 
174    if (bpp == 8) {                  /* look for a 256 color palette */
175       while ((c = pack_getc(f)) != EOF) {
176 	 if (c == 12) {
177 	    for (c=0; c<256; c++) {
178 	       pal[c].r = pack_getc(f) / 4;
179 	       pal[c].g = pack_getc(f) / 4;
180 	       pal[c].b = pack_getc(f) / 4;
181 	    }
182 	    break;
183 	 }
184       }
185    }
186 
187    if (*allegro_errno) {
188       destroy_bitmap(b);
189       return NULL;
190    }
191 
192    if (dest_depth != bpp) {
193       /* restore original palette except if it comes from the bitmap */
194       if ((bpp != 8) && (!want_palette))
195 	 pal = NULL;
196 
197       b = _fixup_loaded_bitmap(b, pal, dest_depth);
198    }
199 
200    /* construct a fake palette if 8-bit mode is not involved */
201    if ((bpp != 8) && (dest_depth != 8) && want_palette)
202       generate_332_palette(pal);
203 
204    return b;
205 }
206 
207 
208 
209 /* save_pcx:
210  *  Writes a bitmap into a PCX file, using the specified palette (this
211  *  should be an array of at least 256 RGB structures).
212  */
save_pcx(AL_CONST char * filename,BITMAP * bmp,AL_CONST RGB * pal)213 int save_pcx(AL_CONST char *filename, BITMAP *bmp, AL_CONST RGB *pal)
214 {
215    PACKFILE *f;
216    int ret;
217    ASSERT(filename);
218 
219    f = pack_fopen(filename, F_WRITE);
220    if (!f)
221       return -1;
222 
223    ret = save_pcx_pf(f, bmp, pal);
224 
225    pack_fclose(f);
226 
227    return ret;
228 }
229 
230 
231 
232 /* save_pcx_pf:
233  *  Like save_pcx but writes into the PACKFILE given instead of a new file.
234  *  The packfile is not closed after writing is completed. On success the
235  *  offset into the file is left after the TGA file just written. On failure
236  *  the offset is left at the end of whatever incomplete data was written.
237  */
save_pcx_pf(PACKFILE * f,BITMAP * bmp,AL_CONST RGB * pal)238 int save_pcx_pf(PACKFILE *f, BITMAP *bmp, AL_CONST RGB *pal)
239 {
240    PALETTE tmppal;
241    int c;
242    int x, y;
243    int runcount;
244    int depth, planes;
245    char runchar;
246    char ch;
247    ASSERT(f);
248    ASSERT(bmp);
249 
250    if (!pal) {
251       get_palette(tmppal);
252       pal = tmppal;
253    }
254 
255    depth = bitmap_color_depth(bmp);
256    if (depth == 8)
257       planes = 1;
258    else
259       planes = 3;
260 
261    *allegro_errno = 0;
262 
263    pack_putc(10, f);                      /* manufacturer */
264    pack_putc(5, f);                       /* version */
265    pack_putc(1, f);                       /* run length encoding  */
266    pack_putc(8, f);                       /* 8 bits per pixel */
267    pack_iputw(0, f);                      /* xmin */
268    pack_iputw(0, f);                      /* ymin */
269    pack_iputw(bmp->w-1, f);               /* xmax */
270    pack_iputw(bmp->h-1, f);               /* ymax */
271    pack_iputw(320, f);                    /* HDpi */
272    pack_iputw(200, f);                    /* VDpi */
273 
274    for (c=0; c<16; c++) {
275       pack_putc(_rgb_scale_6[pal[c].r], f);
276       pack_putc(_rgb_scale_6[pal[c].g], f);
277       pack_putc(_rgb_scale_6[pal[c].b], f);
278    }
279 
280    pack_putc(0, f);                       /* reserved */
281    pack_putc(planes, f);                  /* one or three color planes */
282    pack_iputw(bmp->w, f);                 /* number of bytes per scanline */
283    pack_iputw(1, f);                      /* color palette */
284    pack_iputw(bmp->w, f);                 /* hscreen size */
285    pack_iputw(bmp->h, f);                 /* vscreen size */
286    for (c=0; c<54; c++)                   /* filler */
287       pack_putc(0, f);
288 
289    for (y=0; y<bmp->h; y++) {             /* for each scanline... */
290       runcount = 0;
291       runchar = 0;
292       for (x=0; x<bmp->w*planes; x++) {   /* for each pixel... */
293 	 if (depth == 8) {
294 	    ch = getpixel(bmp, x, y);
295 	 }
296 	 else {
297 	    if (x<bmp->w) {
298 	       c = getpixel(bmp, x, y);
299 	       ch = getr_depth(depth, c);
300 	    }
301 	    else if (x<bmp->w*2) {
302 	       c = getpixel(bmp, x-bmp->w, y);
303 	       ch = getg_depth(depth, c);
304 	    }
305 	    else {
306 	       c = getpixel(bmp, x-bmp->w*2, y);
307 	       ch = getb_depth(depth, c);
308 	    }
309 	 }
310 	 if (runcount==0) {
311 	    runcount = 1;
312 	    runchar = ch;
313 	 }
314 	 else {
315 	    if ((ch != runchar) || (runcount >= 0x3f)) {
316 	       if ((runcount > 1) || ((runchar & 0xC0) == 0xC0))
317 		  pack_putc(0xC0 | runcount, f);
318 	       pack_putc(runchar,f);
319 	       runcount = 1;
320 	       runchar = ch;
321 	    }
322 	    else
323 	       runcount++;
324 	 }
325       }
326       if ((runcount > 1) || ((runchar & 0xC0) == 0xC0))
327 	 pack_putc(0xC0 | runcount, f);
328       pack_putc(runchar,f);
329    }
330 
331    if (depth == 8) {                      /* 256 color palette */
332       pack_putc(12, f);
333 
334       for (c=0; c<256; c++) {
335 	 pack_putc(_rgb_scale_6[pal[c].r], f);
336 	 pack_putc(_rgb_scale_6[pal[c].g], f);
337 	 pack_putc(_rgb_scale_6[pal[c].b], f);
338       }
339    }
340 
341    if (*allegro_errno)
342       return -1;
343    else
344       return 0;
345 }
346 
347