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

Unified Diff: third_party/WebKit/Source/platform/image-decoders/ImageDecoder.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/ImageDecoder.cpp
diff --git a/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.cpp b/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.cpp
index 0b76d3326f24edabb38900dfff72e8440d417c42..3ace861ae0532bf93449db3ea41afefe1c730211 100644
--- a/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.cpp
@@ -23,88 +23,25 @@
#include <memory>
#include "platform/RuntimeEnabledFeatures.h"
#include "platform/graphics/BitmapImageMetrics.h"
-#include "platform/image-decoders/FastSharedBufferReader.h"
-#include "platform/image-decoders/bmp/BMPImageDecoder.h"
-#include "platform/image-decoders/gif/GIFImageDecoder.h"
-#include "platform/image-decoders/ico/ICOImageDecoder.h"
-#include "platform/image-decoders/jpeg/JPEGImageDecoder.h"
-#include "platform/image-decoders/png/PNGImageDecoder.h"
-#include "platform/image-decoders/webp/WEBPImageDecoder.h"
#include "platform/instrumentation/PlatformInstrumentation.h"
#include "platform/wtf/PtrUtil.h"
+#include "third_party/skia/include/core/SkImageInfo.h"
namespace blink {
-inline bool MatchesJPEGSignature(const char* contents) {
- return !memcmp(contents, "\xFF\xD8\xFF", 3);
-}
-
-inline bool MatchesPNGSignature(const char* contents) {
- return !memcmp(contents, "\x89PNG\r\n\x1A\n", 8);
-}
-
-inline bool MatchesGIFSignature(const char* contents) {
- return !memcmp(contents, "GIF87a", 6) || !memcmp(contents, "GIF89a", 6);
-}
-
-inline bool MatchesWebPSignature(const char* contents) {
- return !memcmp(contents, "RIFF", 4) && !memcmp(contents + 8, "WEBPVP", 6);
-}
-
-inline bool MatchesICOSignature(const char* contents) {
- return !memcmp(contents, "\x00\x00\x01\x00", 4);
-}
-
-inline bool MatchesCURSignature(const char* contents) {
- return !memcmp(contents, "\x00\x00\x02\x00", 4);
-}
-
-inline bool MatchesBMPSignature(const char* contents) {
- return !memcmp(contents, "BM", 2);
-}
-
-static constexpr size_t kLongestSignatureLength = sizeof("RIFF????WEBPVP") - 1;
-
std::unique_ptr<ImageDecoder> ImageDecoder::Create(
RefPtr<SegmentReader> data,
bool data_complete,
AlphaOption alpha_option,
const ColorBehavior& color_behavior) {
- // At least kLongestSignatureLength bytes are needed to sniff the signature.
- if (data->size() < kLongestSignatureLength)
- return nullptr;
-
const size_t max_decoded_bytes =
Platform::Current() ? Platform::Current()->MaxDecodedImageBytes()
: kNoDecodedImageByteLimit;
- // Access the first kLongestSignatureLength chars to sniff the signature.
- // (note: FastSharedBufferReader only makes a copy if the bytes are segmented)
- char buffer[kLongestSignatureLength];
- const FastSharedBufferReader fast_reader(data);
- const char* contents =
- fast_reader.GetConsecutiveData(0, kLongestSignatureLength, buffer);
-
std::unique_ptr<ImageDecoder> decoder;
- if (MatchesJPEGSignature(contents)) {
- decoder.reset(
- new JPEGImageDecoder(alpha_option, color_behavior, max_decoded_bytes));
- } else if (MatchesPNGSignature(contents)) {
- decoder.reset(
- new PNGImageDecoder(alpha_option, color_behavior, max_decoded_bytes));
- } else if (MatchesGIFSignature(contents)) {
- decoder.reset(
- new GIFImageDecoder(alpha_option, color_behavior, max_decoded_bytes));
- } else if (MatchesWebPSignature(contents)) {
- decoder.reset(
- new WEBPImageDecoder(alpha_option, color_behavior, max_decoded_bytes));
- } else if (MatchesICOSignature(contents) || MatchesCURSignature(contents)) {
- decoder.reset(
- new ICOImageDecoder(alpha_option, color_behavior, max_decoded_bytes));
- } else if (MatchesBMPSignature(contents)) {
- decoder.reset(
- new BMPImageDecoder(alpha_option, color_behavior, max_decoded_bytes));
- }
+
+ decoder.reset(
+ new ImageDecoder(alpha_option, color_behavior, max_decoded_bytes));
if (decoder)
decoder->SetData(std::move(data), data_complete);
@@ -112,13 +49,31 @@ std::unique_ptr<ImageDecoder> ImageDecoder::Create(
return decoder;
}
-bool ImageDecoder::HasSufficientDataToSniffImageType(const SharedBuffer& data) {
- return data.size() >= kLongestSignatureLength;
+ImageDecoder::ImageDecoder(AlphaOption alpha_option,
+ const ColorBehavior& color_behavior,
+ size_t max_decoded_bytes)
+ : premultiply_alpha_(alpha_option == kAlphaPremultiplied),
+ color_behavior_(color_behavior),
+ max_decoded_bytes_(max_decoded_bytes),
+ purge_aggressively_(false),
+ codec_() {}
+void ImageDecoder::OnSetData(SegmentReader* data) {
+ if (data) {
+ if (!codec_) {
+ SkCodec* codec = SkCodec::NewFromData(data->GetAsSkData());
+ if (codec)
+ codec_.reset(codec);
+ else
+ return;
+ }
+ SkImageInfo image_info = codec_->getInfo();
+ SetSize(image_info.width(), image_info.height());
+ }
}
size_t ImageDecoder::FrameCount() {
const size_t old_size = frame_buffer_cache_.size();
- const size_t new_size = DecodeFrameCount();
+ const size_t new_size = codec_ ? codec_->getFrameCount() : 0;
if (old_size != new_size) {
frame_buffer_cache_.resize(new_size);
for (size_t i = old_size; i < new_size; ++i) {
@@ -129,6 +84,70 @@ size_t ImageDecoder::FrameCount() {
return new_size;
}
+void ImageDecoder::Decode(size_t index) {
+ if (!codec_)
+ return;
+
+ DCHECK(!Failed());
+ DCHECK_LT(index, frame_buffer_cache_.size());
+
+ UpdateAggressivePurging(index);
+ SkImageInfo imageInfo = codec_->getInfo()
+ .makeColorType(kN32_SkColorType)
+ .makeColorSpace(ColorSpaceForSkImages());
+
+ SkCodec::Options options;
+ options.fFrameIndex = index;
+ options.fZeroInitialized = SkCodec::kYes_ZeroInitialized;
+
+ ImageFrame& frame = frame_buffer_cache_[index];
+ if (frame.GetStatus() == ImageFrame::kFrameEmpty) {
+ size_t required_previous_frame_index = frame.RequiredPreviousFrameIndex();
+ if (required_previous_frame_index == WTF::kNotFound) {
+ frame.AllocatePixelData(Size().Width(), Size().Height(),
+ ColorSpaceForSkImages());
+ } else {
+ ImageFrame& required_previous_frame =
+ frame_buffer_cache_[required_previous_frame_index];
+
+ if (required_previous_frame.GetStatus() != ImageFrame::kFrameComplete)
+ Decode(required_previous_frame_index);
+
+ // We try to reuse |required_previous_frame| as starting state to avoid
+ // copying. If CanReusePreviousFrameBuffer returns false, we must copy
+ // the data since |required_previous_frame| is necessary to decode this
+ // or later frames. In that case copy the data instead.
+ if ((!CanReusePreviousFrameBuffer(index) ||
+ !frame.TakeBitmapDataIfWritable(&required_previous_frame)) &&
+ !frame.CopyBitmapData(required_previous_frame)) {
+ SetFailed();
+ return;
+ }
+ options.fHasPriorFrame = true;
+ }
+ }
+
+ SkCodec::Result result =
+ codec_->getPixels(imageInfo, frame.Bitmap().getPixels(),
+ frame.Bitmap().rowBytes(), &options, nullptr, nullptr);
+ switch (result) {
+ case SkCodec::kSuccess:
+ frame.SetPixelsChanged(true);
+ frame.SetStatus(ImageFrame::kFrameComplete);
+ PostDecodeProcessing(index);
+ break;
+ default:
+ SetFailed();
+ return;
+ }
+}
+
+bool ImageDecoder::SetFailed() {
+ codec_ = nullptr;
+ failed_ = true;
+ return false;
+}
+
ImageFrame* ImageDecoder::FrameBufferAtIndex(size_t index) {
if (index >= FrameCount())
return 0;
@@ -155,9 +174,22 @@ bool ImageDecoder::FrameHasAlphaAtIndex(size_t index) const {
}
bool ImageDecoder::FrameIsCompleteAtIndex(size_t index) const {
- // Animated images override this method to return the status based on the data
- // received for the queried frame.
- return IsAllDataReceived();
+ if (!codec_)
+ return false;
+
+ if (codec_->getFrameCount() <= (int)index)
+ return false;
+
+ if (frame_buffer_cache_.IsEmpty())
+ return false;
+
+ return frame_buffer_cache_[0].GetStatus() == ImageFrame::kFrameComplete;
+}
+
+float ImageDecoder::FrameDurationAtIndex(size_t index) const {
+ if (index < frame_buffer_cache_.size())
+ return frame_buffer_cache_[index].Duration();
+ return 0;
}
bool ImageDecoder::FrameIsDecodedAtIndex(size_t index) const {
@@ -357,14 +389,61 @@ bool ImageDecoder::InitFrameBuffer(size_t frame_index) {
}
}
- OnInitFrameBuffer(frame_index);
-
// Update our status to be partially complete.
buffer->SetStatus(ImageFrame::kFramePartial);
return true;
}
+int ImageDecoder::RepetitionCount() {
+ if (!codec_)
+ return kAnimationLoopOnce;
+
+ DCHECK(!Failed());
+
+ // packets sent back by the webserver) not always.
+ // SkCodec will parse forward in the file if the repetition count has not been
+ // seen yet.
+ int repetition_count = codec_->getRepetitionCount();
+ switch (repetition_count) {
+ case 0: {
+ size_t frame_count = codec_->getFrameCount();
+ if (IsAllDataReceived() && frame_count == 1)
+ return kAnimationNone;
+ return kAnimationLoopOnce;
+ }
+ case SkCodec::kRepetitionCountInfinite:
+ return kAnimationLoopInfinite;
+ default:
+ return repetition_count;
+ }
+}
+
+bool ImageDecoder::CanReusePreviousFrameBuffer(size_t index) const {
+ DCHECK(index < frame_buffer_cache_.size());
+
+ // If the current frame and the next frame depend on the same frame, we cannot
+ // reuse the old frame. We must preserve it for the next frame.
+ //
+ // However, if the current and next frame depend on different frames then we
+ // know the current frame is the last one to use the frame it depends on. That
+ // means the current frame can reuse the previous frame buffer.
+ //
+ // If we do not have information about the next frame yet, we cannot assume it
+ // is safe to reuse the previous frame buffer.
+
+ if (index + 1 >= frame_buffer_cache_.size())
+ return false;
+
+ const ImageFrame& frame = frame_buffer_cache_[index];
+ size_t required_frame_index = frame.RequiredPreviousFrameIndex();
+
+ const ImageFrame& next_frame = frame_buffer_cache_[index + 1];
+ size_t next_required_frame_index = next_frame.RequiredPreviousFrameIndex();
+
+ return required_frame_index != next_required_frame_index;
+}
+
void ImageDecoder::UpdateAggressivePurging(size_t index) {
if (purge_aggressively_)
return;
@@ -455,6 +534,36 @@ size_t ImageDecoder::FindRequiredPreviousFrame(size_t frame_index,
}
}
+void ImageDecoder::InitializeNewFrame(size_t index) {
+ DCHECK(codec_);
+
+ ImageFrame& frame = frame_buffer_cache_[index];
+ // SkCodec does not inform us if only a portion of the image was updated
+ // in the current frame. Because of this, rather than correctly filling in
+ // the frame rect, we set the frame rect to be the image's full size.
+ IntSize fullImageSize = Size();
+ frame.SetOriginalFrameRect(IntRect(IntPoint(), fullImageSize));
+
+ SkCodec::FrameInfo frame_info;
+ memset(&frame_info, 0, sizeof(frame_info));
+ codec_->getFrameInfo(index, &frame_info);
+ frame.SetDuration(frame_info.fDuration);
+ frame.SetHasAlpha(!SkAlphaTypeIsOpaque(frame_info.fAlphaType));
+ size_t required_previous_frame_index = WTF::kNotFound;
+
+ if (frame_info.fRequiredFrame > 0) {
+ required_previous_frame_index =
+ static_cast<size_t>(frame_info.fRequiredFrame);
+ }
+
+ frame.SetRequiredPreviousFrameIndex(required_previous_frame_index);
+
+ // The disposal method is not required any more, but is left in place
+ // for the other image decoders that do not yet rely on SkCodec.
+ // For now, fill it with DisposeKeep.
+ frame.SetDisposalMethod(ImageFrame::kDisposeKeep);
+}
+
ImagePlanes::ImagePlanes() {
for (int i = 0; i < 3; ++i) {
planes_[i] = 0;

Powered by Google App Engine
This is Rietveld 408576698