]> git.cworth.org Git - apitrace/blob - image.cpp
Show call name on glGetError warning messages.
[apitrace] / image.cpp
1 /**************************************************************************
2  *
3  * Copyright 2008-2010 VMware, Inc.
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  *
24  **************************************************************************/
25
26
27 #include <png.h>
28
29 #include <math.h>
30 #include <stdint.h>
31 #include <stdlib.h>
32
33 #include <fstream>
34
35 #include "image.hpp"
36
37
38 namespace Image {
39
40
41 #pragma pack(push,2)
42 struct FileHeader {
43     uint16_t bfType;
44     uint32_t bfSize;
45     uint16_t bfReserved1;
46     uint16_t bfReserved2;
47     uint32_t bfOffBits;
48 };
49 #pragma pack(pop)
50
51 struct InfoHeader {
52     uint32_t biSize;
53     int32_t biWidth;
54     int32_t biHeight;
55     uint16_t biPlanes;
56     uint16_t biBitCount;
57     uint32_t biCompression;
58     uint32_t biSizeImage;
59     int32_t biXPelsPerMeter;
60     int32_t biYPelsPerMeter;
61     uint32_t biClrUsed;
62     uint32_t biClrImportant;
63 };
64
65 struct Pixel {
66     uint8_t rgbBlue;
67     uint8_t rgbGreen;
68     uint8_t rgbRed;
69     uint8_t rgbAlpha;
70 };
71
72
73 bool
74 Image::writeBMP(const char *filename) const {
75     struct FileHeader bmfh;
76     struct InfoHeader bmih;
77     unsigned x, y;
78
79     bmfh.bfType = 0x4d42;
80     bmfh.bfSize = 14 + 40 + height*width*4;
81     bmfh.bfReserved1 = 0;
82     bmfh.bfReserved2 = 0;
83     bmfh.bfOffBits = 14 + 40;
84
85     bmih.biSize = 40;
86     bmih.biWidth = width;
87     bmih.biHeight = height;
88     bmih.biPlanes = 1;
89     bmih.biBitCount = 32;
90     bmih.biCompression = 0;
91     bmih.biSizeImage = height*width*4;
92     bmih.biXPelsPerMeter = 0;
93     bmih.biYPelsPerMeter = 0;
94     bmih.biClrUsed = 0;
95     bmih.biClrImportant = 0;
96
97     std::ofstream stream(filename, std::ofstream::binary);
98
99     if (!stream)
100         return false;
101
102     stream.write((const char *)&bmfh, 14);
103     stream.write((const char *)&bmih, 40);
104
105     unsigned stride = width*4;
106
107     if (flipped) {
108         for (y = 0; y < height; ++y) {
109             const unsigned char *ptr = pixels + y * stride;
110             for (x = 0; x < width; ++x) {
111                 struct Pixel pixel;
112                 pixel.rgbRed   = ptr[x*4 + 0];
113                 pixel.rgbGreen = ptr[x*4 + 1];
114                 pixel.rgbBlue  = ptr[x*4 + 2];
115                 pixel.rgbAlpha = ptr[x*4 + 3];
116                 stream.write((const char *)&pixel, 4);
117             }
118         }
119     } else {
120         y = height;
121         while (y--) {
122             const unsigned char *ptr = pixels + y * stride;
123             for (x = 0; x < width; ++x) {
124                 struct Pixel pixel;
125                 pixel.rgbRed   = ptr[x*4 + 0];
126                 pixel.rgbGreen = ptr[x*4 + 1];
127                 pixel.rgbBlue  = ptr[x*4 + 2];
128                 pixel.rgbAlpha = ptr[x*4 + 3];
129                 stream.write((const char *)&pixel, 4);
130             }
131         }
132     }
133
134     stream.close();
135
136     return true;
137 }
138
139 bool
140 Image::writePNG(const char *filename) const {
141     FILE *fp;
142     png_structp png_ptr;
143     png_infop info_ptr;
144
145     fp = fopen(filename, "wb");
146     if (!fp)
147         goto no_fp;
148
149     png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
150     if (!png_ptr)
151         goto no_png;
152
153     info_ptr = png_create_info_struct(png_ptr);
154     if (!info_ptr) {
155         png_destroy_write_struct(&png_ptr,  NULL);
156         goto no_png;
157     }
158
159     if (setjmp(png_jmpbuf(png_ptr))) {
160         png_destroy_write_struct(&png_ptr, &info_ptr);
161         goto no_png;
162     }
163
164     png_init_io(png_ptr, fp);
165
166     png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB_ALPHA,
167         PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
168
169     png_set_compression_level(png_ptr, Z_DEFAULT_COMPRESSION);
170
171     png_write_info(png_ptr, info_ptr);
172
173     if (!flipped) {
174         for (unsigned y = 0; y < height; ++y) {
175             png_bytep row = (png_bytep)(pixels + y*width*4);
176             png_write_rows(png_ptr, &row, 1);
177         }
178     } else {
179         unsigned y = height;
180         while (y--) {
181             png_bytep row = (png_bytep)(pixels + y*width*4);
182             png_write_rows(png_ptr, &row, 1);
183         }
184     }
185
186     png_write_end(png_ptr, info_ptr);
187     png_destroy_write_struct(&png_ptr, &info_ptr);
188
189     fclose(fp);
190     return true;
191
192 no_png:
193     fclose(fp);
194 no_fp:
195     return false;
196 }
197
198
199 Image *
200 readPNG(const char *filename)
201 {
202     FILE *fp;
203     png_structp png_ptr;
204     png_infop info_ptr;
205     png_infop end_info;
206     Image *image;
207
208     fp = fopen(filename, "rb");
209     if (!fp)
210         goto no_fp;
211
212     png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
213     if (!png_ptr)
214         goto no_png;
215
216     info_ptr = png_create_info_struct(png_ptr);
217     if (!info_ptr) {
218         png_destroy_read_struct(&png_ptr, NULL, NULL);
219         goto no_png;
220     }
221
222     end_info = png_create_info_struct(png_ptr);
223     if (!end_info) {
224         png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
225         goto no_png;
226     }
227
228     if (setjmp(png_jmpbuf(png_ptr))) {
229         png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
230         goto no_png;
231     }
232
233     png_init_io(png_ptr, fp);
234
235     png_read_info(png_ptr, info_ptr);
236
237     png_uint_32 width, height;
238     int bit_depth, color_type, interlace_type, compression_type, filter_method;
239
240     png_get_IHDR(png_ptr, info_ptr,
241                  &width, &height,
242                  &bit_depth, &color_type, &interlace_type,
243                  &compression_type, &filter_method);
244
245     image = new Image(width, height);
246     if (!image)
247         goto no_image;
248
249     /* Convert to RGBA8 */
250     if (color_type == PNG_COLOR_TYPE_PALETTE)
251         png_set_palette_to_rgb(png_ptr);
252     if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
253         png_set_expand_gray_1_2_4_to_8(png_ptr);
254     if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
255         png_set_tRNS_to_alpha(png_ptr);
256     if (bit_depth == 16)
257         png_set_strip_16(png_ptr);
258
259     for (unsigned y = 0; y < height; ++y) {
260         png_bytep row = (png_bytep)(image->pixels + y*width*4);
261         png_read_row(png_ptr, row, NULL);
262     }
263
264     png_read_end(png_ptr, info_ptr);
265     png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
266     fclose(fp);
267     return image;
268
269 no_image:
270     png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
271 no_png:
272     fclose(fp);
273 no_fp:
274     return NULL;
275 }
276
277
278 double Image::compare(Image &ref)
279 {
280     if (width != ref.width ||
281         height != ref.height) {
282         return 0.0;
283     }
284
285     const unsigned char *pSrc = start();
286     const unsigned char *pRef = ref.start();
287
288     unsigned long long error = 0;
289     for (unsigned y = 0; y < height; ++y) {
290         for (unsigned  x = 0; x < width; ++x) {
291             // FIXME: Ignore alpha channel until we are able to pick a visual
292             // that matches the traces
293             for (unsigned  c = 0; c < 3; ++c) {
294                 int delta = pSrc[x*4 + c] - pRef[x*4 + c];
295                 error += delta*delta;
296             }
297         }
298
299         pSrc += stride();
300         pRef += ref.stride();
301     }
302
303     double numerator = error*2 + 1;
304     double denominator = height*width*3ULL*255ULL*255ULL*2;
305     double quotient = numerator/denominator;
306
307     // Precision in bits
308     double precision = -log(quotient)/log(2.0);
309
310     return precision;
311 }
312
313 struct png_tmp_buffer
314 {
315     char *buffer;
316     size_t size;
317 };
318
319 static void
320 pngWriteCallback(png_structp png_ptr, png_bytep data, png_size_t length)
321 {
322     struct png_tmp_buffer *buf = (struct png_tmp_buffer*) png_ptr->io_ptr;
323     size_t nsize = buf->size + length;
324
325     /* allocate or grow buffer */
326     if (buf->buffer)
327         buf->buffer = (char*)realloc(buf->buffer, nsize);
328     else
329         buf->buffer = (char*)malloc(nsize);
330
331     if (!buf->buffer)
332         png_error(png_ptr, "Buffer allocation error");
333
334     memcpy(buf->buffer + buf->size, data, length);
335     buf->size += length;
336 }
337
338 bool writePixelsToBuffer(unsigned char *pixels,
339                          unsigned width, unsigned height, unsigned numChannels,
340                          bool flipped,
341                          char **buffer,
342                          int *size)
343 {
344     struct png_tmp_buffer png_mem;
345     png_structp png_ptr;
346     png_infop info_ptr;
347     int type;
348
349     png_mem.buffer = NULL;
350     png_mem.size = 0;
351
352     switch (numChannels) {
353     case 4:
354         type = PNG_COLOR_TYPE_RGB_ALPHA;
355         break;
356     case 3:
357         type = PNG_COLOR_TYPE_RGB;
358         break;
359     case 2:
360         type = PNG_COLOR_TYPE_GRAY_ALPHA;
361         break;
362     case 1:
363         type = PNG_COLOR_TYPE_GRAY;
364         break;
365     default:
366         goto no_png;
367     }
368
369     png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
370     if (!png_ptr)
371         goto no_png;
372
373     info_ptr = png_create_info_struct(png_ptr);
374     if (!info_ptr) {
375         png_destroy_write_struct(&png_ptr,  NULL);
376         goto no_png;
377     }
378
379     if (setjmp(png_jmpbuf(png_ptr))) {
380         png_destroy_write_struct(&png_ptr, &info_ptr);
381         goto no_png;
382     }
383
384     png_set_write_fn(png_ptr, &png_mem, pngWriteCallback, NULL);
385
386     png_set_IHDR(png_ptr, info_ptr, width, height, 8,
387                  type, PNG_INTERLACE_NONE,
388                  PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
389
390     png_set_compression_level(png_ptr, Z_DEFAULT_COMPRESSION);
391
392     png_write_info(png_ptr, info_ptr);
393
394     if (!flipped) {
395         for (unsigned y = 0; y < height; ++y) {
396             png_bytep row = (png_bytep)(pixels + y*width*numChannels);
397             png_write_rows(png_ptr, &row, 1);
398         }
399     } else {
400         unsigned y = height;
401         while (y--) {
402             png_bytep row = (png_bytep)(pixels + y*width*numChannels);
403             png_write_rows(png_ptr, &row, 1);
404         }
405     }
406
407     png_write_end(png_ptr, info_ptr);
408     png_destroy_write_struct(&png_ptr, &info_ptr);
409
410     *buffer = png_mem.buffer;
411     *size = png_mem.size;
412
413     return true;
414
415 no_png:
416     *buffer = NULL;
417     *size = 0;
418
419     if (png_mem.buffer)
420         free(png_mem.buffer);
421     return false;
422 }
423
424 } /* namespace Image */