1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
|
/*
* Copyright 2017 DiUS Computing Pty Ltd. All rights reserved.
*
* Released under GPLv3, see LICENSE for details.
*
* @author Johny Mattsson <jmattsson@dius.com.au>
*/
#include "loadpng.h"
#include <stdlib.h>
#include <assert.h>
#include <png.h>
static_assert(sizeof(png_byte) == sizeof(((ql_raster_image_t *) 0)->data[0]),
"Code relies on png_byte being compatible with ql_raster_image_t data ");
ql_raster_image_t *loadpng(const char *path)
{
ql_raster_image_t *ret = NULL;
if (!path)
goto out;
FILE *f = fopen(path, "rb");
if (!f)
goto out;
uint8_t header[8];
if (fread(header, 1, sizeof(header), f) != 8)
goto close_out;
if (!png_check_sig(header, sizeof(header)))
goto close_out;
png_infop info_ptr = NULL, end_ptr = NULL;
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr)
goto close_out;
info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr)
goto destroy_read_out;
end_ptr = png_create_info_struct(png_ptr);
if (!end_ptr)
goto destroy_read_out;
if (setjmp(png_jmpbuf(png_ptr)))
goto destroy_read_out;
png_init_io(png_ptr, f);
png_set_sig_bytes(png_ptr, sizeof(header));
png_read_info(png_ptr, info_ptr);
png_uint_32 width, height;
int bit_depth, color_type;
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL);
if (color_type & PNG_COLOR_MASK_ALPHA)
png_set_strip_alpha(png_ptr);
if (color_type & (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE))
png_set_rgb_to_gray_fixed(png_ptr, 1, -1, -1); // force into grayscale
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
png_set_expand_gray_1_2_4_to_8(png_ptr); // get us a known output format
png_read_update_info(png_ptr, info_ptr);
png_bytepp row_ptrs = calloc(height, sizeof(png_bytep));
if (!row_ptrs)
goto destroy_read_out;
const unsigned row_bytes = width * sizeof(png_byte);
ql_raster_image_t *img = calloc(1, sizeof(ql_raster_image_t) + height * row_bytes);
if (!img)
goto free_image_out;
if (setjmp(png_jmpbuf(png_ptr)))
goto free_image_out;
for (png_uint_32 i = 0; i < height; ++i)
row_ptrs[i] = (png_bytep) (img->data + (i * row_bytes));
png_read_image(png_ptr, row_ptrs);
png_read_end(png_ptr, end_ptr);
img->height = height;
img->width = width;
ret = img;
img = NULL; // don't free it, we're returning it now
free_image_out:
free(img);
free(row_ptrs);
destroy_read_out:
png_destroy_read_struct(&png_ptr, info_ptr ? &info_ptr : NULL, end_ptr ? &end_ptr : NULL);
close_out:
fclose(f);
out:
return ret;
}
|