]> git.cworth.org Git - apitrace/blob - image.cpp
tracerepack: Don't fail when temp file is in different filesystem.
[apitrace] / image.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 <math.h>
33 #include <stdint.h>
34 #include <stdlib.h>
35
36 #include <fstream>
37
38 #include "image.hpp"
39
40
41 namespace Image {
42
43
44 #pragma pack(push,2)
45 struct FileHeader {
46     uint16_t bfType;
47     uint32_t bfSize;
48     uint16_t bfReserved1;
49     uint16_t bfReserved2;
50     uint32_t bfOffBits;
51 };
52 #pragma pack(pop)
53
54 struct InfoHeader {
55     uint32_t biSize;
56     int32_t biWidth;
57     int32_t biHeight;
58     uint16_t biPlanes;
59     uint16_t biBitCount;
60     uint32_t biCompression;
61     uint32_t biSizeImage;
62     int32_t biXPelsPerMeter;
63     int32_t biYPelsPerMeter;
64     uint32_t biClrUsed;
65     uint32_t biClrImportant;
66 };
67
68 struct Pixel {
69     uint8_t rgbBlue;
70     uint8_t rgbGreen;
71     uint8_t rgbRed;
72     uint8_t rgbAlpha;
73 };
74
75
76 bool
77 Image::writeBMP(const char *filename) const {
78     assert(channels == 4);
79
80     struct FileHeader bmfh;
81     struct InfoHeader bmih;
82     unsigned x, y;
83
84     bmfh.bfType = 0x4d42;
85     bmfh.bfSize = 14 + 40 + height*width*4;
86     bmfh.bfReserved1 = 0;
87     bmfh.bfReserved2 = 0;
88     bmfh.bfOffBits = 14 + 40;
89
90     bmih.biSize = 40;
91     bmih.biWidth = width;
92     bmih.biHeight = height;
93     bmih.biPlanes = 1;
94     bmih.biBitCount = 32;
95     bmih.biCompression = 0;
96     bmih.biSizeImage = height*width*4;
97     bmih.biXPelsPerMeter = 0;
98     bmih.biYPelsPerMeter = 0;
99     bmih.biClrUsed = 0;
100     bmih.biClrImportant = 0;
101
102     std::ofstream stream(filename, std::ofstream::binary);
103
104     if (!stream)
105         return false;
106
107     stream.write((const char *)&bmfh, 14);
108     stream.write((const char *)&bmih, 40);
109
110     unsigned stride = width*4;
111
112     if (flipped) {
113         for (y = 0; y < height; ++y) {
114             const unsigned char *ptr = pixels + y * stride;
115             for (x = 0; x < width; ++x) {
116                 struct Pixel pixel;
117                 pixel.rgbRed   = ptr[x*4 + 0];
118                 pixel.rgbGreen = ptr[x*4 + 1];
119                 pixel.rgbBlue  = ptr[x*4 + 2];
120                 pixel.rgbAlpha = ptr[x*4 + 3];
121                 stream.write((const char *)&pixel, 4);
122             }
123         }
124     } else {
125         y = height;
126         while (y--) {
127             const unsigned char *ptr = pixels + y * stride;
128             for (x = 0; x < width; ++x) {
129                 struct Pixel pixel;
130                 pixel.rgbRed   = ptr[x*4 + 0];
131                 pixel.rgbGreen = ptr[x*4 + 1];
132                 pixel.rgbBlue  = ptr[x*4 + 2];
133                 pixel.rgbAlpha = ptr[x*4 + 3];
134                 stream.write((const char *)&pixel, 4);
135             }
136         }
137     }
138
139     stream.close();
140
141     return true;
142 }
143
144 bool
145 Image::writePNG(const char *filename) const {
146     FILE *fp;
147     png_structp png_ptr;
148     png_infop info_ptr;
149
150     fp = fopen(filename, "wb");
151     if (!fp)
152         goto no_fp;
153
154     png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
155     if (!png_ptr)
156         goto no_png;
157
158     info_ptr = png_create_info_struct(png_ptr);
159     if (!info_ptr) {
160         png_destroy_write_struct(&png_ptr,  NULL);
161         goto no_png;
162     }
163
164     if (setjmp(png_jmpbuf(png_ptr))) {
165         png_destroy_write_struct(&png_ptr, &info_ptr);
166         goto no_png;
167     }
168
169     png_init_io(png_ptr, fp);
170
171     int color_type;
172     switch (channels) {
173     case 4:
174         color_type = PNG_COLOR_TYPE_RGB_ALPHA;
175         break;
176     case 3:
177         color_type = PNG_COLOR_TYPE_RGB;
178         break;
179     case 2:
180         color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
181         break;
182     case 1:
183         color_type = PNG_COLOR_TYPE_GRAY;
184         break;
185     default:
186         assert(0);
187         return false;
188     }
189
190     png_set_IHDR(png_ptr, info_ptr, width, height, 8, color_type,
191         PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
192
193     png_set_compression_level(png_ptr, Z_DEFAULT_COMPRESSION);
194
195     png_write_info(png_ptr, info_ptr);
196
197     if (!flipped) {
198         for (unsigned y = 0; y < height; ++y) {
199             png_bytep row = (png_bytep)(pixels + y*width*channels);
200             png_write_rows(png_ptr, &row, 1);
201         }
202     } else {
203         unsigned y = height;
204         while (y--) {
205             png_bytep row = (png_bytep)(pixels + y*width*channels);
206             png_write_rows(png_ptr, &row, 1);
207         }
208     }
209
210     png_write_end(png_ptr, info_ptr);
211     png_destroy_write_struct(&png_ptr, &info_ptr);
212
213     fclose(fp);
214     return true;
215
216 no_png:
217     fclose(fp);
218 no_fp:
219     return false;
220 }
221
222
223 Image *
224 readPNG(const char *filename)
225 {
226     FILE *fp;
227     png_structp png_ptr;
228     png_infop info_ptr;
229     png_infop end_info;
230     Image *image;
231
232     fp = fopen(filename, "rb");
233     if (!fp)
234         goto no_fp;
235
236     png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
237     if (!png_ptr)
238         goto no_png;
239
240     info_ptr = png_create_info_struct(png_ptr);
241     if (!info_ptr) {
242         png_destroy_read_struct(&png_ptr, NULL, NULL);
243         goto no_png;
244     }
245
246     end_info = png_create_info_struct(png_ptr);
247     if (!end_info) {
248         png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
249         goto no_png;
250     }
251
252     if (setjmp(png_jmpbuf(png_ptr))) {
253         png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
254         goto no_png;
255     }
256
257     png_init_io(png_ptr, fp);
258
259     png_read_info(png_ptr, info_ptr);
260
261     png_uint_32 width, height;
262     int bit_depth, color_type, interlace_type, compression_type, filter_method;
263
264     png_get_IHDR(png_ptr, info_ptr,
265                  &width, &height,
266                  &bit_depth, &color_type, &interlace_type,
267                  &compression_type, &filter_method);
268
269     image = new Image(width, height);
270     if (!image)
271         goto no_image;
272
273     /* Convert to RGBA8 */
274     if (color_type == PNG_COLOR_TYPE_PALETTE)
275         png_set_palette_to_rgb(png_ptr);
276     if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
277         png_set_expand_gray_1_2_4_to_8(png_ptr);
278     if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
279         png_set_tRNS_to_alpha(png_ptr);
280     if (bit_depth == 16)
281         png_set_strip_16(png_ptr);
282
283     for (unsigned y = 0; y < height; ++y) {
284         png_bytep row = (png_bytep)(image->pixels + y*width*4);
285         png_read_row(png_ptr, row, NULL);
286     }
287
288     png_read_end(png_ptr, info_ptr);
289     png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
290     fclose(fp);
291     return image;
292
293 no_image:
294     png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
295 no_png:
296     fclose(fp);
297 no_fp:
298     return NULL;
299 }
300
301
302 double Image::compare(Image &ref)
303 {
304     if (width != ref.width ||
305         height != ref.height ||
306         channels != ref.channels) {
307         return 0.0;
308     }
309
310     const unsigned char *pSrc = start();
311     const unsigned char *pRef = ref.start();
312
313     assert(channels >= 3);
314
315     unsigned long long error = 0;
316     for (unsigned y = 0; y < height; ++y) {
317         for (unsigned  x = 0; x < width; ++x) {
318             // FIXME: Ignore alpha channel until we are able to pick a visual
319             // that matches the traces
320             for (unsigned  c = 0; c < 3; ++c) {
321                 int delta = pSrc[x*channels + c] - pRef[x*channels + c];
322                 error += delta*delta;
323             }
324         }
325
326         pSrc += stride();
327         pRef += ref.stride();
328     }
329
330     double numerator = error*2 + 1;
331     double denominator = height*width*3ULL*255ULL*255ULL*2;
332     double quotient = numerator/denominator;
333
334     // Precision in bits
335     double precision = -log(quotient)/log(2.0);
336
337     return precision;
338 }
339
340 struct png_tmp_buffer
341 {
342     char *buffer;
343     size_t size;
344 };
345
346 static void
347 pngWriteCallback(png_structp png_ptr, png_bytep data, png_size_t length)
348 {
349     struct png_tmp_buffer *buf = (struct png_tmp_buffer*) png_get_io_ptr(png_ptr);
350     size_t nsize = buf->size + length;
351
352     /* allocate or grow buffer */
353     if (buf->buffer)
354         buf->buffer = (char*)realloc(buf->buffer, nsize);
355     else
356         buf->buffer = (char*)malloc(nsize);
357
358     if (!buf->buffer)
359         png_error(png_ptr, "Buffer allocation error");
360
361     memcpy(buf->buffer + buf->size, data, length);
362     buf->size += length;
363 }
364
365 bool writePixelsToBuffer(unsigned char *pixels,
366                          unsigned width, unsigned height, unsigned numChannels,
367                          bool flipped,
368                          char **buffer,
369                          int *size)
370 {
371     struct png_tmp_buffer png_mem;
372     png_structp png_ptr;
373     png_infop info_ptr;
374     int type;
375
376     png_mem.buffer = NULL;
377     png_mem.size = 0;
378
379     switch (numChannels) {
380     case 4:
381         type = PNG_COLOR_TYPE_RGB_ALPHA;
382         break;
383     case 3:
384         type = PNG_COLOR_TYPE_RGB;
385         break;
386     case 2:
387         type = PNG_COLOR_TYPE_GRAY_ALPHA;
388         break;
389     case 1:
390         type = PNG_COLOR_TYPE_GRAY;
391         break;
392     default:
393         goto no_png;
394     }
395
396     png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
397     if (!png_ptr)
398         goto no_png;
399
400     info_ptr = png_create_info_struct(png_ptr);
401     if (!info_ptr) {
402         png_destroy_write_struct(&png_ptr,  NULL);
403         goto no_png;
404     }
405
406     if (setjmp(png_jmpbuf(png_ptr))) {
407         png_destroy_write_struct(&png_ptr, &info_ptr);
408         goto no_png;
409     }
410
411     png_set_write_fn(png_ptr, &png_mem, pngWriteCallback, NULL);
412
413     png_set_IHDR(png_ptr, info_ptr, width, height, 8,
414                  type, PNG_INTERLACE_NONE,
415                  PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
416
417     png_set_compression_level(png_ptr, Z_DEFAULT_COMPRESSION);
418
419     png_write_info(png_ptr, info_ptr);
420
421     if (!flipped) {
422         for (unsigned y = 0; y < height; ++y) {
423             png_bytep row = (png_bytep)(pixels + y*width*numChannels);
424             png_write_rows(png_ptr, &row, 1);
425         }
426     } else {
427         unsigned y = height;
428         while (y--) {
429             png_bytep row = (png_bytep)(pixels + y*width*numChannels);
430             png_write_rows(png_ptr, &row, 1);
431         }
432     }
433
434     png_write_end(png_ptr, info_ptr);
435     png_destroy_write_struct(&png_ptr, &info_ptr);
436
437     *buffer = png_mem.buffer;
438     *size = png_mem.size;
439
440     return true;
441
442 no_png:
443     *buffer = NULL;
444     *size = 0;
445
446     if (png_mem.buffer)
447         free(png_mem.buffer);
448     return false;
449 }
450
451 } /* namespace Image */