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