Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/3rdparty/libwebp/src/demux/anim_decode.c
16345 views
1
// Copyright 2015 Google Inc. All Rights Reserved.
2
//
3
// Use of this source code is governed by a BSD-style license
4
// that can be found in the COPYING file in the root of the source
5
// tree. An additional intellectual property rights grant can be found
6
// in the file PATENTS. All contributing project authors may
7
// be found in the AUTHORS file in the root of the source tree.
8
// -----------------------------------------------------------------------------
9
//
10
// AnimDecoder implementation.
11
//
12
13
#ifdef HAVE_CONFIG_H
14
#include "src/webp/config.h"
15
#endif
16
17
#include <assert.h>
18
#include <string.h>
19
20
#include "src/utils/utils.h"
21
#include "src/webp/decode.h"
22
#include "src/webp/demux.h"
23
24
#define NUM_CHANNELS 4
25
26
typedef void (*BlendRowFunc)(uint32_t* const, const uint32_t* const, int);
27
static void BlendPixelRowNonPremult(uint32_t* const src,
28
const uint32_t* const dst, int num_pixels);
29
static void BlendPixelRowPremult(uint32_t* const src, const uint32_t* const dst,
30
int num_pixels);
31
32
struct WebPAnimDecoder {
33
WebPDemuxer* demux_; // Demuxer created from given WebP bitstream.
34
WebPDecoderConfig config_; // Decoder config.
35
// Note: we use a pointer to a function blending multiple pixels at a time to
36
// allow possible inlining of per-pixel blending function.
37
BlendRowFunc blend_func_; // Pointer to the chose blend row function.
38
WebPAnimInfo info_; // Global info about the animation.
39
uint8_t* curr_frame_; // Current canvas (not disposed).
40
uint8_t* prev_frame_disposed_; // Previous canvas (properly disposed).
41
int prev_frame_timestamp_; // Previous frame timestamp (milliseconds).
42
WebPIterator prev_iter_; // Iterator object for previous frame.
43
int prev_frame_was_keyframe_; // True if previous frame was a keyframe.
44
int next_frame_; // Index of the next frame to be decoded
45
// (starting from 1).
46
};
47
48
static void DefaultDecoderOptions(WebPAnimDecoderOptions* const dec_options) {
49
dec_options->color_mode = MODE_RGBA;
50
dec_options->use_threads = 0;
51
}
52
53
int WebPAnimDecoderOptionsInitInternal(WebPAnimDecoderOptions* dec_options,
54
int abi_version) {
55
if (dec_options == NULL ||
56
WEBP_ABI_IS_INCOMPATIBLE(abi_version, WEBP_DEMUX_ABI_VERSION)) {
57
return 0;
58
}
59
DefaultDecoderOptions(dec_options);
60
return 1;
61
}
62
63
static int ApplyDecoderOptions(const WebPAnimDecoderOptions* const dec_options,
64
WebPAnimDecoder* const dec) {
65
WEBP_CSP_MODE mode;
66
WebPDecoderConfig* config = &dec->config_;
67
assert(dec_options != NULL);
68
69
mode = dec_options->color_mode;
70
if (mode != MODE_RGBA && mode != MODE_BGRA &&
71
mode != MODE_rgbA && mode != MODE_bgrA) {
72
return 0;
73
}
74
dec->blend_func_ = (mode == MODE_RGBA || mode == MODE_BGRA)
75
? &BlendPixelRowNonPremult
76
: &BlendPixelRowPremult;
77
WebPInitDecoderConfig(config);
78
config->output.colorspace = mode;
79
config->output.is_external_memory = 1;
80
config->options.use_threads = dec_options->use_threads;
81
// Note: config->output.u.RGBA is set at the time of decoding each frame.
82
return 1;
83
}
84
85
WebPAnimDecoder* WebPAnimDecoderNewInternal(
86
const WebPData* webp_data, const WebPAnimDecoderOptions* dec_options,
87
int abi_version) {
88
WebPAnimDecoderOptions options;
89
WebPAnimDecoder* dec = NULL;
90
if (webp_data == NULL ||
91
WEBP_ABI_IS_INCOMPATIBLE(abi_version, WEBP_DEMUX_ABI_VERSION)) {
92
return NULL;
93
}
94
95
// Note: calloc() so that the pointer members are initialized to NULL.
96
dec = (WebPAnimDecoder*)WebPSafeCalloc(1ULL, sizeof(*dec));
97
if (dec == NULL) goto Error;
98
99
if (dec_options != NULL) {
100
options = *dec_options;
101
} else {
102
DefaultDecoderOptions(&options);
103
}
104
if (!ApplyDecoderOptions(&options, dec)) goto Error;
105
106
dec->demux_ = WebPDemux(webp_data);
107
if (dec->demux_ == NULL) goto Error;
108
109
dec->info_.canvas_width = WebPDemuxGetI(dec->demux_, WEBP_FF_CANVAS_WIDTH);
110
dec->info_.canvas_height = WebPDemuxGetI(dec->demux_, WEBP_FF_CANVAS_HEIGHT);
111
dec->info_.loop_count = WebPDemuxGetI(dec->demux_, WEBP_FF_LOOP_COUNT);
112
dec->info_.bgcolor = WebPDemuxGetI(dec->demux_, WEBP_FF_BACKGROUND_COLOR);
113
dec->info_.frame_count = WebPDemuxGetI(dec->demux_, WEBP_FF_FRAME_COUNT);
114
115
// Note: calloc() because we fill frame with zeroes as well.
116
dec->curr_frame_ = (uint8_t*)WebPSafeCalloc(
117
dec->info_.canvas_width * NUM_CHANNELS, dec->info_.canvas_height);
118
if (dec->curr_frame_ == NULL) goto Error;
119
dec->prev_frame_disposed_ = (uint8_t*)WebPSafeCalloc(
120
dec->info_.canvas_width * NUM_CHANNELS, dec->info_.canvas_height);
121
if (dec->prev_frame_disposed_ == NULL) goto Error;
122
123
WebPAnimDecoderReset(dec);
124
return dec;
125
126
Error:
127
WebPAnimDecoderDelete(dec);
128
return NULL;
129
}
130
131
int WebPAnimDecoderGetInfo(const WebPAnimDecoder* dec, WebPAnimInfo* info) {
132
if (dec == NULL || info == NULL) return 0;
133
*info = dec->info_;
134
return 1;
135
}
136
137
// Returns true if the frame covers the full canvas.
138
static int IsFullFrame(int width, int height, int canvas_width,
139
int canvas_height) {
140
return (width == canvas_width && height == canvas_height);
141
}
142
143
// Clear the canvas to transparent.
144
static int ZeroFillCanvas(uint8_t* buf, uint32_t canvas_width,
145
uint32_t canvas_height) {
146
const uint64_t size =
147
(uint64_t)canvas_width * canvas_height * NUM_CHANNELS * sizeof(*buf);
148
if (size != (size_t)size) return 0;
149
memset(buf, 0, (size_t)size);
150
return 1;
151
}
152
153
// Clear given frame rectangle to transparent.
154
static void ZeroFillFrameRect(uint8_t* buf, int buf_stride, int x_offset,
155
int y_offset, int width, int height) {
156
int j;
157
assert(width * NUM_CHANNELS <= buf_stride);
158
buf += y_offset * buf_stride + x_offset * NUM_CHANNELS;
159
for (j = 0; j < height; ++j) {
160
memset(buf, 0, width * NUM_CHANNELS);
161
buf += buf_stride;
162
}
163
}
164
165
// Copy width * height pixels from 'src' to 'dst'.
166
static int CopyCanvas(const uint8_t* src, uint8_t* dst,
167
uint32_t width, uint32_t height) {
168
const uint64_t size = (uint64_t)width * height * NUM_CHANNELS;
169
if (size != (size_t)size) return 0;
170
assert(src != NULL && dst != NULL);
171
memcpy(dst, src, (size_t)size);
172
return 1;
173
}
174
175
// Returns true if the current frame is a key-frame.
176
static int IsKeyFrame(const WebPIterator* const curr,
177
const WebPIterator* const prev,
178
int prev_frame_was_key_frame,
179
int canvas_width, int canvas_height) {
180
if (curr->frame_num == 1) {
181
return 1;
182
} else if ((!curr->has_alpha || curr->blend_method == WEBP_MUX_NO_BLEND) &&
183
IsFullFrame(curr->width, curr->height,
184
canvas_width, canvas_height)) {
185
return 1;
186
} else {
187
return (prev->dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) &&
188
(IsFullFrame(prev->width, prev->height, canvas_width,
189
canvas_height) ||
190
prev_frame_was_key_frame);
191
}
192
}
193
194
195
// Blend a single channel of 'src' over 'dst', given their alpha channel values.
196
// 'src' and 'dst' are assumed to be NOT pre-multiplied by alpha.
197
static uint8_t BlendChannelNonPremult(uint32_t src, uint8_t src_a,
198
uint32_t dst, uint8_t dst_a,
199
uint32_t scale, int shift) {
200
const uint8_t src_channel = (src >> shift) & 0xff;
201
const uint8_t dst_channel = (dst >> shift) & 0xff;
202
const uint32_t blend_unscaled = src_channel * src_a + dst_channel * dst_a;
203
assert(blend_unscaled < (1ULL << 32) / scale);
204
return (blend_unscaled * scale) >> 24;
205
}
206
207
// Blend 'src' over 'dst' assuming they are NOT pre-multiplied by alpha.
208
static uint32_t BlendPixelNonPremult(uint32_t src, uint32_t dst) {
209
const uint8_t src_a = (src >> 24) & 0xff;
210
211
if (src_a == 0) {
212
return dst;
213
} else {
214
const uint8_t dst_a = (dst >> 24) & 0xff;
215
// This is the approximate integer arithmetic for the actual formula:
216
// dst_factor_a = (dst_a * (255 - src_a)) / 255.
217
const uint8_t dst_factor_a = (dst_a * (256 - src_a)) >> 8;
218
const uint8_t blend_a = src_a + dst_factor_a;
219
const uint32_t scale = (1UL << 24) / blend_a;
220
221
const uint8_t blend_r =
222
BlendChannelNonPremult(src, src_a, dst, dst_factor_a, scale, 0);
223
const uint8_t blend_g =
224
BlendChannelNonPremult(src, src_a, dst, dst_factor_a, scale, 8);
225
const uint8_t blend_b =
226
BlendChannelNonPremult(src, src_a, dst, dst_factor_a, scale, 16);
227
assert(src_a + dst_factor_a < 256);
228
229
return (blend_r << 0) |
230
(blend_g << 8) |
231
(blend_b << 16) |
232
((uint32_t)blend_a << 24);
233
}
234
}
235
236
// Blend 'num_pixels' in 'src' over 'dst' assuming they are NOT pre-multiplied
237
// by alpha.
238
static void BlendPixelRowNonPremult(uint32_t* const src,
239
const uint32_t* const dst, int num_pixels) {
240
int i;
241
for (i = 0; i < num_pixels; ++i) {
242
const uint8_t src_alpha = (src[i] >> 24) & 0xff;
243
if (src_alpha != 0xff) {
244
src[i] = BlendPixelNonPremult(src[i], dst[i]);
245
}
246
}
247
}
248
249
// Individually multiply each channel in 'pix' by 'scale'.
250
static WEBP_INLINE uint32_t ChannelwiseMultiply(uint32_t pix, uint32_t scale) {
251
uint32_t mask = 0x00FF00FF;
252
uint32_t rb = ((pix & mask) * scale) >> 8;
253
uint32_t ag = ((pix >> 8) & mask) * scale;
254
return (rb & mask) | (ag & ~mask);
255
}
256
257
// Blend 'src' over 'dst' assuming they are pre-multiplied by alpha.
258
static uint32_t BlendPixelPremult(uint32_t src, uint32_t dst) {
259
const uint8_t src_a = (src >> 24) & 0xff;
260
return src + ChannelwiseMultiply(dst, 256 - src_a);
261
}
262
263
// Blend 'num_pixels' in 'src' over 'dst' assuming they are pre-multiplied by
264
// alpha.
265
static void BlendPixelRowPremult(uint32_t* const src, const uint32_t* const dst,
266
int num_pixels) {
267
int i;
268
for (i = 0; i < num_pixels; ++i) {
269
const uint8_t src_alpha = (src[i] >> 24) & 0xff;
270
if (src_alpha != 0xff) {
271
src[i] = BlendPixelPremult(src[i], dst[i]);
272
}
273
}
274
}
275
276
// Returns two ranges (<left, width> pairs) at row 'canvas_y', that belong to
277
// 'src' but not 'dst'. A point range is empty if the corresponding width is 0.
278
static void FindBlendRangeAtRow(const WebPIterator* const src,
279
const WebPIterator* const dst, int canvas_y,
280
int* const left1, int* const width1,
281
int* const left2, int* const width2) {
282
const int src_max_x = src->x_offset + src->width;
283
const int dst_max_x = dst->x_offset + dst->width;
284
const int dst_max_y = dst->y_offset + dst->height;
285
assert(canvas_y >= src->y_offset && canvas_y < (src->y_offset + src->height));
286
*left1 = -1;
287
*width1 = 0;
288
*left2 = -1;
289
*width2 = 0;
290
291
if (canvas_y < dst->y_offset || canvas_y >= dst_max_y ||
292
src->x_offset >= dst_max_x || src_max_x <= dst->x_offset) {
293
*left1 = src->x_offset;
294
*width1 = src->width;
295
return;
296
}
297
298
if (src->x_offset < dst->x_offset) {
299
*left1 = src->x_offset;
300
*width1 = dst->x_offset - src->x_offset;
301
}
302
303
if (src_max_x > dst_max_x) {
304
*left2 = dst_max_x;
305
*width2 = src_max_x - dst_max_x;
306
}
307
}
308
309
int WebPAnimDecoderGetNext(WebPAnimDecoder* dec,
310
uint8_t** buf_ptr, int* timestamp_ptr) {
311
WebPIterator iter;
312
uint32_t width;
313
uint32_t height;
314
int is_key_frame;
315
int timestamp;
316
BlendRowFunc blend_row;
317
318
if (dec == NULL || buf_ptr == NULL || timestamp_ptr == NULL) return 0;
319
if (!WebPAnimDecoderHasMoreFrames(dec)) return 0;
320
321
width = dec->info_.canvas_width;
322
height = dec->info_.canvas_height;
323
blend_row = dec->blend_func_;
324
325
// Get compressed frame.
326
if (!WebPDemuxGetFrame(dec->demux_, dec->next_frame_, &iter)) {
327
return 0;
328
}
329
timestamp = dec->prev_frame_timestamp_ + iter.duration;
330
331
// Initialize.
332
is_key_frame = IsKeyFrame(&iter, &dec->prev_iter_,
333
dec->prev_frame_was_keyframe_, width, height);
334
if (is_key_frame) {
335
if (!ZeroFillCanvas(dec->curr_frame_, width, height)) {
336
goto Error;
337
}
338
} else {
339
if (!CopyCanvas(dec->prev_frame_disposed_, dec->curr_frame_,
340
width, height)) {
341
goto Error;
342
}
343
}
344
345
// Decode.
346
{
347
const uint8_t* in = iter.fragment.bytes;
348
const size_t in_size = iter.fragment.size;
349
const size_t out_offset =
350
(iter.y_offset * width + iter.x_offset) * NUM_CHANNELS;
351
WebPDecoderConfig* const config = &dec->config_;
352
WebPRGBABuffer* const buf = &config->output.u.RGBA;
353
buf->stride = NUM_CHANNELS * width;
354
buf->size = buf->stride * iter.height;
355
buf->rgba = dec->curr_frame_ + out_offset;
356
357
if (WebPDecode(in, in_size, config) != VP8_STATUS_OK) {
358
goto Error;
359
}
360
}
361
362
// During the decoding of current frame, we may have set some pixels to be
363
// transparent (i.e. alpha < 255). However, the value of each of these
364
// pixels should have been determined by blending it against the value of
365
// that pixel in the previous frame if blending method of is WEBP_MUX_BLEND.
366
if (iter.frame_num > 1 && iter.blend_method == WEBP_MUX_BLEND &&
367
!is_key_frame) {
368
if (dec->prev_iter_.dispose_method == WEBP_MUX_DISPOSE_NONE) {
369
int y;
370
// Blend transparent pixels with pixels in previous canvas.
371
for (y = 0; y < iter.height; ++y) {
372
const size_t offset =
373
(iter.y_offset + y) * width + iter.x_offset;
374
blend_row((uint32_t*)dec->curr_frame_ + offset,
375
(uint32_t*)dec->prev_frame_disposed_ + offset, iter.width);
376
}
377
} else {
378
int y;
379
assert(dec->prev_iter_.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND);
380
// We need to blend a transparent pixel with its value just after
381
// initialization. That is, blend it with:
382
// * Fully transparent pixel if it belongs to prevRect <-- No-op.
383
// * The pixel in the previous canvas otherwise <-- Need alpha-blending.
384
for (y = 0; y < iter.height; ++y) {
385
const int canvas_y = iter.y_offset + y;
386
int left1, width1, left2, width2;
387
FindBlendRangeAtRow(&iter, &dec->prev_iter_, canvas_y, &left1, &width1,
388
&left2, &width2);
389
if (width1 > 0) {
390
const size_t offset1 = canvas_y * width + left1;
391
blend_row((uint32_t*)dec->curr_frame_ + offset1,
392
(uint32_t*)dec->prev_frame_disposed_ + offset1, width1);
393
}
394
if (width2 > 0) {
395
const size_t offset2 = canvas_y * width + left2;
396
blend_row((uint32_t*)dec->curr_frame_ + offset2,
397
(uint32_t*)dec->prev_frame_disposed_ + offset2, width2);
398
}
399
}
400
}
401
}
402
403
// Update info of the previous frame and dispose it for the next iteration.
404
dec->prev_frame_timestamp_ = timestamp;
405
WebPDemuxReleaseIterator(&dec->prev_iter_);
406
dec->prev_iter_ = iter;
407
dec->prev_frame_was_keyframe_ = is_key_frame;
408
CopyCanvas(dec->curr_frame_, dec->prev_frame_disposed_, width, height);
409
if (dec->prev_iter_.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) {
410
ZeroFillFrameRect(dec->prev_frame_disposed_, width * NUM_CHANNELS,
411
dec->prev_iter_.x_offset, dec->prev_iter_.y_offset,
412
dec->prev_iter_.width, dec->prev_iter_.height);
413
}
414
++dec->next_frame_;
415
416
// All OK, fill in the values.
417
*buf_ptr = dec->curr_frame_;
418
*timestamp_ptr = timestamp;
419
return 1;
420
421
Error:
422
WebPDemuxReleaseIterator(&iter);
423
return 0;
424
}
425
426
int WebPAnimDecoderHasMoreFrames(const WebPAnimDecoder* dec) {
427
if (dec == NULL) return 0;
428
return (dec->next_frame_ <= (int)dec->info_.frame_count);
429
}
430
431
void WebPAnimDecoderReset(WebPAnimDecoder* dec) {
432
if (dec != NULL) {
433
dec->prev_frame_timestamp_ = 0;
434
WebPDemuxReleaseIterator(&dec->prev_iter_);
435
memset(&dec->prev_iter_, 0, sizeof(dec->prev_iter_));
436
dec->prev_frame_was_keyframe_ = 0;
437
dec->next_frame_ = 1;
438
}
439
}
440
441
const WebPDemuxer* WebPAnimDecoderGetDemuxer(const WebPAnimDecoder* dec) {
442
if (dec == NULL) return NULL;
443
return dec->demux_;
444
}
445
446
void WebPAnimDecoderDelete(WebPAnimDecoder* dec) {
447
if (dec != NULL) {
448
WebPDemuxReleaseIterator(&dec->prev_iter_);
449
WebPDemuxDelete(dec->demux_);
450
WebPSafeFree(dec->curr_frame_);
451
WebPSafeFree(dec->prev_frame_disposed_);
452
WebPSafeFree(dec);
453
}
454
}
455
456