diff options
Diffstat (limited to 'chromium/third_party/blink/renderer/modules/webcodecs/image_decoder_external.cc')
-rw-r--r-- | chromium/third_party/blink/renderer/modules/webcodecs/image_decoder_external.cc | 170 |
1 files changed, 97 insertions, 73 deletions
diff --git a/chromium/third_party/blink/renderer/modules/webcodecs/image_decoder_external.cc b/chromium/third_party/blink/renderer/modules/webcodecs/image_decoder_external.cc index cc082117cf4..e42ceaf3703 100644 --- a/chromium/third_party/blink/renderer/modules/webcodecs/image_decoder_external.cc +++ b/chromium/third_party/blink/renderer/modules/webcodecs/image_decoder_external.cc @@ -8,6 +8,7 @@ #include "base/logging.h" #include "base/time/time.h" +#include "third_party/blink/public/common/mime_util/mime_util.h" #include "third_party/blink/renderer/bindings/core/v8/v8_image_bitmap_options.h" #include "third_party/blink/renderer/bindings/modules/v8/v8_image_decoder_init.h" #include "third_party/blink/renderer/bindings/modules/v8/v8_image_frame.h" @@ -41,10 +42,16 @@ ImageDecoderExternal::DecodeRequest::DecodeRequest( frame_index(frame_index), complete_frames_only(complete_frames_only) {} -void ImageDecoderExternal::DecodeRequest::Trace(Visitor* visitor) { +void ImageDecoderExternal::DecodeRequest::Trace(Visitor* visitor) const { visitor->Trace(resolver); } +// static +bool ImageDecoderExternal::canDecodeType(String type) { + return type.ContainsOnlyASCIIOrEmpty() && + IsSupportedImageMimeType(type.Ascii()); +} + ImageDecoderExternal::ImageDecoderExternal(ScriptState* script_state, const ImageDecoderInit* init, ExceptionState& exception_state) @@ -56,6 +63,13 @@ ImageDecoderExternal::ImageDecoderExternal(ScriptState* script_state, options_ = init->hasOptions() ? init->options() : ImageBitmapOptions::Create(); + mime_type_ = init->type(); + if (!canDecodeType(mime_type_)) { + exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError, + "Unsupported image format."); + return; + } + if (init->data().IsReadableStream()) { consumer_ = MakeGarbageCollected<ReadableStreamBytesConsumer>( script_state, init->data().GetAsReadableStream(), exception_state); @@ -63,11 +77,11 @@ ImageDecoderExternal::ImageDecoderExternal(ScriptState* script_state, return; stream_buffer_ = WTF::SharedBuffer::Create(); - consumer_->SetClient(this); + CreateImageDecoder(); - // We can't create the ImageDecoder until we have some data, so we may be - // done for now; we need one initial call to OnStateChange(), but thereafter - // calls will be driven by the ReadableStreamBytesConsumer. + // We need one initial call to OnStateChange() to start reading, but + // thereafter calls will be driven by the ReadableStreamBytesConsumer. + consumer_->SetClient(this); OnStateChange(); return; } @@ -88,23 +102,18 @@ ImageDecoderExternal::ImageDecoderExternal(ScriptState* script_state, // TODO: Data is owned by the caller who may be free to manipulate it. We will // probably need to make a copy to our own internal data or neuter the buffers // as seen by JS. - auto sr = SegmentReader::CreateFromSkData( + segment_reader_ = SegmentReader::CreateFromSkData( SkData::MakeWithoutCopy(buffer.Data(), buffer.ByteLengthAsSizeT())); - if (!sr) { + if (!segment_reader_) { exception_state.ThrowDOMException(DOMExceptionCode::kConstraintError, "Failed to read image data"); return; } data_complete_ = true; - MaybeCreateImageDecoder(std::move(sr)); - if (!decoder_) { - exception_state.ThrowDOMException(DOMExceptionCode::kConstraintError, - "Failed to create image decoder"); - return; - } - UpdateFrameAndRepetitionCount(); + CreateImageDecoder(); + MaybeUpdateMetadata(); if (decoder_->Failed()) { exception_state.ThrowDOMException(DOMExceptionCode::kOperationError, "Image decoding failed"); @@ -128,6 +137,16 @@ ScriptPromise ImageDecoderExternal::decode(uint32_t frame_index, return promise; } +ScriptPromise ImageDecoderExternal::decodeMetadata() { + DVLOG(1) << __func__; + + auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state_); + auto promise = resolver->Promise(); + pending_metadata_decodes_.push_back(resolver); + MaybeSatisfyPendingMetadataDecodes(); + return promise; +} + uint32_t ImageDecoderExternal::frameCount() const { return frame_count_; } @@ -164,12 +183,9 @@ void ImageDecoderExternal::OnStateChange() { } data_complete_ = result == BytesConsumer::Result::kDone; - if (!decoder_) - MaybeCreateImageDecoder(nullptr); - else - decoder_->SetData(stream_buffer_, data_complete_); + decoder_->SetData(stream_buffer_, data_complete_); - UpdateFrameAndRepetitionCount(); + MaybeUpdateMetadata(); MaybeSatisfyPendingDecodes(); } } @@ -178,24 +194,65 @@ String ImageDecoderExternal::DebugName() const { return "ImageDecoderExternal"; } -void ImageDecoderExternal::Trace(Visitor* visitor) { +void ImageDecoderExternal::Trace(Visitor* visitor) const { visitor->Trace(script_state_); visitor->Trace(consumer_); visitor->Trace(pending_decodes_); + visitor->Trace(pending_metadata_decodes_); visitor->Trace(init_data_); visitor->Trace(options_); ScriptWrappable::Trace(visitor); } +void ImageDecoderExternal::CreateImageDecoder() { + DCHECK(!decoder_); + + // TODO: We should probably call ImageDecoder::SetMemoryAllocator() so that + // we can recycle frame buffers for decoded images. + + constexpr char kNoneOption[] = "none"; + + auto color_behavior = ColorBehavior::Tag(); + if (options_->colorSpaceConversion() == kNoneOption) + color_behavior = ColorBehavior::Ignore(); + + auto premultiply_alpha = ImageDecoder::kAlphaPremultiplied; + if (options_->premultiplyAlpha() == kNoneOption) + premultiply_alpha = ImageDecoder::kAlphaNotPremultiplied; + + // TODO: Is it okay to use resize size like this? + auto desired_size = SkISize::MakeEmpty(); + if (options_->hasResizeWidth() && options_->hasResizeHeight()) { + desired_size = + SkISize::Make(options_->resizeWidth(), options_->resizeHeight()); + } + + if (stream_buffer_) { + if (!segment_reader_) + segment_reader_ = SegmentReader::CreateFromSharedBuffer(stream_buffer_); + } else { + DCHECK(data_complete_); + } + + DCHECK(canDecodeType(mime_type_)); + decoder_ = ImageDecoder::CreateByMimeType( + mime_type_, segment_reader_, data_complete_, premultiply_alpha, + ImageDecoder::kHighBitDepthToHalfFloat, color_behavior, + ImageDecoder::OverrideAllowDecodeToYuv::kDeny, desired_size); + + // CreateByImageType() can't fail if we use a supported image type. Which we + // DCHECK above via canDecodeType(). + DCHECK(decoder_); +} + void ImageDecoderExternal::MaybeSatisfyPendingDecodes() { + DCHECK(decoder_); for (auto& request : pending_decodes_) { if (!data_complete_) { // We can't fulfill this promise at this time. if (request->frame_index >= frame_count_) continue; - - DCHECK(decoder_); - } else if (!decoder_ || request->frame_index >= frame_count_) { + } else if (request->frame_index >= frame_count_) { request->complete = true; // TODO: Include frameIndex in rejection? request->resolver->Reject(MakeGarbageCollected<DOMException>( @@ -268,62 +325,27 @@ void ImageDecoderExternal::MaybeSatisfyPendingDecodes() { static_cast<wtf_size_t>(new_end - pending_decodes_.begin())); } -void ImageDecoderExternal::MaybeCreateImageDecoder( - scoped_refptr<SegmentReader> sr) { - // TODO: This does not handle SVG Images since they use another "decoder." It - // is highly coupled with the DOM today, so isn't suitable for this API. - - // TODO: We should probably call ImageDecoder::SetMemoryAllocator() so that - // we can recycle frame buffers for decoded images. - - constexpr char kNoneOption[] = "none"; - - auto color_behavior = ColorBehavior::Tag(); - if (options_->colorSpaceConversion() == kNoneOption) - color_behavior = ColorBehavior::Ignore(); - - auto premultiply_alpha = ImageDecoder::kAlphaPremultiplied; - if (options_->premultiplyAlpha() == kNoneOption) - premultiply_alpha = ImageDecoder::kAlphaNotPremultiplied; - - // TODO: Is it okay to use resize size like this? - auto desired_size = SkISize::MakeEmpty(); - if (options_->hasResizeWidth() && options_->hasResizeHeight()) { - desired_size = - SkISize::Make(options_->resizeWidth(), options_->resizeHeight()); - } - - if (stream_buffer_) { - // TODO: If mime-type is supplied we must use that instead of sniffing. - if (!ImageDecoder::HasSufficientDataToSniffImageType(*stream_buffer_)) - return; - - DCHECK(!sr); - decoder_ = ImageDecoder::Create( - stream_buffer_, data_complete_, premultiply_alpha, - ImageDecoder::kHighBitDepthToHalfFloat, color_behavior, - ImageDecoder::OverrideAllowDecodeToYuv::kDeny, desired_size); - return; - } - - DCHECK(data_complete_); - decoder_ = ImageDecoder::Create( - std::move(sr), data_complete_, premultiply_alpha, - ImageDecoder::kHighBitDepthToHalfFloat, color_behavior, - ImageDecoder::OverrideAllowDecodeToYuv::kDeny, desired_size); +void ImageDecoderExternal::MaybeSatisfyPendingMetadataDecodes() { + DCHECK(decoder_); + DCHECK(decoder_->Failed() || decoder_->IsDecodedSizeAvailable()); + for (auto& resolver : pending_metadata_decodes_) + resolver->Resolve(); + pending_metadata_decodes_.clear(); } -void ImageDecoderExternal::UpdateFrameAndRepetitionCount() { - if (!decoder_) +void ImageDecoderExternal::MaybeUpdateMetadata() { + const size_t decoded_frame_count = decoder_->FrameCount(); + if (decoder_->Failed()) { + MaybeSatisfyPendingMetadataDecodes(); return; + } - const size_t decoded_frame_count = decoder_->FrameCount(); - if (decoder_->Failed()) + // Since we always create the decoder at construction, we need to wait until + // at least the size is available before signaling that metadata has been + // retrieved. + if (!decoder_->IsSizeAvailable()) return; - // TODO: Is this useful? We should have each decoder indicate its own mime - // type then. - mime_type_ = "image/todo"; frame_count_ = static_cast<uint32_t>(decoded_frame_count); // The internal value has some magic negative numbers; for external purposes @@ -332,6 +354,8 @@ void ImageDecoderExternal::UpdateFrameAndRepetitionCount() { const int decoded_repetition_count = decoder_->RepetitionCount(); if (decoded_repetition_count > 0) repetition_count_ = decoded_repetition_count; + + MaybeSatisfyPendingMetadataDecodes(); } } // namespace blink |