| 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
|
|
|
|
|
|
|
|