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