File Coverage

imqoi.c
Criterion Covered Total %
statement 66 120 55.0
branch 29 54 53.7
condition n/a
subroutine n/a
pod n/a
total 95 174 54.6


line stmt bran cond sub pod time code
1             #include "imqoi.h"
2             #include "imext.h"
3             #include
4             #include
5              
6             #define QOI_IMPLEMENTATION
7             #include "qoi.h"
8              
9             #define BUF_BASE_SIZE (16384)
10             #define BUF_SCALE(x) ((x) * 3U / 2U)
11              
12             /* qoi.h wants the entire file in memory */
13             /* io_slurp() isn't suitable for this */
14             static void *
15 4           slurp(io_glue *io, size_t *data_size) {
16 4           unsigned char *data = malloc(BUF_BASE_SIZE);
17 4           ptrdiff_t offset = 0;
18 4           size_t size = BUF_BASE_SIZE;
19             ssize_t rd_size;
20            
21 4 50         if (data == NULL) {
22 0           i_push_error(errno, "out of memory");
23 0           return NULL;
24             }
25              
26 8 100         while ((rd_size = i_io_read(io, data + offset, size - offset)) > 0) {
27 4           offset += rd_size;
28 4 50         if (size - offset < BUF_BASE_SIZE / 2) {
29 0           size_t new_size = BUF_SCALE(size);
30             unsigned char *new_data;
31 0 0         if (new_size < size) {
32 0           i_push_error(0, "file too large");
33 0           free(data);
34 0           return NULL;
35             }
36 0           new_data = realloc(data, new_size);
37 0 0         if (new_data == NULL) {
38 0           free(data);
39 0           i_push_error(errno, "out of memory");
40 0           return NULL;
41             }
42 0           data = new_data;
43 0           size = new_size;
44             }
45             }
46 4           *data_size = offset;
47 4           return data;
48             }
49              
50             i_img *
51 4           i_readqoi(io_glue *ig, int page) {
52             size_t data_size;
53             void *data;
54             qoi_desc desc;
55 4           void *image_data = NULL;
56 4           i_img *img = NULL;
57             size_t row_size;
58             i_img_dim y;
59              
60 4           i_clear_error();
61              
62 4 50         if (page != 0) {
63 0           i_push_error(0, "qoi files contain only one image");
64 0           return NULL;
65             }
66              
67 4           data = slurp(ig, &data_size);
68 4 50         if (!data)
69 0           goto fail;
70              
71 4           image_data = qoi_decode(data, data_size, &desc, 0);
72 4 50         if (image_data == NULL) {
73             /* the decoder doesn't say why */
74 0           i_push_error(0, "image parse error");
75 0           goto fail;
76             }
77              
78             /* no longer need this */
79 4           free(data);
80 4           data = NULL;
81              
82 4 50         if (!i_int_check_image_file_limits(desc.width, desc.height,
83             desc.channels, sizeof(i_sample_t))) {
84             /* errors already pushed */
85 0           mm_log((1, "i_readqoi: image size exceeds limits\n"));
86 0           goto fail;
87             }
88 4           img = i_img_8_new(desc.width, desc.height, desc.channels);
89 4 50         if (!img)
90 0           goto fail;
91              
92 4           row_size = desc.width * desc.channels;
93 604 100         for (y = 0; y < desc.height; ++y) {
94 600           i_psamp(img, 0, desc.width, y, image_data + row_size * y, NULL, desc.channels);
95             }
96              
97 4           i_tags_set(&img->tags, "i_format", "qoi", 3);
98 4           i_tags_setn(&img->tags, "qoi_colorspace", desc.colorspace);
99              
100 4           free(image_data);
101              
102 4           return img;
103              
104             fail:
105 0           free(data);
106 0           free(image_data);
107              
108 4           return 0;
109             }
110              
111             i_img **
112 0           i_readqoi_multi(io_glue *ig, int *count) {
113 0           i_img *img = i_readqoi(ig, 0);
114 0 0         if (img) {
115 0           i_img **imgs = mymalloc(sizeof(i_img *));
116 0           *imgs = img;
117 0           *count = 1;
118 0           return imgs;
119             }
120             else {
121 0           *count = 0;
122 0           return NULL;
123             }
124             }
125              
126             static const int gray_chans[4] = { 0, 0, 0, 1 };
127              
128             undef_int
129 5           i_writeqoi(i_img *im, io_glue *ig) {
130             size_t data_size;
131 5           unsigned char *data = NULL;
132             int out_len;
133 5           void *image_data = NULL;
134             size_t row_size;
135             int channels;
136 5           const int *chans = NULL;
137             i_img_dim y;
138             qoi_desc desc;
139 5           int colorspace = 0;
140              
141 5           i_clear_error();
142              
143 5 50         if (im->xsize > INT_MAX || im->ysize > INT_MAX) {
    50          
144 0           i_push_error(0, "image too large for QOI");
145 0           return 0;
146             }
147              
148 5           i_tags_get_int(&im->tags, "qoi_colorspace", 0, &colorspace);
149 5 100         if (colorspace != QOI_SRGB && colorspace != QOI_LINEAR) {
    100          
150 1           i_push_errorf(0, "qoi_colorspace must be %d or %d", QOI_SRGB, QOI_LINEAR);
151 1           return 0;
152             }
153              
154             /* no greyscale */
155 4 50         channels = (i_img_has_alpha(im) ? 1 : 0) + 3;
156             /* do unsigned arithmetic to avoid undefined behaviour that the compiler
157             might decide to optimize away.
158             */
159 4           data_size = (size_t)im->xsize * (size_t)im->ysize * (size_t)channels;
160 4 50         if (data_size / (size_t)im->xsize / (size_t)im->ysize != (size_t)channels) {
161 0           i_push_error(0, "temporary image buffer size too large");
162 0           return 0;
163             }
164 4 50         if (data_size > INT_MAX ||
    50          
165 4           im->ysize >= QOI_PIXELS_MAX / im->xsize) {
166             /* qoi.h uses int for pointer offsets */
167 0           i_push_error(0, "image too large for qoi implementation");
168 0           return 0;
169             }
170              
171 4           data = malloc(data_size);
172 4 50         if (data == NULL) {
173 0           i_push_error(0, "out of memory");
174 0           goto fail;
175             }
176              
177 4 100         chans = i_img_color_channels(im) < 3 ? gray_chans : NULL;
178              
179 4           row_size = im->xsize * channels;
180 604 100         for (y = 0; y < im->ysize; ++y) {
181 600           i_gsamp(im, 0, im->xsize, y, data + row_size * y, chans, channels);
182             }
183              
184 4           desc.width = im->xsize;
185 4           desc.height = im->ysize;
186 4           desc.channels = channels;
187 4           desc.colorspace = colorspace;
188              
189 4           image_data = qoi_encode(data, &desc, &out_len);
190 4 50         if (image_data == NULL) {
191             /* we don't get any other diagnostics */
192 0           i_push_error(0, "unknown failure to write QOI image");
193 0           goto fail;
194             }
195              
196 4           free(data);
197 4           data = NULL;
198              
199 4 50         if (i_io_write(ig, image_data, out_len) != out_len) {
200 0           i_push_error(0, "write failed for image data");
201 0           goto fail;
202             }
203              
204 4 50         if (i_io_close(ig)) {
205 0           i_push_error(0, "failed to close");
206 0           goto fail;
207             }
208            
209 4           return 1;
210              
211             fail:
212 0           free(data);
213 5           return 0;
214             }
215              
216             undef_int
217 0           i_writeqoi_multi(io_glue *ig, i_img **imgs, int count) {
218 0 0         if (count != 1) {
219 0           i_clear_error();
220              
221 0           i_push_error(0, "QOI allows only a single image");
222 0           return 0;
223             }
224 0           return i_writeqoi(imgs[0], ig);
225             }
226