-
Notifications
You must be signed in to change notification settings - Fork 137
/
Copy pathimage.cc
439 lines (399 loc) · 16 KB
/
image.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
// After editing this file, run "go generate" in the ../data directory.
// Copyright 2021 The Wuffs Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ---------------- Auxiliary - Image
#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__AUX__IMAGE)
#include <utility>
namespace wuffs_aux {
DecodeImageResult::DecodeImageResult(MemOwner&& pixbuf_mem_owner0,
wuffs_base__pixel_buffer pixbuf0,
std::string&& error_message0)
: pixbuf_mem_owner(std::move(pixbuf_mem_owner0)),
pixbuf(pixbuf0),
error_message(std::move(error_message0)) {}
DecodeImageResult::DecodeImageResult(std::string&& error_message0)
: pixbuf_mem_owner(nullptr, &free),
pixbuf(wuffs_base__null_pixel_buffer()),
error_message(std::move(error_message0)) {}
DecodeImageCallbacks::~DecodeImageCallbacks() {}
DecodeImageCallbacks::AllocPixbufResult::AllocPixbufResult(
MemOwner&& mem_owner0,
wuffs_base__pixel_buffer pixbuf0)
: mem_owner(std::move(mem_owner0)), pixbuf(pixbuf0), error_message("") {}
DecodeImageCallbacks::AllocPixbufResult::AllocPixbufResult(
std::string&& error_message0)
: mem_owner(nullptr, &free),
pixbuf(wuffs_base__null_pixel_buffer()),
error_message(std::move(error_message0)) {}
DecodeImageCallbacks::AllocWorkbufResult::AllocWorkbufResult(
MemOwner&& mem_owner0,
wuffs_base__slice_u8 workbuf0)
: mem_owner(std::move(mem_owner0)), workbuf(workbuf0), error_message("") {}
DecodeImageCallbacks::AllocWorkbufResult::AllocWorkbufResult(
std::string&& error_message0)
: mem_owner(nullptr, &free),
workbuf(wuffs_base__empty_slice_u8()),
error_message(std::move(error_message0)) {}
wuffs_base__image_decoder::unique_ptr //
DecodeImageCallbacks::SelectDecoder(uint32_t fourcc,
wuffs_base__slice_u8 prefix) {
switch (fourcc) {
#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__BMP)
case WUFFS_BASE__FOURCC__BMP:
return wuffs_bmp__decoder::alloc_as__wuffs_base__image_decoder();
#endif
#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__GIF)
case WUFFS_BASE__FOURCC__GIF:
return wuffs_gif__decoder::alloc_as__wuffs_base__image_decoder();
#endif
#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__NIE)
case WUFFS_BASE__FOURCC__NIE:
return wuffs_nie__decoder::alloc_as__wuffs_base__image_decoder();
#endif
#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__PNG)
case WUFFS_BASE__FOURCC__PNG: {
auto dec = wuffs_png__decoder::alloc_as__wuffs_base__image_decoder();
// Favor faster decodes over rejecting invalid checksums.
dec->set_quirk_enabled(WUFFS_BASE__QUIRK_IGNORE_CHECKSUM, true);
return dec;
}
#endif
#if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__WBMP)
case WUFFS_BASE__FOURCC__WBMP:
return wuffs_wbmp__decoder::alloc_as__wuffs_base__image_decoder();
#endif
}
return wuffs_base__image_decoder::unique_ptr(nullptr, &free);
}
wuffs_base__pixel_format //
DecodeImageCallbacks::SelectPixfmt(
const wuffs_base__image_config& image_config) {
return wuffs_base__make_pixel_format(WUFFS_BASE__PIXEL_FORMAT__BGRA_PREMUL);
}
DecodeImageCallbacks::AllocPixbufResult //
DecodeImageCallbacks::AllocPixbuf(const wuffs_base__image_config& image_config,
bool allow_uninitialized_memory) {
uint32_t w = image_config.pixcfg.width();
uint32_t h = image_config.pixcfg.height();
if ((w == 0) || (h == 0)) {
return AllocPixbufResult("");
}
uint64_t len = image_config.pixcfg.pixbuf_len();
if ((len == 0) || (SIZE_MAX < len)) {
return AllocPixbufResult(DecodeImage_UnsupportedPixelConfiguration);
}
void* ptr =
allow_uninitialized_memory ? malloc((size_t)len) : calloc((size_t)len, 1);
if (!ptr) {
return AllocPixbufResult(DecodeImage_OutOfMemory);
}
wuffs_base__pixel_buffer pixbuf;
wuffs_base__status status = pixbuf.set_from_slice(
&image_config.pixcfg,
wuffs_base__make_slice_u8((uint8_t*)ptr, (size_t)len));
if (!status.is_ok()) {
free(ptr);
return AllocPixbufResult(status.message());
}
return AllocPixbufResult(MemOwner(ptr, &free), pixbuf);
}
DecodeImageCallbacks::AllocWorkbufResult //
DecodeImageCallbacks::AllocWorkbuf(wuffs_base__range_ii_u64 len_range,
bool allow_uninitialized_memory) {
uint64_t len = len_range.max_incl;
if (len == 0) {
return AllocWorkbufResult("");
} else if (SIZE_MAX < len) {
return AllocWorkbufResult(DecodeImage_OutOfMemory);
}
void* ptr =
allow_uninitialized_memory ? malloc((size_t)len) : calloc((size_t)len, 1);
if (!ptr) {
return AllocWorkbufResult(DecodeImage_OutOfMemory);
}
return AllocWorkbufResult(
MemOwner(ptr, &free),
wuffs_base__make_slice_u8((uint8_t*)ptr, (size_t)len));
}
void //
DecodeImageCallbacks::Done(
DecodeImageResult& result,
sync_io::Input& input,
IOBuffer& buffer,
wuffs_base__image_decoder::unique_ptr image_decoder) {}
const char DecodeImage_BufferIsTooShort[] = //
"wuffs_aux::DecodeImage: buffer is too short";
const char DecodeImage_MaxInclDimensionExceeded[] = //
"wuffs_aux::DecodeImage: max_incl_dimension exceeded";
const char DecodeImage_OutOfMemory[] = //
"wuffs_aux::DecodeImage: out of memory";
const char DecodeImage_UnexpectedEndOfFile[] = //
"wuffs_aux::DecodeImage: unexpected end of file";
const char DecodeImage_UnsupportedImageFormat[] = //
"wuffs_aux::DecodeImage: unsupported image format";
const char DecodeImage_UnsupportedPixelBlend[] = //
"wuffs_aux::DecodeImage: unsupported pixel blend";
const char DecodeImage_UnsupportedPixelConfiguration[] = //
"wuffs_aux::DecodeImage: unsupported pixel configuration";
const char DecodeImage_UnsupportedPixelFormat[] = //
"wuffs_aux::DecodeImage: unsupported pixel format";
// --------
namespace {
std::string //
DecodeImageAdvanceIOBuf(sync_io::Input& input,
wuffs_base__io_buffer& io_buf,
bool compactable,
uint64_t min_excl_pos,
uint64_t pos) {
if ((pos <= min_excl_pos) || (pos < io_buf.reader_position())) {
// Redirects must go forward.
return DecodeImage_UnsupportedImageFormat;
}
while (true) {
uint64_t relative_pos = pos - io_buf.reader_position();
if (relative_pos <= io_buf.reader_length()) {
io_buf.meta.ri += (size_t)relative_pos;
break;
} else if (io_buf.meta.closed) {
return DecodeImage_UnexpectedEndOfFile;
}
io_buf.meta.ri = io_buf.meta.wi;
if (compactable) {
io_buf.compact();
}
std::string error_message = input.CopyIn(&io_buf);
if (!error_message.empty()) {
return error_message;
}
}
return "";
}
DecodeImageResult //
DecodeImage0(wuffs_base__image_decoder::unique_ptr& image_decoder,
DecodeImageCallbacks& callbacks,
sync_io::Input& input,
wuffs_base__io_buffer& io_buf,
wuffs_base__pixel_blend pixel_blend,
wuffs_base__color_u32_argb_premul background_color,
uint32_t max_incl_dimension) {
// Check args.
switch (pixel_blend) {
case WUFFS_BASE__PIXEL_BLEND__SRC:
case WUFFS_BASE__PIXEL_BLEND__SRC_OVER:
break;
default:
return DecodeImageResult(DecodeImage_UnsupportedPixelBlend);
}
wuffs_base__image_config image_config = wuffs_base__null_image_config();
uint64_t start_pos = io_buf.reader_position();
bool redirected = false;
int32_t fourcc = 0;
redirect:
do {
// Determine the image format.
if (!redirected) {
while (true) {
fourcc = wuffs_base__magic_number_guess_fourcc(io_buf.reader_slice());
if (fourcc > 0) {
break;
} else if ((fourcc == 0) && (io_buf.reader_length() >= 64)) {
break;
} else if (io_buf.meta.closed || (io_buf.writer_length() == 0)) {
fourcc = 0;
break;
}
std::string error_message = input.CopyIn(&io_buf);
if (!error_message.empty()) {
return DecodeImageResult(std::move(error_message));
}
}
} else {
wuffs_base__io_buffer empty = wuffs_base__empty_io_buffer();
wuffs_base__more_information minfo = wuffs_base__empty_more_information();
wuffs_base__status tmm_status =
image_decoder->tell_me_more(&empty, &minfo, &io_buf);
if (tmm_status.repr != nullptr) {
return DecodeImageResult(tmm_status.message());
}
if (minfo.flavor != WUFFS_BASE__MORE_INFORMATION__FLAVOR__IO_REDIRECT) {
return DecodeImageResult(DecodeImage_UnsupportedImageFormat);
}
uint64_t pos = minfo.io_redirect__range().min_incl;
std::string error_message = DecodeImageAdvanceIOBuf(
input, io_buf, !input.BringsItsOwnIOBuffer(), start_pos, pos);
if (!error_message.empty()) {
return DecodeImageResult(std::move(error_message));
}
fourcc = (int32_t)(minfo.io_redirect__fourcc());
if (fourcc == 0) {
return DecodeImageResult(DecodeImage_UnsupportedImageFormat);
}
image_decoder.reset();
}
// Select the image decoder.
image_decoder = callbacks.SelectDecoder(
(uint32_t)fourcc,
fourcc ? wuffs_base__empty_slice_u8() : io_buf.reader_slice());
if (!image_decoder) {
return DecodeImageResult(DecodeImage_UnsupportedImageFormat);
}
// Decode the image config.
while (true) {
wuffs_base__status id_dic_status =
image_decoder->decode_image_config(&image_config, &io_buf);
if (id_dic_status.repr == nullptr) {
break;
} else if (id_dic_status.repr == wuffs_base__note__i_o_redirect) {
if (redirected) {
return DecodeImageResult(DecodeImage_UnsupportedImageFormat);
}
redirected = true;
goto redirect;
} else if (id_dic_status.repr != wuffs_base__suspension__short_read) {
return DecodeImageResult(id_dic_status.message());
} else if (io_buf.meta.closed) {
return DecodeImageResult(DecodeImage_UnexpectedEndOfFile);
} else {
std::string error_message = input.CopyIn(&io_buf);
if (!error_message.empty()) {
return DecodeImageResult(std::move(error_message));
}
}
}
} while (false);
// Select the pixel format.
uint32_t w = image_config.pixcfg.width();
uint32_t h = image_config.pixcfg.height();
if ((w > max_incl_dimension) || (h > max_incl_dimension)) {
return DecodeImageResult(DecodeImage_MaxInclDimensionExceeded);
}
wuffs_base__pixel_format pixel_format = callbacks.SelectPixfmt(image_config);
if (pixel_format.repr != image_config.pixcfg.pixel_format().repr) {
switch (pixel_format.repr) {
case WUFFS_BASE__PIXEL_FORMAT__BGR_565:
case WUFFS_BASE__PIXEL_FORMAT__BGR:
case WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL:
case WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL_4X16LE:
case WUFFS_BASE__PIXEL_FORMAT__BGRA_PREMUL:
case WUFFS_BASE__PIXEL_FORMAT__RGBA_NONPREMUL:
case WUFFS_BASE__PIXEL_FORMAT__RGBA_PREMUL:
break;
default:
return DecodeImageResult(DecodeImage_UnsupportedPixelFormat);
}
image_config.pixcfg.set(pixel_format.repr,
WUFFS_BASE__PIXEL_SUBSAMPLING__NONE, w, h);
}
// Allocate the pixel buffer.
bool valid_background_color =
wuffs_base__color_u32_argb_premul__is_valid(background_color);
DecodeImageCallbacks::AllocPixbufResult alloc_pixbuf_result =
callbacks.AllocPixbuf(image_config, valid_background_color);
if (!alloc_pixbuf_result.error_message.empty()) {
return DecodeImageResult(std::move(alloc_pixbuf_result.error_message));
}
wuffs_base__pixel_buffer pixel_buffer = alloc_pixbuf_result.pixbuf;
if (valid_background_color) {
wuffs_base__status pb_scufr_status = pixel_buffer.set_color_u32_fill_rect(
pixel_buffer.pixcfg.bounds(), background_color);
if (pb_scufr_status.repr != nullptr) {
return DecodeImageResult(pb_scufr_status.message());
}
}
// Allocate the work buffer. Wuffs' decoders conventionally assume that this
// can be uninitialized memory.
wuffs_base__range_ii_u64 workbuf_len = image_decoder->workbuf_len();
DecodeImageCallbacks::AllocWorkbufResult alloc_workbuf_result =
callbacks.AllocWorkbuf(workbuf_len, true);
if (!alloc_workbuf_result.error_message.empty()) {
return DecodeImageResult(std::move(alloc_workbuf_result.error_message));
} else if (alloc_workbuf_result.workbuf.len < workbuf_len.min_incl) {
return DecodeImageResult(DecodeImage_BufferIsTooShort);
}
// Decode the frame config.
wuffs_base__frame_config frame_config = wuffs_base__null_frame_config();
while (true) {
wuffs_base__status id_dfc_status =
image_decoder->decode_frame_config(&frame_config, &io_buf);
if (id_dfc_status.repr == nullptr) {
break;
} else if (id_dfc_status.repr != wuffs_base__suspension__short_read) {
return DecodeImageResult(id_dfc_status.message());
} else if (io_buf.meta.closed) {
return DecodeImageResult(DecodeImage_UnexpectedEndOfFile);
} else {
std::string error_message = input.CopyIn(&io_buf);
if (!error_message.empty()) {
return DecodeImageResult(std::move(error_message));
}
}
}
// Decode the frame (the pixels).
//
// From here on, always returns the pixel_buffer. If we get this far, we can
// still display a partial image, even if we encounter an error.
std::string message("");
if ((pixel_blend == WUFFS_BASE__PIXEL_BLEND__SRC_OVER) &&
frame_config.overwrite_instead_of_blend()) {
pixel_blend = WUFFS_BASE__PIXEL_BLEND__SRC;
}
while (true) {
wuffs_base__status id_df_status =
image_decoder->decode_frame(&pixel_buffer, &io_buf, pixel_blend,
alloc_workbuf_result.workbuf, nullptr);
if (id_df_status.repr == nullptr) {
break;
} else if (id_df_status.repr != wuffs_base__suspension__short_read) {
message = id_df_status.message();
break;
} else if (io_buf.meta.closed) {
message = DecodeImage_UnexpectedEndOfFile;
break;
} else {
std::string error_message = input.CopyIn(&io_buf);
if (!error_message.empty()) {
message = std::move(error_message);
break;
}
}
}
return DecodeImageResult(std::move(alloc_pixbuf_result.mem_owner),
pixel_buffer, std::move(message));
}
} // namespace
DecodeImageResult //
DecodeImage(DecodeImageCallbacks& callbacks,
sync_io::Input& input,
wuffs_base__pixel_blend pixel_blend,
wuffs_base__color_u32_argb_premul background_color,
uint32_t max_incl_dimension) {
wuffs_base__io_buffer* io_buf = input.BringsItsOwnIOBuffer();
wuffs_base__io_buffer fallback_io_buf = wuffs_base__empty_io_buffer();
std::unique_ptr<uint8_t[]> fallback_io_array(nullptr);
if (!io_buf) {
fallback_io_array = std::unique_ptr<uint8_t[]>(new uint8_t[32768]);
fallback_io_buf =
wuffs_base__ptr_u8__writer(fallback_io_array.get(), 32768);
io_buf = &fallback_io_buf;
}
wuffs_base__image_decoder::unique_ptr image_decoder(nullptr, &free);
DecodeImageResult result =
DecodeImage0(image_decoder, callbacks, input, *io_buf, pixel_blend,
background_color, max_incl_dimension);
callbacks.Done(result, input, *io_buf, std::move(image_decoder));
return result;
}
} // namespace wuffs_aux
#endif // !defined(WUFFS_CONFIG__MODULES) ||
// defined(WUFFS_CONFIG__MODULE__AUX__IMAGE)