1 
2 #include <string.h>
3 #include <stdlib.h>
4 #include <SDL2/SDL_rwops.h>
5 #include <SDL2/SDL_endian.h>
6 
7 #include <png.h>
8 #include <SDL2/SDL_pixels.h>
9 
10 #include "image.h"
11 
12 
13 /// png and pcx code partially taken from SDL_Image, partialy from github and forums.
14 /* Load a PNG type image from an SDL datasource */
png_read_data(png_structp ctx,png_bytep area,png_size_t size)15 static void png_read_data(png_structp ctx, png_bytep area, png_size_t size)
16 {
17     SDL_RWops *src = (SDL_RWops *)png_get_io_ptr(ctx);
18     SDL_RWread(src, area, size, 1);
19 }
20 
Image_LoadPNG(const char * file_name,uint8_t ** buffer,uint32_t * w,uint32_t * h,uint32_t * bpp)21 static int Image_LoadPNG(const char *file_name, uint8_t **buffer, uint32_t *w, uint32_t *h, uint32_t *bpp)
22 {
23     png_byte png_header[8];    // 8 is the maximum size that can be checked
24 
25     /* open file and test for it being a png */
26     SDL_RWops *src = SDL_RWFromFile(file_name, "rb");
27     if(!src)
28     {
29         return 0;
30     }
31 
32     SDL_RWread(src, png_header, 1, 8);
33     if(png_sig_cmp(png_header, 0, 8))
34     {
35         SDL_RWclose(src);
36         return 0;
37     }
38 
39     SDL_RWseek(src, 0, RW_SEEK_SET);
40     /* initialize stuff */
41     png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
42 
43     if(!png_ptr)
44     {
45         SDL_RWclose(src);
46         return 0;
47     }
48 
49     png_infop read_info_ptr = png_create_info_struct(png_ptr);
50     png_infop end_info_ptr = png_create_info_struct(png_ptr);
51     if(!read_info_ptr || !end_info_ptr)
52     {
53         png_destroy_read_struct(&png_ptr, &read_info_ptr, &end_info_ptr);
54         SDL_RWclose(src);
55         return 0;
56     }
57 
58     if(setjmp(png_jmpbuf(png_ptr)))
59     {
60         png_destroy_read_struct(&png_ptr, &read_info_ptr, &end_info_ptr);
61         SDL_RWclose(src);
62         return 0;
63     }
64 
65     png_set_read_fn(png_ptr, src, png_read_data);
66     png_read_info(png_ptr, read_info_ptr);
67 
68     *w = png_get_image_width(png_ptr, read_info_ptr);
69     *h = png_get_image_height(png_ptr, read_info_ptr);
70     int color_type = png_get_color_type(png_ptr, read_info_ptr);
71 
72     /* tell libpng to strip 16 bit/color files down to 8 bits/color */
73     png_set_strip_16(png_ptr) ;
74 
75     /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single
76      * byte into separate bytes (useful for paletted and grayscale images).
77      */
78     png_set_packing(png_ptr);
79 
80     /* scale greyscale values to the range 0..255 */
81     if(color_type == PNG_COLOR_TYPE_GRAY)
82     {
83         png_set_expand(png_ptr);
84     }
85 
86     /* For images with a single "transparent colour", set colour key;
87        if more than one index has transparency, or if partially transparent
88        entries exist, use full alpha channel */
89     if(png_get_valid(png_ptr, read_info_ptr, PNG_INFO_tRNS))
90     {
91         png_color_16 *transv = NULL;
92         int num_trans;
93         uint8_t *trans;
94         png_get_tRNS(png_ptr, read_info_ptr, &trans, &num_trans, &transv);
95         if(color_type == PNG_COLOR_TYPE_PALETTE)
96         {
97             /* Check if all tRNS entries are opaque except one */
98             int j, t = -1;
99             for(j = 0; j < num_trans; j++)
100             {
101                 if(trans[j] == 0)
102                 {
103                     if(t >= 0)
104                     {
105                         break;
106                     }
107                     t = j;
108                 }
109                 else if(trans[j] != 255)
110                 {
111                     break;
112                 }
113             }
114             if(j != num_trans)
115             {
116                 /* more than one transparent index, or translucency */
117                 png_set_expand(png_ptr);
118             }
119         }
120     }
121 
122     if(color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
123     {
124         png_set_gray_to_rgb(png_ptr);
125     }
126 
127     //int number_of_passes = png_set_interlace_handling(png_ptr);
128     png_read_update_info(png_ptr, read_info_ptr);
129     color_type = png_get_color_type(png_ptr, read_info_ptr);
130 
131     if(color_type == (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA))
132     {
133         *bpp = 8 * 4;
134     }
135     else if(color_type == PNG_COLOR_MASK_COLOR)
136     {
137         *bpp = 8 * 3;
138     }
139 
140     /* read file */
141     if(setjmp(png_jmpbuf(png_ptr)))
142     {
143         png_destroy_read_struct(&png_ptr, &read_info_ptr, &end_info_ptr);
144         SDL_RWclose(src);
145         return 0;
146     }
147 
148     png_bytep *row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * (*h));
149     int row_bytes = png_get_rowbytes(png_ptr, read_info_ptr);
150     for(uint32_t y = 0; y < (*h); y++)
151     {
152         row_pointers[y] = (png_byte*)malloc(row_bytes);
153     }
154 
155     png_read_image(png_ptr, row_pointers);
156 
157     *buffer = (uint8_t*)malloc(row_bytes * (*h));
158     uint8_t *p = *buffer;
159     for(uint32_t y = 0; y < (*h); y++)
160     {
161         memcpy(p, row_pointers[y], row_bytes);
162         p += row_bytes;
163     }
164 
165     for(uint32_t y = 0; y < (*h); y++)
166     {
167         free(row_pointers[y]);
168     }
169     free(row_pointers);
170 
171     png_destroy_read_struct(&png_ptr, &read_info_ptr, &end_info_ptr);
172     SDL_RWclose(src);
173     return 1;
174 }
175 
176 
177 struct PCXheader
178 {
179     uint8_t Manufacturer;
180     uint8_t Version;
181     uint8_t Encoding;
182     uint8_t BitsPerPixel;
183     int16_t Xmin, Ymin, Xmax, Ymax;
184     int16_t HDpi, VDpi;
185     uint8_t Colormap[48];
186     uint8_t Reserved;
187     uint8_t NPlanes;
188     int16_t BytesPerLine;
189     int16_t PaletteInfo;
190     int16_t HscreenSize;
191     int16_t VscreenSize;
192     uint8_t Filler[54];
193 };
194 
195 /* See if an image is contained in a data source */
IMG_isPCX(SDL_RWops * src)196 int IMG_isPCX(SDL_RWops *src)
197 {
198     int64_t start;
199     int is_PCX = 0;
200     const int ZSoft_Manufacturer = 10;
201     const int PC_Paintbrush_Version = 5;
202     const int PCX_Uncompressed_Encoding = 0;
203     const int PCX_RunLength_Encoding = 1;
204     struct PCXheader pcxh;
205     start = SDL_RWtell(src);
206 
207     if(SDL_RWread(src, &pcxh, sizeof(pcxh), 1) == 1)
208     {
209         if ( (pcxh.Manufacturer == ZSoft_Manufacturer) &&
210              (pcxh.Version == PC_Paintbrush_Version) &&
211              (pcxh.Encoding == PCX_RunLength_Encoding ||
212               pcxh.Encoding == PCX_Uncompressed_Encoding) )
213         {
214             is_PCX = 1;
215         }
216     }
217     SDL_RWseek(src, start, RW_SEEK_SET);
218     return is_PCX;
219 }
220 
221 /* Load a PCX type image from an SDL datasource */
Image_LoadPCX(const char * file_name,uint8_t ** buffer,uint32_t * w,uint32_t * h,uint32_t * bpp)222 static int Image_LoadPCX(const char *file_name, uint8_t **buffer, uint32_t *w, uint32_t *h, uint32_t *bpp)
223 {
224     struct PCXheader pcxh;
225     uint32_t y, bpl;
226     int bits, src_bits;
227     SDL_RWops *src = SDL_RWFromFile(file_name, "rb");
228 
229     if(!src)
230     {
231         /* The error message has been set in SDL_RWFromFile */
232         return 0;
233     }
234 
235     if(!IMG_isPCX(src))
236     {
237         SDL_RWclose(src);
238         return 0;
239     }
240 
241     if(!SDL_RWread(src, &pcxh, sizeof(pcxh), 1))
242     {
243         SDL_RWclose(src);
244         return 0;
245     }
246     *bpp = 24;
247     pcxh.Xmin = SDL_SwapLE16(pcxh.Xmin);
248     pcxh.Ymin = SDL_SwapLE16(pcxh.Ymin);
249     pcxh.Xmax = SDL_SwapLE16(pcxh.Xmax);
250     pcxh.Ymax = SDL_SwapLE16(pcxh.Ymax);
251     pcxh.BytesPerLine = SDL_SwapLE16(pcxh.BytesPerLine);
252 
253     *w = (pcxh.Xmax - pcxh.Xmin) + 1;
254     *h = (pcxh.Ymax - pcxh.Ymin) + 1;
255     src_bits = pcxh.BitsPerPixel * pcxh.NPlanes;
256     if((pcxh.BitsPerPixel == 1 && pcxh.NPlanes >= 1 && pcxh.NPlanes <= 4)
257        || (pcxh.BitsPerPixel == 8 && pcxh.NPlanes == 1))
258     {
259         bits = 8;
260     }
261     else if(pcxh.BitsPerPixel == 8 && pcxh.NPlanes == 3)
262     {
263         bits = 24;
264     }
265     else
266     {
267         SDL_RWclose(src);
268         return 0;
269     }
270 
271     bpl = pcxh.NPlanes * pcxh.BytesPerLine;
272     uint8_t *orig_pixels = (uint8_t *)malloc(bpl * (*h));
273     uint8_t *temp_line = (uint8_t *)malloc(bpl);
274     uint8_t *row = orig_pixels;
275     for(y = 0; y < (*h); ++y)
276     {
277         /* decode a scan line to a temporary buffer first */
278         uint32_t i, count = 0;
279         uint8_t ch;
280         uint8_t *dst = (src_bits == 8) ? row : temp_line;
281         if(pcxh.Encoding == 0)
282         {
283             if(!SDL_RWread(src, dst, bpl, 1))
284             {
285                 free(temp_line);
286                 free(orig_pixels);
287                 SDL_RWclose(src);
288                 return 0;
289             }
290         }
291         else
292         {
293             for(i = 0; i < bpl; i++)
294             {
295                 if(!count)
296                 {
297                     if(!SDL_RWread(src, &ch, 1, 1))
298                     {
299                         free(temp_line);
300                         free(orig_pixels);
301                         SDL_RWclose(src);
302                         return 0;
303                     }
304                     if((ch & 0xc0) == 0xc0)
305                     {
306                         count = ch & 0x3f;
307                         if(!SDL_RWread(src, &ch, 1, 1))
308                         {
309                             free(temp_line);
310                             free(orig_pixels);
311                             SDL_RWclose(src);
312                             return 0;
313                         }
314                     }
315                     else
316                     {
317                         count = 1;
318                     }
319                 }
320                 dst[i] = ch;
321                 count--;
322             }
323         }
324 
325         if(src_bits <= 4)
326         {
327             /* expand planes to 1 byte/pixel */
328             uint8_t *innerSrc = temp_line;
329             int plane;
330             for(plane = 0; plane < pcxh.NPlanes; plane++)
331             {
332                 uint32_t j, x = 0;
333                 for(j = 0; j < pcxh.BytesPerLine; j++)
334                 {
335                     uint8_t byte = *innerSrc++;
336                     int sk = 7;
337                     for(; sk >= 0; sk--)
338                     {
339                         unsigned bit = (byte >> sk) & 1;
340                         /* skip padding bits */
341                         if(j * 8 + sk >= (*w))
342                         {
343                             continue;
344                         }
345                         row[x++] |= bit << plane;
346                     }
347                 }
348             }
349         }
350         else if(src_bits == 24)
351         {
352             /* de-interlace planes */
353             uint8_t *innerSrc = temp_line;
354             int plane;
355             for(plane = 0; plane < pcxh.NPlanes; plane++)
356             {
357                 uint32_t x;
358                 dst = row + plane;
359                 for(x = 0; x < (*w); x++)
360                 {
361                     *dst = *innerSrc++;
362                     dst += pcxh.NPlanes;
363                 }
364             }
365         }
366 
367         row += bpl;
368     }
369     free(temp_line);
370 
371     if(bits == 8)
372     {
373         int i;
374         int nc = 1 << src_bits;
375         SDL_Color *colors = (SDL_Color*)malloc(sizeof(SDL_Color) * nc);
376 
377         if(src_bits == 8)
378         {
379             uint8_t ch;
380             /* look for a 256-colour palette */
381             do
382             {
383                 if(!SDL_RWread(src, &ch, 1, 1))
384                 {
385                     free(colors);
386                     free(orig_pixels);
387                     SDL_RWclose(src);
388                     return 0;
389                 }
390             } while(ch != 12);
391 
392             for(i = 0; i < 256; i++)
393             {
394                 SDL_RWread(src, &colors[i].r, 1, 1);
395                 SDL_RWread(src, &colors[i].g, 1, 1);
396                 SDL_RWread(src, &colors[i].b, 1, 1);
397             }
398         }
399         else
400         {
401             for(i = 0; i < nc; i++)
402             {
403                 colors[i].r = pcxh.Colormap[i * 3];
404                 colors[i].g = pcxh.Colormap[i * 3 + 1];
405                 colors[i].b = pcxh.Colormap[i * 3 + 2];
406             }
407         }
408 
409         *buffer = (uint8_t *)malloc((*w) * (*h) * 3);
410         uint8_t *dst_row = *buffer;
411         uint8_t *src_row = orig_pixels;
412         for(y = 0; y < (*h); ++y)
413         {
414             for(uint32_t x = 0; x < *w; x++)
415             {
416                 if(src_row[x] < nc)
417                 {
418                     dst_row[x * 3 + 0] = colors[src_row[x]].r;
419                     dst_row[x * 3 + 1] = colors[src_row[x]].g;
420                     dst_row[x * 3 + 2] = colors[src_row[x]].b;
421                 }
422                 else
423                 {
424                     dst_row[x * 3 + 0] = 0;
425                     dst_row[x * 3 + 1] = 0;
426                     dst_row[x * 3 + 2] = 0;
427                 }
428             }
429             src_row += bpl;
430             dst_row += (*w) * 3;
431         }
432         free(colors);
433         free(orig_pixels);
434     }
435     else if(bits == 24)
436     {
437         *buffer = orig_pixels;
438     }
439 
440     SDL_RWclose(src);
441     return 1;
442 }
443 
444 
Image_Load(const char * file_name,int format,uint8_t ** buffer,uint32_t * w,uint32_t * h,uint32_t * bpp)445 int Image_Load(const char *file_name, int format, uint8_t **buffer, uint32_t *w, uint32_t *h, uint32_t *bpp)
446 {
447     switch(format)
448     {
449         case IMAGE_FORMAT_PNG:
450             return Image_LoadPNG(file_name, buffer, w, h, bpp);
451 
452         case IMAGE_FORMAT_PCX:
453             return Image_LoadPCX(file_name, buffer, w, h, bpp);
454 
455         default:
456             return 0;
457     }
458 }
459 
460 //------------------------------------------------------------------------------
461 
png_write_data(png_structp ctx,png_bytep area,png_size_t size)462 static void png_write_data(png_structp ctx, png_bytep area, png_size_t size)
463 {
464     SDL_RWops *dst = (SDL_RWops *)png_get_io_ptr(ctx);
465     SDL_RWwrite(dst, area, size, 1);
466 }
467 
468 
png_flush_data(png_structp ctx)469 static void png_flush_data(png_structp ctx)
470 {
471 }
472 
473 
Image_SavePNG(const char * file_name,uint8_t * buffer,uint32_t w,uint32_t h,uint32_t bpp)474 static int Image_SavePNG(const char *file_name, uint8_t *buffer, uint32_t w, uint32_t h, uint32_t bpp)
475 {
476     SDL_RWops *dst = SDL_RWFromFile(file_name, "wb");
477     int cell_size;
478     int png_color_type;
479 
480     if(!dst)
481     {
482         return 0;
483     }
484 
485     if(bpp == 24)
486     {
487         png_color_type = PNG_COLOR_TYPE_RGB;
488         cell_size = 3;
489     }
490     else if(bpp == 32)
491     {
492         png_color_type = PNG_COLOR_TYPE_RGBA;
493         cell_size = 4;
494     }
495     else
496     {
497         SDL_RWclose(dst);
498         return 0;
499     }
500 
501     png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
502     if(png_ptr == NULL)
503     {
504         SDL_RWclose(dst);
505         return 0;
506     }
507 
508     png_infop info_ptr = png_create_info_struct(png_ptr);
509     if(info_ptr == NULL)
510     {
511         png_destroy_write_struct(&png_ptr, NULL);
512         SDL_RWclose(dst);
513         return 0;
514     }
515 
516     if(setjmp(png_jmpbuf(png_ptr)))
517     {
518         png_destroy_write_struct(&png_ptr, &info_ptr);
519         SDL_RWclose(dst);
520         return 0;
521     }
522 
523     //png_set_compression_level(png_ptr, 9);
524     png_set_write_fn(png_ptr, dst, png_write_data, png_flush_data);
525 
526     png_set_IHDR(png_ptr, info_ptr, w, h,
527          8, png_color_type, PNG_INTERLACE_NONE,
528          PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
529 
530     png_write_info(png_ptr, info_ptr);
531 
532     // Write image data
533     for(uint32_t y = 0 ; y < h ; y++)
534     {
535         png_bytep row = (png_bytep)(buffer + (h - y - 1) * w * cell_size);
536         png_write_row(png_ptr, row);
537     }
538 
539     // End write
540     png_write_end(png_ptr, NULL);
541     SDL_RWclose(dst);
542 
543     png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
544     png_destroy_write_struct(&png_ptr, &info_ptr);
545 
546     return 1;
547 }
548 
549 
Image_Save(const char * file_name,int format,uint8_t * buffer,uint32_t w,uint32_t h,uint32_t bpp)550 int Image_Save(const char *file_name, int format, uint8_t *buffer, uint32_t w, uint32_t h, uint32_t bpp)
551 {
552     switch(format)
553     {
554         case IMAGE_FORMAT_PNG:
555             return Image_SavePNG(file_name, buffer, w, h, bpp);
556 
557         default:
558             return 0;
559     }
560 }
561