| OLD | NEW |
| 1 // Copyright 2015 Google Inc. All Rights Reserved. | 1 // Copyright 2015 Google Inc. All Rights Reserved. |
| 2 // | 2 // |
| 3 // Use of this source code is governed by a BSD-style license | 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 | 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 | 5 // tree. An additional intellectual property rights grant can be found |
| 6 // in the file PATENTS. All contributing project authors may | 6 // in the file PATENTS. All contributing project authors may |
| 7 // be found in the AUTHORS file in the root of the source tree. | 7 // be found in the AUTHORS file in the root of the source tree. |
| 8 // ----------------------------------------------------------------------------- | 8 // ----------------------------------------------------------------------------- |
| 9 // | 9 // |
| 10 // AnimDecoder implementation. | 10 // AnimDecoder implementation. |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 105 | 105 |
| 106 dec->demux_ = WebPDemux(webp_data); | 106 dec->demux_ = WebPDemux(webp_data); |
| 107 if (dec->demux_ == NULL) goto Error; | 107 if (dec->demux_ == NULL) goto Error; |
| 108 | 108 |
| 109 dec->info_.canvas_width = WebPDemuxGetI(dec->demux_, WEBP_FF_CANVAS_WIDTH); | 109 dec->info_.canvas_width = WebPDemuxGetI(dec->demux_, WEBP_FF_CANVAS_WIDTH); |
| 110 dec->info_.canvas_height = WebPDemuxGetI(dec->demux_, WEBP_FF_CANVAS_HEIGHT); | 110 dec->info_.canvas_height = WebPDemuxGetI(dec->demux_, WEBP_FF_CANVAS_HEIGHT); |
| 111 dec->info_.loop_count = WebPDemuxGetI(dec->demux_, WEBP_FF_LOOP_COUNT); | 111 dec->info_.loop_count = WebPDemuxGetI(dec->demux_, WEBP_FF_LOOP_COUNT); |
| 112 dec->info_.bgcolor = WebPDemuxGetI(dec->demux_, WEBP_FF_BACKGROUND_COLOR); | 112 dec->info_.bgcolor = WebPDemuxGetI(dec->demux_, WEBP_FF_BACKGROUND_COLOR); |
| 113 dec->info_.frame_count = WebPDemuxGetI(dec->demux_, WEBP_FF_FRAME_COUNT); | 113 dec->info_.frame_count = WebPDemuxGetI(dec->demux_, WEBP_FF_FRAME_COUNT); |
| 114 | 114 |
| 115 { | 115 // Note: calloc() because we fill frame with zeroes as well. |
| 116 const int canvas_bytes = | 116 dec->curr_frame_ = (uint8_t*)WebPSafeCalloc( |
| 117 dec->info_.canvas_width * NUM_CHANNELS * dec->info_.canvas_height; | 117 dec->info_.canvas_width * NUM_CHANNELS, dec->info_.canvas_height); |
| 118 // Note: calloc() because we fill frame with zeroes as well. | 118 if (dec->curr_frame_ == NULL) goto Error; |
| 119 dec->curr_frame_ = WebPSafeCalloc(1ULL, canvas_bytes); | 119 dec->prev_frame_disposed_ = (uint8_t*)WebPSafeCalloc( |
| 120 if (dec->curr_frame_ == NULL) goto Error; | 120 dec->info_.canvas_width * NUM_CHANNELS, dec->info_.canvas_height); |
| 121 dec->prev_frame_disposed_ = WebPSafeCalloc(1ULL, canvas_bytes); | 121 if (dec->prev_frame_disposed_ == NULL) goto Error; |
| 122 if (dec->prev_frame_disposed_ == NULL) goto Error; | |
| 123 } | |
| 124 | 122 |
| 125 WebPAnimDecoderReset(dec); | 123 WebPAnimDecoderReset(dec); |
| 126 | |
| 127 return dec; | 124 return dec; |
| 128 | 125 |
| 129 Error: | 126 Error: |
| 130 WebPAnimDecoderDelete(dec); | 127 WebPAnimDecoderDelete(dec); |
| 131 return NULL; | 128 return NULL; |
| 132 } | 129 } |
| 133 | 130 |
| 134 int WebPAnimDecoderGetInfo(const WebPAnimDecoder* dec, WebPAnimInfo* info) { | 131 int WebPAnimDecoderGetInfo(const WebPAnimDecoder* dec, WebPAnimInfo* info) { |
| 135 if (dec == NULL || info == NULL) return 0; | 132 if (dec == NULL || info == NULL) return 0; |
| 136 *info = dec->info_; | 133 *info = dec->info_; |
| 137 return 1; | 134 return 1; |
| 138 } | 135 } |
| 139 | 136 |
| 140 // Returns true if the frame covers the full canvas. | 137 // Returns true if the frame covers the full canvas. |
| 141 static int IsFullFrame(int width, int height, int canvas_width, | 138 static int IsFullFrame(int width, int height, int canvas_width, |
| 142 int canvas_height) { | 139 int canvas_height) { |
| 143 return (width == canvas_width && height == canvas_height); | 140 return (width == canvas_width && height == canvas_height); |
| 144 } | 141 } |
| 145 | 142 |
| 146 // Clear the canvas to transparent. | 143 // Clear the canvas to transparent. |
| 147 static void ZeroFillCanvas(uint8_t* buf, uint32_t canvas_width, | 144 static int ZeroFillCanvas(uint8_t* buf, uint32_t canvas_width, |
| 148 uint32_t canvas_height) { | 145 uint32_t canvas_height) { |
| 149 memset(buf, 0, canvas_width * NUM_CHANNELS * 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; |
| 150 } | 151 } |
| 151 | 152 |
| 152 // Clear given frame rectangle to transparent. | 153 // Clear given frame rectangle to transparent. |
| 153 static void ZeroFillFrameRect(uint8_t* buf, int buf_stride, int x_offset, | 154 static void ZeroFillFrameRect(uint8_t* buf, int buf_stride, int x_offset, |
| 154 int y_offset, int width, int height) { | 155 int y_offset, int width, int height) { |
| 155 int j; | 156 int j; |
| 156 assert(width * NUM_CHANNELS <= buf_stride); | 157 assert(width * NUM_CHANNELS <= buf_stride); |
| 157 buf += y_offset * buf_stride + x_offset * NUM_CHANNELS; | 158 buf += y_offset * buf_stride + x_offset * NUM_CHANNELS; |
| 158 for (j = 0; j < height; ++j) { | 159 for (j = 0; j < height; ++j) { |
| 159 memset(buf, 0, width * NUM_CHANNELS); | 160 memset(buf, 0, width * NUM_CHANNELS); |
| 160 buf += buf_stride; | 161 buf += buf_stride; |
| 161 } | 162 } |
| 162 } | 163 } |
| 163 | 164 |
| 164 // Copy width * height pixels from 'src' to 'dst'. | 165 // Copy width * height pixels from 'src' to 'dst'. |
| 165 static void CopyCanvas(const uint8_t* src, uint8_t* dst, | 166 static int CopyCanvas(const uint8_t* src, uint8_t* dst, |
| 166 uint32_t width, uint32_t height) { | 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; |
| 167 assert(src != NULL && dst != NULL); | 170 assert(src != NULL && dst != NULL); |
| 168 memcpy(dst, src, width * NUM_CHANNELS * height); | 171 memcpy(dst, src, (size_t)size); |
| 172 return 1; |
| 169 } | 173 } |
| 170 | 174 |
| 171 // Returns true if the current frame is a key-frame. | 175 // Returns true if the current frame is a key-frame. |
| 172 static int IsKeyFrame(const WebPIterator* const curr, | 176 static int IsKeyFrame(const WebPIterator* const curr, |
| 173 const WebPIterator* const prev, | 177 const WebPIterator* const prev, |
| 174 int prev_frame_was_key_frame, | 178 int prev_frame_was_key_frame, |
| 175 int canvas_width, int canvas_height) { | 179 int canvas_width, int canvas_height) { |
| 176 if (curr->frame_num == 1) { | 180 if (curr->frame_num == 1) { |
| 177 return 1; | 181 return 1; |
| 178 } else if ((!curr->has_alpha || curr->blend_method == WEBP_MUX_NO_BLEND) && | 182 } else if ((!curr->has_alpha || curr->blend_method == WEBP_MUX_NO_BLEND) && |
| (...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 321 // Get compressed frame. | 325 // Get compressed frame. |
| 322 if (!WebPDemuxGetFrame(dec->demux_, dec->next_frame_, &iter)) { | 326 if (!WebPDemuxGetFrame(dec->demux_, dec->next_frame_, &iter)) { |
| 323 return 0; | 327 return 0; |
| 324 } | 328 } |
| 325 timestamp = dec->prev_frame_timestamp_ + iter.duration; | 329 timestamp = dec->prev_frame_timestamp_ + iter.duration; |
| 326 | 330 |
| 327 // Initialize. | 331 // Initialize. |
| 328 is_key_frame = IsKeyFrame(&iter, &dec->prev_iter_, | 332 is_key_frame = IsKeyFrame(&iter, &dec->prev_iter_, |
| 329 dec->prev_frame_was_keyframe_, width, height); | 333 dec->prev_frame_was_keyframe_, width, height); |
| 330 if (is_key_frame) { | 334 if (is_key_frame) { |
| 331 ZeroFillCanvas(dec->curr_frame_, width, height); | 335 if (!ZeroFillCanvas(dec->curr_frame_, width, height)) { |
| 336 goto Error; |
| 337 } |
| 332 } else { | 338 } else { |
| 333 CopyCanvas(dec->prev_frame_disposed_, dec->curr_frame_, width, height); | 339 if (!CopyCanvas(dec->prev_frame_disposed_, dec->curr_frame_, |
| 340 width, height)) { |
| 341 goto Error; |
| 342 } |
| 334 } | 343 } |
| 335 | 344 |
| 336 // Decode. | 345 // Decode. |
| 337 { | 346 { |
| 338 const uint8_t* in = iter.fragment.bytes; | 347 const uint8_t* in = iter.fragment.bytes; |
| 339 const size_t in_size = iter.fragment.size; | 348 const size_t in_size = iter.fragment.size; |
| 340 const size_t out_offset = | 349 const size_t out_offset = |
| 341 (iter.y_offset * width + iter.x_offset) * NUM_CHANNELS; | 350 (iter.y_offset * width + iter.x_offset) * NUM_CHANNELS; |
| 342 WebPDecoderConfig* const config = &dec->config_; | 351 WebPDecoderConfig* const config = &dec->config_; |
| 343 WebPRGBABuffer* const buf = &config->output.u.RGBA; | 352 WebPRGBABuffer* const buf = &config->output.u.RGBA; |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 386 const size_t offset2 = canvas_y * width + left2; | 395 const size_t offset2 = canvas_y * width + left2; |
| 387 blend_row((uint32_t*)dec->curr_frame_ + offset2, | 396 blend_row((uint32_t*)dec->curr_frame_ + offset2, |
| 388 (uint32_t*)dec->prev_frame_disposed_ + offset2, width2); | 397 (uint32_t*)dec->prev_frame_disposed_ + offset2, width2); |
| 389 } | 398 } |
| 390 } | 399 } |
| 391 } | 400 } |
| 392 } | 401 } |
| 393 | 402 |
| 394 // Update info of the previous frame and dispose it for the next iteration. | 403 // Update info of the previous frame and dispose it for the next iteration. |
| 395 dec->prev_frame_timestamp_ = timestamp; | 404 dec->prev_frame_timestamp_ = timestamp; |
| 405 WebPDemuxReleaseIterator(&dec->prev_iter_); |
| 396 dec->prev_iter_ = iter; | 406 dec->prev_iter_ = iter; |
| 397 dec->prev_frame_was_keyframe_ = is_key_frame; | 407 dec->prev_frame_was_keyframe_ = is_key_frame; |
| 398 CopyCanvas(dec->curr_frame_, dec->prev_frame_disposed_, width, height); | 408 CopyCanvas(dec->curr_frame_, dec->prev_frame_disposed_, width, height); |
| 399 if (dec->prev_iter_.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) { | 409 if (dec->prev_iter_.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) { |
| 400 ZeroFillFrameRect(dec->prev_frame_disposed_, width * NUM_CHANNELS, | 410 ZeroFillFrameRect(dec->prev_frame_disposed_, width * NUM_CHANNELS, |
| 401 dec->prev_iter_.x_offset, dec->prev_iter_.y_offset, | 411 dec->prev_iter_.x_offset, dec->prev_iter_.y_offset, |
| 402 dec->prev_iter_.width, dec->prev_iter_.height); | 412 dec->prev_iter_.width, dec->prev_iter_.height); |
| 403 } | 413 } |
| 404 ++dec->next_frame_; | 414 ++dec->next_frame_; |
| 405 | 415 |
| 406 // All OK, fill in the values. | 416 // All OK, fill in the values. |
| 407 *buf_ptr = dec->curr_frame_; | 417 *buf_ptr = dec->curr_frame_; |
| 408 *timestamp_ptr = timestamp; | 418 *timestamp_ptr = timestamp; |
| 409 return 1; | 419 return 1; |
| 410 | 420 |
| 411 Error: | 421 Error: |
| 412 WebPDemuxReleaseIterator(&iter); | 422 WebPDemuxReleaseIterator(&iter); |
| 413 return 0; | 423 return 0; |
| 414 } | 424 } |
| 415 | 425 |
| 416 int WebPAnimDecoderHasMoreFrames(const WebPAnimDecoder* dec) { | 426 int WebPAnimDecoderHasMoreFrames(const WebPAnimDecoder* dec) { |
| 417 if (dec == NULL) return 0; | 427 if (dec == NULL) return 0; |
| 418 return (dec->next_frame_ <= (int)dec->info_.frame_count); | 428 return (dec->next_frame_ <= (int)dec->info_.frame_count); |
| 419 } | 429 } |
| 420 | 430 |
| 421 void WebPAnimDecoderReset(WebPAnimDecoder* dec) { | 431 void WebPAnimDecoderReset(WebPAnimDecoder* dec) { |
| 422 if (dec != NULL) { | 432 if (dec != NULL) { |
| 423 dec->prev_frame_timestamp_ = 0; | 433 dec->prev_frame_timestamp_ = 0; |
| 434 WebPDemuxReleaseIterator(&dec->prev_iter_); |
| 424 memset(&dec->prev_iter_, 0, sizeof(dec->prev_iter_)); | 435 memset(&dec->prev_iter_, 0, sizeof(dec->prev_iter_)); |
| 425 dec->prev_frame_was_keyframe_ = 0; | 436 dec->prev_frame_was_keyframe_ = 0; |
| 426 dec->next_frame_ = 1; | 437 dec->next_frame_ = 1; |
| 427 } | 438 } |
| 428 } | 439 } |
| 429 | 440 |
| 430 const WebPDemuxer* WebPAnimDecoderGetDemuxer(const WebPAnimDecoder* dec) { | 441 const WebPDemuxer* WebPAnimDecoderGetDemuxer(const WebPAnimDecoder* dec) { |
| 431 if (dec == NULL) return NULL; | 442 if (dec == NULL) return NULL; |
| 432 return dec->demux_; | 443 return dec->demux_; |
| 433 } | 444 } |
| 434 | 445 |
| 435 void WebPAnimDecoderDelete(WebPAnimDecoder* dec) { | 446 void WebPAnimDecoderDelete(WebPAnimDecoder* dec) { |
| 436 if (dec != NULL) { | 447 if (dec != NULL) { |
| 448 WebPDemuxReleaseIterator(&dec->prev_iter_); |
| 437 WebPDemuxDelete(dec->demux_); | 449 WebPDemuxDelete(dec->demux_); |
| 438 WebPSafeFree(dec->curr_frame_); | 450 WebPSafeFree(dec->curr_frame_); |
| 439 WebPSafeFree(dec->prev_frame_disposed_); | 451 WebPSafeFree(dec->prev_frame_disposed_); |
| 440 WebPSafeFree(dec); | 452 WebPSafeFree(dec); |
| 441 } | 453 } |
| 442 } | 454 } |
| OLD | NEW |