]> git.cworth.org Git - apitrace/blob - common/image_png.cpp
cc6d3f2cc4d7ff6e476db9df609a5148e27a6546
[apitrace] / common / image_png.cpp
1 /**************************************************************************
2  *
3  * Copyright 2011 Jose Fonseca
4  * Copyright 2008-2010 VMware, Inc.
5  * All Rights Reserved.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23  * THE SOFTWARE.
24  *
25  **************************************************************************/
26
27
28 #include <zlib.h>
29 #include <png.h>
30
31 #include <assert.h>
32 #include <stdint.h>
33 #include <stdlib.h>
34
35 #include <fstream>
36
37 #include "image.hpp"
38
39
40 namespace image {
41
42
43 static const int png_compression_level = Z_BEST_SPEED;
44
45
46 bool
47 Image::writePNG(const char *filename) const {
48     FILE *fp;
49     png_structp png_ptr;
50     png_infop info_ptr;
51
52     fp = fopen(filename, "wb");
53     if (!fp)
54         goto no_fp;
55
56     png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
57     if (!png_ptr)
58         goto no_png;
59
60     info_ptr = png_create_info_struct(png_ptr);
61     if (!info_ptr) {
62         png_destroy_write_struct(&png_ptr,  NULL);
63         goto no_png;
64     }
65
66     if (setjmp(png_jmpbuf(png_ptr))) {
67         png_destroy_write_struct(&png_ptr, &info_ptr);
68         goto no_png;
69     }
70
71     png_init_io(png_ptr, fp);
72
73     int color_type;
74     switch (channels) {
75     case 4:
76         color_type = PNG_COLOR_TYPE_RGB_ALPHA;
77         break;
78     case 3:
79         color_type = PNG_COLOR_TYPE_RGB;
80         break;
81     case 2:
82         color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
83         break;
84     case 1:
85         color_type = PNG_COLOR_TYPE_GRAY;
86         break;
87     default:
88         assert(0);
89         return false;
90     }
91
92     png_set_IHDR(png_ptr, info_ptr, width, height, 8, color_type,
93         PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
94
95     png_set_compression_level(png_ptr, png_compression_level);
96
97     png_write_info(png_ptr, info_ptr);
98
99     if (!flipped) {
100         for (unsigned y = 0; y < height; ++y) {
101             png_bytep row = (png_bytep)(pixels + y*width*channels);
102             png_write_rows(png_ptr, &row, 1);
103         }
104     } else {
105         unsigned y = height;
106         while (y--) {
107             png_bytep row = (png_bytep)(pixels + y*width*channels);
108             png_write_rows(png_ptr, &row, 1);
109         }
110     }
111
112     png_write_end(png_ptr, info_ptr);
113     png_destroy_write_struct(&png_ptr, &info_ptr);
114
115     fclose(fp);
116     return true;
117
118 no_png:
119     fclose(fp);
120 no_fp:
121     return false;
122 }
123
124
125 Image *
126 readPNG(const char *filename)
127 {
128     FILE *fp;
129     png_structp png_ptr;
130     png_infop info_ptr;
131     png_infop end_info;
132     Image *image;
133
134     fp = fopen(filename, "rb");
135     if (!fp)
136         goto no_fp;
137
138     png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
139     if (!png_ptr)
140         goto no_png;
141
142     info_ptr = png_create_info_struct(png_ptr);
143     if (!info_ptr) {
144         png_destroy_read_struct(&png_ptr, NULL, NULL);
145         goto no_png;
146     }
147
148     end_info = png_create_info_struct(png_ptr);
149     if (!end_info) {
150         png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
151         goto no_png;
152     }
153
154     if (setjmp(png_jmpbuf(png_ptr))) {
155         png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
156         goto no_png;
157     }
158
159     png_init_io(png_ptr, fp);
160
161     png_read_info(png_ptr, info_ptr);
162
163     png_uint_32 width, height;
164     int bit_depth, color_type, interlace_type, compression_type, filter_method;
165
166     png_get_IHDR(png_ptr, info_ptr,
167                  &width, &height,
168                  &bit_depth, &color_type, &interlace_type,
169                  &compression_type, &filter_method);
170
171     image = new Image(width, height);
172     if (!image)
173         goto no_image;
174
175     /* Convert to RGBA8 */
176     if (color_type == PNG_COLOR_TYPE_PALETTE)
177         png_set_palette_to_rgb(png_ptr);
178     if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
179         png_set_expand_gray_1_2_4_to_8(png_ptr);
180     if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
181         png_set_tRNS_to_alpha(png_ptr);
182     if (bit_depth == 16)
183         png_set_strip_16(png_ptr);
184
185     for (unsigned y = 0; y < height; ++y) {
186         png_bytep row = (png_bytep)(image->pixels + y*width*4);
187         png_read_row(png_ptr, row, NULL);
188     }
189
190     png_read_end(png_ptr, info_ptr);
191     png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
192     fclose(fp);
193     return image;
194
195 no_image:
196     png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
197 no_png:
198     fclose(fp);
199 no_fp:
200     return NULL;
201 }
202
203
204 struct png_tmp_buffer
205 {
206     char *buffer;
207     size_t size;
208 };
209
210 static void
211 pngWriteCallback(png_structp png_ptr, png_bytep data, png_size_t length)
212 {
213     struct png_tmp_buffer *buf = (struct png_tmp_buffer*) png_get_io_ptr(png_ptr);
214     size_t nsize = buf->size + length;
215
216     /* allocate or grow buffer */
217     if (buf->buffer)
218         buf->buffer = (char*)realloc(buf->buffer, nsize);
219     else
220         buf->buffer = (char*)malloc(nsize);
221
222     if (!buf->buffer)
223         png_error(png_ptr, "Buffer allocation error");
224
225     memcpy(buf->buffer + buf->size, data, length);
226     buf->size += length;
227 }
228
229 bool writePixelsToBuffer(unsigned char *pixels,
230                          unsigned width, unsigned height, unsigned numChannels,
231                          bool flipped,
232                          char **buffer,
233                          int *size)
234 {
235     struct png_tmp_buffer png_mem;
236     png_structp png_ptr;
237     png_infop info_ptr;
238     int type;
239
240     png_mem.buffer = NULL;
241     png_mem.size = 0;
242
243     switch (numChannels) {
244     case 4:
245         type = PNG_COLOR_TYPE_RGB_ALPHA;
246         break;
247     case 3:
248         type = PNG_COLOR_TYPE_RGB;
249         break;
250     case 2:
251         type = PNG_COLOR_TYPE_GRAY_ALPHA;
252         break;
253     case 1:
254         type = PNG_COLOR_TYPE_GRAY;
255         break;
256     default:
257         goto no_png;
258     }
259
260     png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
261     if (!png_ptr)
262         goto no_png;
263
264     info_ptr = png_create_info_struct(png_ptr);
265     if (!info_ptr) {
266         png_destroy_write_struct(&png_ptr,  NULL);
267         goto no_png;
268     }
269
270     if (setjmp(png_jmpbuf(png_ptr))) {
271         png_destroy_write_struct(&png_ptr, &info_ptr);
272         goto no_png;
273     }
274
275     png_set_write_fn(png_ptr, &png_mem, pngWriteCallback, NULL);
276
277     png_set_IHDR(png_ptr, info_ptr, width, height, 8,
278                  type, PNG_INTERLACE_NONE,
279                  PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
280
281     png_set_compression_level(png_ptr, png_compression_level);
282
283     png_write_info(png_ptr, info_ptr);
284
285     if (!flipped) {
286         for (unsigned y = 0; y < height; ++y) {
287             png_bytep row = (png_bytep)(pixels + y*width*numChannels);
288             png_write_rows(png_ptr, &row, 1);
289         }
290     } else {
291         unsigned y = height;
292         while (y--) {
293             png_bytep row = (png_bytep)(pixels + y*width*numChannels);
294             png_write_rows(png_ptr, &row, 1);
295         }
296     }
297
298     png_write_end(png_ptr, info_ptr);
299     png_destroy_write_struct(&png_ptr, &info_ptr);
300
301     *buffer = png_mem.buffer;
302     *size = png_mem.size;
303
304     return true;
305
306 no_png:
307     *buffer = NULL;
308     *size = 0;
309
310     if (png_mem.buffer)
311         free(png_mem.buffer);
312     return false;
313 }
314
315 } /* namespace image */