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