1 #pragma once
2 
3 #include <stdlib.h>
4 #include <stdint.h>
5 #include <string.h>
6 
7 #define IMWIDTH 1024
8 #define IMHEIGHT 1024
9 
10 extern "C" {
11 #  include <png.h>
12 }
13 
14 typedef struct _png_dat {
15     uint8_t *buf;
16     int64_t len;
17     size_t buf_rem;
18 } png_dat;
19 
20 typedef struct _png_parse_dat {
21     uint8_t *cur_pos;
22 } png_parse_dat;
23 
24 /* Write a customized write callback so that we write back to an in-memory buffer.
25  * This allows the testing to not involve disk IO */
png_write_cb(png_structp pngp,png_bytep data,png_size_t len)26 static void png_write_cb(png_structp pngp, png_bytep data, png_size_t len) {
27     png_dat *dat = (png_dat*)png_get_io_ptr(pngp);
28     size_t curSize = dat->len + len;
29 
30     /* realloc double the requested buffer size to prevent excessive reallocs */
31     if (dat->buf_rem < len) {
32         dat->buf = (uint8_t*)realloc(dat->buf, dat->len + dat->buf_rem + 2 * len);
33 
34         if (!dat->buf) {
35             /* Pretty unlikely but we'll put it here just in case */
36             fprintf(stderr, "realloc failed, exiting\n");
37             exit(1);
38         }
39 
40         dat->buf_rem += 2 * len;
41     }
42 
43     memcpy(dat->buf + dat->len, data, len);
44     dat->len = curSize;
45     dat->buf_rem -= len;
46 }
47 
init_compressible(png_bytep buf,size_t num_pix)48 static void init_compressible(png_bytep buf, size_t num_pix) {
49     /* It doesn't actually matter what we make this, but for
50      * the sake of a reasonable test image, let's make this
51      * be a stripe of R, G, & B, with no alpha channel */
52     int32_t i = 0;
53     int32_t red_stop = num_pix / 3;
54     int32_t blue_stop = 2 * num_pix / 3;
55     int32_t green_stop = num_pix;
56 
57     for (int32_t x = 0; i < red_stop; x += 3, ++i) {
58        buf[x] = 255;
59        buf[x + 1] = 0;
60        buf[x + 2] = 0;
61     }
62 
63     for (int32_t x = 3 * i; i < blue_stop; x+= 3, ++i) {
64        buf[x] = 0;
65        buf[x + 1] = 255;
66        buf[x + 2] = 0;
67     }
68 
69     for (int32_t x = 3 * i; i < green_stop; x += 3, ++i) {
70        buf[x] = 0;
71        buf[x + 1] = 0;
72        buf[x + 2] = 255;
73     }
74 }
75 
encode_png(png_bytep buf,png_dat * outpng,int32_t comp_level,uint32_t width,uint32_t height)76 static inline void encode_png(png_bytep buf, png_dat *outpng, int32_t comp_level, uint32_t width, uint32_t height) {
77     png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
78 
79     /* Most of this error handling is _likely_ not necessary. Likewise it's likely
80      * a lot of this stuff can be done in the setup function to avoid measuring this
81      * fixed setup time, but for now we'll do it here */
82     if (!png) abort();
83 
84     png_infop info  = png_create_info_struct(png);
85     if (!info) abort();
86 
87     png_set_write_fn(png, outpng, png_write_cb, NULL);
88     png_bytep *png_row_ptrs = new png_bytep[height];
89     for (int i = 0; i < IMHEIGHT; ++i) {
90         png_row_ptrs[i] = (png_bytep)&buf[3*i*width];
91     }
92 
93     png_set_IHDR(png, info, IMWIDTH, IMHEIGHT, 8, PNG_COLOR_TYPE_RGB,
94                  PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
95                  PNG_FILTER_TYPE_DEFAULT);
96 
97     png_write_info(png, info);
98     png_set_compression_level(png, comp_level);
99     png_set_filter(png, 0, PNG_FILTER_NONE);
100     png_write_image(png, (png_bytepp)png_row_ptrs);
101     png_write_end(png, NULL);
102     png_destroy_write_struct(&png, &info);
103     delete[] png_row_ptrs;
104 }
105 
read_from_pngdat(png_structp png,png_bytep out,png_size_t bytes_to_read)106 static void read_from_pngdat(png_structp png, png_bytep out, png_size_t bytes_to_read) {
107     png_parse_dat *io = (png_parse_dat*)png_get_io_ptr(png);
108     memcpy(out, io->cur_pos, bytes_to_read);
109     io->cur_pos += bytes_to_read;
110 }
111 
decode_png(png_parse_dat * dat,png_bytepp out_bytes,size_t in_size,uint32_t & width,uint32_t & height)112 static inline int decode_png(png_parse_dat *dat, png_bytepp out_bytes, size_t in_size, uint32_t &width, uint32_t &height) {
113     png_structp png = NULL;
114     png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
115 
116     if (!png) abort();
117     png_infop info = NULL;
118     info = png_create_info_struct(png);
119     if (!info) abort();
120 
121     png_set_read_fn(png, dat, read_from_pngdat);
122     png_read_info(png, info);
123 
124     int bit_depth = 0, color_type = -1;
125     png_get_IHDR(png, info, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL);
126 
127     size_t im_size = width * height * bit_depth/8 * 3;
128     if (color_type != PNG_COLOR_TYPE_RGB) {
129         fprintf(stderr, "expected an 8 bpp RGB image\n");
130         abort();
131     }
132 
133     if (im_size > in_size) {
134        *out_bytes = (png_bytep)realloc(*out_bytes, im_size);
135     }
136 
137     png_bytep *out_rows = new png_bytep[height];
138     for (size_t i = 0; i < height; ++i)
139         out_rows[i] = *out_bytes + (width*i*3);
140 
141     png_read_rows(png, out_rows, NULL, height);
142     png_destroy_read_struct(&png, &info, NULL);
143     delete[] out_rows;
144 
145     return im_size;
146 }
147