Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1390)

Unified Diff: third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoder.cpp

Issue 2930513004: [WIP] Move ImageDecoders to SkCodec
Patch Set: Adding check for decoder creation Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoder.cpp
diff --git a/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoder.cpp b/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoder.cpp
deleted file mode 100644
index 2fdf29e19ba2dbd5df6681985877f3ed1050d320..0000000000000000000000000000000000000000
--- a/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoder.cpp
+++ /dev/null
@@ -1,525 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
- * its contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "platform/image-decoders/webp/WEBPImageDecoder.h"
-
-#if CPU(BIG_ENDIAN) || CPU(MIDDLE_ENDIAN)
-#error Blink assumes a little-endian target.
-#endif
-
-#if SK_B32_SHIFT // Output little-endian RGBA pixels (Android).
-inline WEBP_CSP_MODE outputMode(bool hasAlpha) {
- return hasAlpha ? MODE_rgbA : MODE_RGBA;
-}
-#else // Output little-endian BGRA pixels.
-inline WEBP_CSP_MODE outputMode(bool hasAlpha) {
- return hasAlpha ? MODE_bgrA : MODE_BGRA;
-}
-#endif
-
-namespace {
-
-// Returns two point ranges (<left, width> pairs) at row |canvasY| which belong
-// to |src| but not |dst|. A range is empty if its width is 0.
-inline void findBlendRangeAtRow(const blink::IntRect& src,
- const blink::IntRect& dst,
- int canvasY,
- int& left1,
- int& width1,
- int& left2,
- int& width2) {
- SECURITY_DCHECK(canvasY >= src.Y() && canvasY < src.MaxY());
- left1 = -1;
- width1 = 0;
- left2 = -1;
- width2 = 0;
-
- if (canvasY < dst.Y() || canvasY >= dst.MaxY() || src.X() >= dst.MaxX() ||
- src.MaxX() <= dst.X()) {
- left1 = src.X();
- width1 = src.Width();
- return;
- }
-
- if (src.X() < dst.X()) {
- left1 = src.X();
- width1 = dst.X() - src.X();
- }
-
- if (src.MaxX() > dst.MaxX()) {
- left2 = dst.MaxX();
- width2 = src.MaxX() - dst.MaxX();
- }
-}
-
-// alphaBlendPremultiplied and alphaBlendNonPremultiplied are separate methods,
-// even though they only differ by one line. This is done so that the compiler
-// can inline BlendSrcOverDstPremultiplied() and BlensSrcOverDstRaw() calls.
-// For GIF images, this optimization reduces decoding time by 15% for 3MB
-// images.
-void alphaBlendPremultiplied(blink::ImageFrame& src,
- blink::ImageFrame& dst,
- int canvasY,
- int left,
- int width) {
- for (int x = 0; x < width; ++x) {
- int canvasX = left + x;
- blink::ImageFrame::PixelData* pixel = src.GetAddr(canvasX, canvasY);
- if (SkGetPackedA32(*pixel) != 0xff) {
- blink::ImageFrame::PixelData prevPixel = *dst.GetAddr(canvasX, canvasY);
- blink::ImageFrame::BlendSrcOverDstPremultiplied(pixel, prevPixel);
- }
- }
-}
-
-void alphaBlendNonPremultiplied(blink::ImageFrame& src,
- blink::ImageFrame& dst,
- int canvasY,
- int left,
- int width) {
- for (int x = 0; x < width; ++x) {
- int canvasX = left + x;
- blink::ImageFrame::PixelData* pixel = src.GetAddr(canvasX, canvasY);
- if (SkGetPackedA32(*pixel) != 0xff) {
- blink::ImageFrame::PixelData prevPixel = *dst.GetAddr(canvasX, canvasY);
- blink::ImageFrame::BlendSrcOverDstRaw(pixel, prevPixel);
- }
- }
-}
-
-} // namespace
-
-namespace blink {
-
-WEBPImageDecoder::WEBPImageDecoder(AlphaOption alpha_option,
- const ColorBehavior& color_behavior,
- size_t max_decoded_bytes)
- : ImageDecoder(alpha_option, color_behavior, max_decoded_bytes),
- decoder_(0),
- format_flags_(0),
- frame_background_has_alpha_(false),
- demux_(0),
- demux_state_(WEBP_DEMUX_PARSING_HEADER),
- have_already_parsed_this_data_(false),
- repetition_count_(kAnimationLoopOnce),
- decoded_height_(0) {
- blend_function_ = (alpha_option == kAlphaPremultiplied)
- ? alphaBlendPremultiplied
- : alphaBlendNonPremultiplied;
-}
-
-WEBPImageDecoder::~WEBPImageDecoder() {
- Clear();
-}
-
-void WEBPImageDecoder::Clear() {
- WebPDemuxDelete(demux_);
- demux_ = 0;
- consolidated_data_.reset();
- ClearDecoder();
-}
-
-void WEBPImageDecoder::ClearDecoder() {
- WebPIDelete(decoder_);
- decoder_ = 0;
- decoded_height_ = 0;
- frame_background_has_alpha_ = false;
-}
-
-void WEBPImageDecoder::OnSetData(SegmentReader*) {
- have_already_parsed_this_data_ = false;
-}
-
-int WEBPImageDecoder::RepetitionCount() const {
- return Failed() ? kAnimationLoopOnce : repetition_count_;
-}
-
-bool WEBPImageDecoder::FrameIsCompleteAtIndex(size_t index) const {
- if (!demux_ || demux_state_ <= WEBP_DEMUX_PARSING_HEADER)
- return false;
- if (!(format_flags_ & ANIMATION_FLAG))
- return ImageDecoder::FrameIsCompleteAtIndex(index);
- bool frame_is_received_at_index = index < frame_buffer_cache_.size();
- return frame_is_received_at_index;
-}
-
-float WEBPImageDecoder::FrameDurationAtIndex(size_t index) const {
- return index < frame_buffer_cache_.size()
- ? frame_buffer_cache_[index].Duration()
- : 0;
-}
-
-bool WEBPImageDecoder::UpdateDemuxer() {
- if (Failed())
- return false;
-
- if (have_already_parsed_this_data_)
- return true;
-
- have_already_parsed_this_data_ = true;
-
- const unsigned kWebpHeaderSize = 30;
- if (data_->size() < kWebpHeaderSize)
- return false; // Await VP8X header so WebPDemuxPartial succeeds.
-
- WebPDemuxDelete(demux_);
- consolidated_data_ = data_->GetAsSkData();
- WebPData input_data = {
- reinterpret_cast<const uint8_t*>(consolidated_data_->data()),
- consolidated_data_->size()};
- demux_ = WebPDemuxPartial(&input_data, &demux_state_);
- if (!demux_ || (IsAllDataReceived() && demux_state_ != WEBP_DEMUX_DONE)) {
- if (!demux_)
- consolidated_data_.reset();
- return SetFailed();
- }
-
- DCHECK_GT(demux_state_, WEBP_DEMUX_PARSING_HEADER);
- if (!WebPDemuxGetI(demux_, WEBP_FF_FRAME_COUNT))
- return false; // Wait until the encoded image frame data arrives.
-
- if (!IsDecodedSizeAvailable()) {
- int width = WebPDemuxGetI(demux_, WEBP_FF_CANVAS_WIDTH);
- int height = WebPDemuxGetI(demux_, WEBP_FF_CANVAS_HEIGHT);
- if (!SetSize(width, height))
- return SetFailed();
-
- format_flags_ = WebPDemuxGetI(demux_, WEBP_FF_FORMAT_FLAGS);
- if (!(format_flags_ & ANIMATION_FLAG)) {
- repetition_count_ = kAnimationNone;
- } else {
- // Since we have parsed at least one frame, even if partially,
- // the global animation (ANIM) properties have been read since
- // an ANIM chunk must precede the ANMF frame chunks.
- repetition_count_ = WebPDemuxGetI(demux_, WEBP_FF_LOOP_COUNT);
- // Repetition count is always <= 16 bits.
- DCHECK_EQ(repetition_count_, repetition_count_ & 0xffff);
- if (!repetition_count_)
- repetition_count_ = kAnimationLoopInfinite;
- // FIXME: Implement ICC profile support for animated images.
- format_flags_ &= ~ICCP_FLAG;
- }
-
- if ((format_flags_ & ICCP_FLAG) && !IgnoresColorSpace())
- ReadColorProfile();
- }
-
- DCHECK(IsDecodedSizeAvailable());
-
- size_t frame_count = WebPDemuxGetI(demux_, WEBP_FF_FRAME_COUNT);
- UpdateAggressivePurging(frame_count);
-
- return true;
-}
-
-void WEBPImageDecoder::OnInitFrameBuffer(size_t frame_index) {
- // ImageDecoder::InitFrameBuffer does a DCHECK if |frame_index| exists.
- ImageFrame& buffer = frame_buffer_cache_[frame_index];
-
- const size_t required_previous_frame_index =
- buffer.RequiredPreviousFrameIndex();
- if (required_previous_frame_index == kNotFound) {
- frame_background_has_alpha_ =
- !buffer.OriginalFrameRect().Contains(IntRect(IntPoint(), Size()));
- } else {
- const ImageFrame& prev_buffer =
- frame_buffer_cache_[required_previous_frame_index];
- frame_background_has_alpha_ =
- prev_buffer.HasAlpha() || (prev_buffer.GetDisposalMethod() ==
- ImageFrame::kDisposeOverwriteBgcolor);
- }
-
- // The buffer is transparent outside the decoded area while the image is
- // loading. The correct alpha value for the frame will be set when it is fully
- // decoded.
- buffer.SetHasAlpha(true);
-}
-
-bool WEBPImageDecoder::CanReusePreviousFrameBuffer(size_t frame_index) const {
- DCHECK(frame_index < frame_buffer_cache_.size());
- return frame_buffer_cache_[frame_index].GetAlphaBlendSource() !=
- ImageFrame::kBlendAtopPreviousFrame;
-}
-
-void WEBPImageDecoder::ClearFrameBuffer(size_t frame_index) {
- if (demux_ && demux_state_ >= WEBP_DEMUX_PARSED_HEADER &&
- frame_buffer_cache_[frame_index].GetStatus() ==
- ImageFrame::kFramePartial) {
- // Clear the decoder state so that this partial frame can be decoded again
- // when requested.
- ClearDecoder();
- }
- ImageDecoder::ClearFrameBuffer(frame_index);
-}
-
-void WEBPImageDecoder::ReadColorProfile() {
- WebPChunkIterator chunk_iterator;
- if (!WebPDemuxGetChunk(demux_, "ICCP", 1, &chunk_iterator)) {
- WebPDemuxReleaseChunkIterator(&chunk_iterator);
- return;
- }
-
- const char* profile_data =
- reinterpret_cast<const char*>(chunk_iterator.chunk.bytes);
- size_t profile_size = chunk_iterator.chunk.size;
-
- SetEmbeddedColorProfile(profile_data, profile_size);
-
- WebPDemuxReleaseChunkIterator(&chunk_iterator);
-}
-
-void WEBPImageDecoder::ApplyPostProcessing(size_t frame_index) {
- ImageFrame& buffer = frame_buffer_cache_[frame_index];
- int width;
- int decoded_height;
- if (!WebPIDecGetRGB(decoder_, &decoded_height, &width, 0, 0))
- return; // See also https://bugs.webkit.org/show_bug.cgi?id=74062
- if (decoded_height <= 0)
- return;
-
- const IntRect& frame_rect = buffer.OriginalFrameRect();
- SECURITY_DCHECK(width == frame_rect.Width());
- SECURITY_DCHECK(decoded_height <= frame_rect.Height());
- const int left = frame_rect.X();
- const int top = frame_rect.Y();
-
- // TODO (msarett):
- // Here we apply the color space transformation to the dst space.
- // It does not really make sense to transform to a gamma-encoded
- // space and then immediately after, perform a linear premultiply
- // and linear blending. Can we find a way to perform the
- // premultiplication and blending in a linear space?
- SkColorSpaceXform* xform = ColorTransform();
- if (xform) {
- const SkColorSpaceXform::ColorFormat kSrcFormat =
- SkColorSpaceXform::kBGRA_8888_ColorFormat;
- const SkColorSpaceXform::ColorFormat kDstFormat =
- SkColorSpaceXform::kRGBA_8888_ColorFormat;
- for (int y = decoded_height_; y < decoded_height; ++y) {
- const int canvas_y = top + y;
- uint8_t* row = reinterpret_cast<uint8_t*>(buffer.GetAddr(left, canvas_y));
- xform->apply(kDstFormat, row, kSrcFormat, row, width,
- kUnpremul_SkAlphaType);
-
- uint8_t* pixel = row;
- for (int x = 0; x < width; ++x, pixel += 4) {
- const int canvas_x = left + x;
- buffer.SetRGBA(canvas_x, canvas_y, pixel[0], pixel[1], pixel[2],
- pixel[3]);
- }
- }
- }
-
- // During the decoding of the current frame, we may have set some pixels to be
- // transparent (i.e. alpha < 255). If the alpha blend source was
- // 'BlendAtopPreviousFrame', the values of these pixels should be determined
- // by blending them against the pixels of the corresponding previous frame.
- // Compute the correct opaque values now.
- // FIXME: This could be avoided if libwebp decoder had an API that used the
- // previous required frame to do the alpha-blending by itself.
- if ((format_flags_ & ANIMATION_FLAG) && frame_index &&
- buffer.GetAlphaBlendSource() == ImageFrame::kBlendAtopPreviousFrame &&
- buffer.RequiredPreviousFrameIndex() != kNotFound) {
- ImageFrame& prev_buffer = frame_buffer_cache_[frame_index - 1];
- DCHECK_EQ(prev_buffer.GetStatus(), ImageFrame::kFrameComplete);
- ImageFrame::DisposalMethod prev_disposal_method =
- prev_buffer.GetDisposalMethod();
- if (prev_disposal_method == ImageFrame::kDisposeKeep) {
- // Blend transparent pixels with pixels in previous canvas.
- for (int y = decoded_height_; y < decoded_height; ++y) {
- blend_function_(buffer, prev_buffer, top + y, left, width);
- }
- } else if (prev_disposal_method == ImageFrame::kDisposeOverwriteBgcolor) {
- const IntRect& prev_rect = prev_buffer.OriginalFrameRect();
- // We need to blend a transparent pixel with the starting value (from just
- // after the InitFrame() call). If the pixel belongs to prev_rect, the
- // starting value was fully transparent, so this is a no-op. Otherwise, we
- // need to blend against the pixel from the previous canvas.
- for (int y = decoded_height_; y < decoded_height; ++y) {
- int canvas_y = top + y;
- int left1, width1, left2, width2;
- findBlendRangeAtRow(frame_rect, prev_rect, canvas_y, left1, width1,
- left2, width2);
- if (width1 > 0)
- blend_function_(buffer, prev_buffer, canvas_y, left1, width1);
- if (width2 > 0)
- blend_function_(buffer, prev_buffer, canvas_y, left2, width2);
- }
- }
- }
-
- decoded_height_ = decoded_height;
- buffer.SetPixelsChanged(true);
-}
-
-size_t WEBPImageDecoder::DecodeFrameCount() {
- // If UpdateDemuxer() fails, return the existing number of frames. This way
- // if we get halfway through the image before decoding fails, we won't
- // suddenly start reporting that the image has zero frames.
- return UpdateDemuxer() ? WebPDemuxGetI(demux_, WEBP_FF_FRAME_COUNT)
- : frame_buffer_cache_.size();
-}
-
-void WEBPImageDecoder::InitializeNewFrame(size_t index) {
- if (!(format_flags_ & ANIMATION_FLAG)) {
- DCHECK(!index);
- return;
- }
- WebPIterator animated_frame;
- WebPDemuxGetFrame(demux_, index + 1, &animated_frame);
- DCHECK_EQ(animated_frame.complete, 1);
- ImageFrame* buffer = &frame_buffer_cache_[index];
- IntRect frame_rect(animated_frame.x_offset, animated_frame.y_offset,
- animated_frame.width, animated_frame.height);
- buffer->SetOriginalFrameRect(
- Intersection(frame_rect, IntRect(IntPoint(), Size())));
- buffer->SetDuration(animated_frame.duration);
- buffer->SetDisposalMethod(animated_frame.dispose_method ==
- WEBP_MUX_DISPOSE_BACKGROUND
- ? ImageFrame::kDisposeOverwriteBgcolor
- : ImageFrame::kDisposeKeep);
- buffer->SetAlphaBlendSource(animated_frame.blend_method == WEBP_MUX_BLEND
- ? ImageFrame::kBlendAtopPreviousFrame
- : ImageFrame::kBlendAtopBgcolor);
- buffer->SetRequiredPreviousFrameIndex(
- FindRequiredPreviousFrame(index, !animated_frame.has_alpha));
- WebPDemuxReleaseIterator(&animated_frame);
-}
-
-void WEBPImageDecoder::Decode(size_t index) {
- if (Failed())
- return;
-
- Vector<size_t> frames_to_decode = FindFramesToDecode(index);
-
- DCHECK(demux_);
- for (auto i = frames_to_decode.rbegin(); i != frames_to_decode.rend(); ++i) {
- if ((format_flags_ & ANIMATION_FLAG) && !InitFrameBuffer(*i)) {
- SetFailed();
- return;
- }
-
- WebPIterator webp_frame;
- if (!WebPDemuxGetFrame(demux_, *i + 1, &webp_frame)) {
- SetFailed();
- } else {
- DecodeSingleFrame(webp_frame.fragment.bytes, webp_frame.fragment.size,
- *i);
- WebPDemuxReleaseIterator(&webp_frame);
- }
- if (Failed())
- return;
-
- // If this returns false, we need more data to continue decoding.
- if (!PostDecodeProcessing(*i))
- break;
- }
-
- // It is also a fatal error if all data is received and we have decoded all
- // frames available but the file is truncated.
- if (index >= frame_buffer_cache_.size() - 1 && IsAllDataReceived() &&
- demux_ && demux_state_ != WEBP_DEMUX_DONE)
- SetFailed();
-}
-
-bool WEBPImageDecoder::DecodeSingleFrame(const uint8_t* data_bytes,
- size_t data_size,
- size_t frame_index) {
- if (Failed())
- return false;
-
- DCHECK(IsDecodedSizeAvailable());
-
- DCHECK_GT(frame_buffer_cache_.size(), frame_index);
- ImageFrame& buffer = frame_buffer_cache_[frame_index];
- DCHECK_NE(buffer.GetStatus(), ImageFrame::kFrameComplete);
-
- if (buffer.GetStatus() == ImageFrame::kFrameEmpty) {
- if (!buffer.AllocatePixelData(Size().Width(), Size().Height(),
- ColorSpaceForSkImages()))
- return SetFailed();
- buffer.ZeroFillPixelData();
- buffer.SetStatus(ImageFrame::kFramePartial);
- // The buffer is transparent outside the decoded area while the image is
- // loading. The correct alpha value for the frame will be set when it is
- // fully decoded.
- buffer.SetHasAlpha(true);
- buffer.SetOriginalFrameRect(IntRect(IntPoint(), Size()));
- }
-
- const IntRect& frame_rect = buffer.OriginalFrameRect();
- if (!decoder_) {
- WEBP_CSP_MODE mode = outputMode(format_flags_ & ALPHA_FLAG);
- if (!premultiply_alpha_)
- mode = outputMode(false);
- if (ColorTransform()) {
- // Swizzling between RGBA and BGRA is zero cost in a color transform.
- // So when we have a color transform, we should decode to whatever is
- // easiest for libwebp, and then let the color transform swizzle if
- // necessary.
- // Lossy webp is encoded as YUV (so RGBA and BGRA are the same cost).
- // Lossless webp is encoded as BGRA. This means decoding to BGRA is
- // either faster or the same cost as RGBA.
- mode = MODE_BGRA;
- }
- WebPInitDecBuffer(&decoder_buffer_);
- decoder_buffer_.colorspace = mode;
- decoder_buffer_.u.RGBA.stride =
- Size().Width() * sizeof(ImageFrame::PixelData);
- decoder_buffer_.u.RGBA.size =
- decoder_buffer_.u.RGBA.stride * frame_rect.Height();
- decoder_buffer_.is_external_memory = 1;
- decoder_ = WebPINewDecoder(&decoder_buffer_);
- if (!decoder_)
- return SetFailed();
- }
-
- decoder_buffer_.u.RGBA.rgba = reinterpret_cast<uint8_t*>(
- buffer.GetAddr(frame_rect.X(), frame_rect.Y()));
-
- switch (WebPIUpdate(decoder_, data_bytes, data_size)) {
- case VP8_STATUS_OK:
- ApplyPostProcessing(frame_index);
- buffer.SetHasAlpha((format_flags_ & ALPHA_FLAG) ||
- frame_background_has_alpha_);
- buffer.SetStatus(ImageFrame::kFrameComplete);
- ClearDecoder();
- return true;
- case VP8_STATUS_SUSPENDED:
- if (!IsAllDataReceived() && !FrameIsCompleteAtIndex(frame_index)) {
- ApplyPostProcessing(frame_index);
- return false;
- }
- // FALLTHROUGH
- default:
- Clear();
- return SetFailed();
- }
-}
-
-} // namespace blink

Powered by Google App Engine
This is Rietveld 408576698