src/util/png-io.c (view raw)
1#include "util/png-io.h"
2
3#ifdef USE_PNG
4
5#include "vfs.h"
6
7static void _pngWrite(png_structp png, png_bytep buffer, png_size_t size) {
8 struct VFile* vf = png_get_io_ptr(png);
9 size_t written = vf->write(vf, buffer, size);
10 if (written != size) {
11 png_error(png, "Could not write PNG");
12 }
13}
14
15static void _pngRead(png_structp png, png_bytep buffer, png_size_t size) {
16 struct VFile* vf = png_get_io_ptr(png);
17 size_t read = vf->read(vf, buffer, size);
18 if (read != size) {
19 png_error(png, "Could not read PNG");
20 }
21}
22
23png_structp PNGWriteOpen(struct VFile* source) {
24 png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
25 if (!png) {
26 return 0;
27 }
28 if (setjmp(png_jmpbuf(png))) {
29 png_destroy_write_struct(&png, 0);
30 return 0;
31 }
32 png_set_write_fn(png, source, _pngWrite, 0);
33 return png;
34}
35
36png_infop PNGWriteHeader(png_structp png, unsigned width, unsigned height) {
37 png_infop info = png_create_info_struct(png);
38 if (!info) {
39 return 0;
40 }
41 png_set_IHDR(png, info, width, height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
42 png_write_info(png, info);
43 return info;
44}
45
46bool PNGWritePixels(png_structp png, unsigned width, unsigned height, unsigned stride, void* pixels) {
47 png_bytep row = malloc(sizeof(png_bytep) * width * 3);
48 if (!row) {
49 return false;
50 }
51 png_bytep pixelData = pixels;
52 if (setjmp(png_jmpbuf(png))) {
53 free(row);
54 return false;
55 }
56 unsigned i;
57 for (i = 0; i < height; ++i) {
58 unsigned x;
59 for (x = 0; x < width; ++x) {
60 row[x * 3] = pixelData[stride * i * 4 + x * 4];
61 row[x * 3 + 1] = pixelData[stride * i * 4 + x * 4 + 1];
62 row[x * 3 + 2] = pixelData[stride * i * 4 + x * 4 + 2];
63 }
64 png_write_row(png, row);
65 }
66 free(row);
67 return true;
68}
69
70bool PNGWriteCustomChunk(png_structp png, const char* name, size_t size, void* data) {
71 char realName[5];
72 strncpy(realName, name, 4);
73 realName[0] = tolower(realName[0]);
74 realName[1] = tolower(realName[1]);
75 realName[4] = '\0';
76 if (setjmp(png_jmpbuf(png))) {
77 return false;
78 }
79 png_write_chunk(png, (const png_bytep) realName, data, size);
80 return true;
81}
82
83void PNGWriteClose(png_structp png, png_infop info) {
84 if (!setjmp(png_jmpbuf(png))) {
85 png_write_end(png, info);
86 }
87 png_destroy_write_struct(&png, &info);
88}
89
90bool isPNG(struct VFile* source) {
91 png_byte header[PNG_HEADER_BYTES];
92 source->read(source, header, PNG_HEADER_BYTES);
93 return !png_sig_cmp(header, 0, PNG_HEADER_BYTES);
94}
95
96png_structp PNGReadOpen(struct VFile* source, unsigned offset) {
97 png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
98 if (!png) {
99 return 0;
100 }
101 if (setjmp(png_jmpbuf(png))) {
102 png_destroy_read_struct(&png, 0, 0);
103 return 0;
104 }
105 png_set_read_fn(png, source, _pngRead);
106 png_set_sig_bytes(png, offset);
107 return png;
108}
109
110bool PNGInstallChunkHandler(png_structp png, void* context, ChunkHandler handler, const char* chunkName) {
111 if (setjmp(png_jmpbuf(png))) {
112 return false;
113 }
114 png_set_read_user_chunk_fn(png, context, handler);
115 png_set_keep_unknown_chunks(png, PNG_HANDLE_CHUNK_ALWAYS, (const png_bytep) chunkName, 1);
116 return true;
117}
118
119bool PNGReadHeader(png_structp png, png_infop info) {
120 if (setjmp(png_jmpbuf(png))) {
121 return false;
122 }
123 png_read_info(png, info);
124 return true;
125}
126
127bool PNGIgnorePixels(png_structp png, png_infop info) {
128 if (setjmp(png_jmpbuf(png))) {
129 return false;
130 }
131
132 unsigned height = png_get_image_height(png, info);
133 unsigned i;
134 for (i = 0; i < height; ++i) {
135 png_read_row(png, 0, 0);
136 }
137 return true;
138}
139
140bool PNGReadPixels(png_structp png, png_infop info, void* pixels, unsigned width, unsigned height, unsigned stride) {
141 if (setjmp(png_jmpbuf(png))) {
142 return false;
143 }
144
145 uint8_t* pixelData = pixels;
146 unsigned pngHeight = png_get_image_height(png, info);
147 if (height > pngHeight) {
148 height = pngHeight;
149 }
150
151 unsigned pngWidth = png_get_image_width(png, info);
152 if (width > pngWidth) {
153 width = pngWidth;
154 }
155
156 unsigned i;
157 png_bytep row = malloc(png_get_rowbytes(png, info));
158 for (i = 0; i < height; ++i) {
159 png_read_row(png, row, 0);
160 unsigned x;
161 for (x = 0; x < width; ++x) {
162 pixelData[stride * i * 4 + x * 4] = row[x * 3];
163 pixelData[stride * i * 4 + x * 4 + 1] = row[x * 3 + 1];
164 pixelData[stride * i * 4 + x * 4 + 2] = row[x * 3 + 2];
165 }
166 }
167 free(row);
168 return true;
169}
170
171bool PNGReadFooter(png_structp png, png_infop end) {
172 if (setjmp(png_jmpbuf(png))) {
173 return false;
174 }
175 png_read_end(png, end);
176 return true;
177}
178
179void PNGReadClose(png_structp png, png_infop info, png_infop end) {
180 png_destroy_read_struct(&png, &info, &end);
181}
182
183#endif