| /* |
| * Copyright (C) 2012 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. |
| * |
| * 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 "third_party/blink/renderer/platform/graphics/decoding_image_generator.h" |
| |
| #include <utility> |
| #include <memory> |
| |
| #include "third_party/blink/renderer/platform/graphics/image_frame_generator.h" |
| #include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h" |
| #include "third_party/blink/renderer/platform/image-decoders/image_decoder.h" |
| #include "third_party/blink/renderer/platform/image-decoders/segment_reader.h" |
| #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h" |
| #include "third_party/blink/renderer/platform/shared_buffer.h" |
| #include "third_party/skia/include/core/SkData.h" |
| |
| namespace blink { |
| |
| // static |
| std::unique_ptr<SkImageGenerator> |
| DecodingImageGenerator::CreateAsSkImageGenerator(sk_sp<SkData> data) { |
| scoped_refptr<SegmentReader> segment_reader = |
| SegmentReader::CreateFromSkData(std::move(data)); |
| // We just need the size of the image, so we have to temporarily create an |
| // ImageDecoder. Since we only need the size, the premul, high bit depth and |
| // gamma settings don't really matter. |
| const bool data_complete = true; |
| std::unique_ptr<ImageDecoder> decoder = ImageDecoder::Create( |
| segment_reader, data_complete, ImageDecoder::kAlphaPremultiplied, |
| ImageDecoder::kDefaultBitDepth, ColorBehavior::Ignore()); |
| if (!decoder || !decoder->IsSizeAvailable()) |
| return nullptr; |
| |
| const IntSize size = decoder->Size(); |
| const SkImageInfo info = |
| SkImageInfo::MakeN32(size.Width(), size.Height(), kPremul_SkAlphaType, |
| decoder->ColorSpaceForSkImages()); |
| |
| scoped_refptr<ImageFrameGenerator> frame = ImageFrameGenerator::Create( |
| SkISize::Make(size.Width(), size.Height()), false, |
| decoder->GetColorBehavior(), decoder->GetSupportedDecodeSizes()); |
| if (!frame) |
| return nullptr; |
| |
| std::vector<FrameMetadata> frames = {FrameMetadata()}; |
| sk_sp<DecodingImageGenerator> generator = DecodingImageGenerator::Create( |
| std::move(frame), info, std::move(segment_reader), std::move(frames), |
| PaintImage::GetNextContentId(), true /* all_data_received */, |
| true /* is_eligible_for_accelerated_decoding */, |
| false /* can_yuv_decode */); |
| return std::make_unique<SkiaPaintImageGenerator>( |
| std::move(generator), PaintImage::kDefaultFrameIndex, |
| PaintImage::kDefaultGeneratorClientId); |
| } |
| |
| // static |
| sk_sp<DecodingImageGenerator> DecodingImageGenerator::Create( |
| scoped_refptr<ImageFrameGenerator> frame_generator, |
| const SkImageInfo& info, |
| scoped_refptr<SegmentReader> data, |
| std::vector<FrameMetadata> frames, |
| PaintImage::ContentId content_id, |
| bool all_data_received, |
| bool is_eligible_for_accelerated_decoding, |
| bool can_yuv_decode) { |
| return sk_sp<DecodingImageGenerator>(new DecodingImageGenerator( |
| std::move(frame_generator), info, std::move(data), std::move(frames), |
| content_id, all_data_received, is_eligible_for_accelerated_decoding, |
| can_yuv_decode)); |
| } |
| |
| DecodingImageGenerator::DecodingImageGenerator( |
| scoped_refptr<ImageFrameGenerator> frame_generator, |
| const SkImageInfo& info, |
| scoped_refptr<SegmentReader> data, |
| std::vector<FrameMetadata> frames, |
| PaintImage::ContentId complete_frame_content_id, |
| bool all_data_received, |
| bool is_eligible_for_accelerated_decoding, |
| bool can_yuv_decode) |
| : PaintImageGenerator(info, std::move(frames)), |
| frame_generator_(std::move(frame_generator)), |
| data_(std::move(data)), |
| all_data_received_(all_data_received), |
| is_eligible_for_accelerated_decoding_( |
| is_eligible_for_accelerated_decoding), |
| can_yuv_decode_(can_yuv_decode), |
| complete_frame_content_id_(complete_frame_content_id) {} |
| |
| DecodingImageGenerator::~DecodingImageGenerator() = default; |
| |
| bool DecodingImageGenerator::IsEligibleForAcceleratedDecoding() const { |
| return is_eligible_for_accelerated_decoding_; |
| } |
| |
| sk_sp<SkData> DecodingImageGenerator::GetEncodedData() const { |
| TRACE_EVENT0("blink", "DecodingImageGenerator::refEncodedData"); |
| |
| // getAsSkData() may require copying, but the clients of this function are |
| // serializers, which want the data even if it requires copying, and even |
| // if the data is incomplete. (Otherwise they would potentially need to |
| // decode the partial image in order to re-encode it.) |
| return data_->GetAsSkData(); |
| } |
| |
| bool DecodingImageGenerator::GetPixels(const SkImageInfo& dst_info, |
| void* pixels, |
| size_t row_bytes, |
| size_t frame_index, |
| PaintImage::GeneratorClientId client_id, |
| uint32_t lazy_pixel_ref) { |
| TRACE_EVENT2("blink", "DecodingImageGenerator::getPixels", "frame index", |
| static_cast<int>(frame_index), "client_id", client_id); |
| |
| // Implementation only supports decoding to a supported size. |
| if (dst_info.dimensions() != GetSupportedDecodeSize(dst_info.dimensions())) { |
| return false; |
| } |
| |
| // Color type can be N32 or F16. Otherwise, decode to N32 and convert to |
| // the requested color type from N32. |
| SkImageInfo target_info = dst_info; |
| char* memory = static_cast<char*>(pixels); |
| std::unique_ptr<char[]> memory_ref_ptr; |
| size_t adjusted_row_bytes = row_bytes; |
| if ((target_info.colorType() != kN32_SkColorType) && |
| (target_info.colorType() != kRGBA_F16_SkColorType)) { |
| target_info = target_info.makeColorType(kN32_SkColorType); |
| // row_bytes is the size of scanline, so it should be >= info.minRowBytes(). |
| DCHECK(row_bytes >= dst_info.minRowBytes()); |
| // row_bytes must be a multiple of dst_info.bytesPerPixel(). |
| DCHECK_EQ(0ul, row_bytes % dst_info.bytesPerPixel()); |
| adjusted_row_bytes = |
| target_info.bytesPerPixel() * (row_bytes / dst_info.bytesPerPixel()); |
| memory_ref_ptr.reset(new char[target_info.computeMinByteSize()]); |
| memory = memory_ref_ptr.get(); |
| } |
| |
| // Skip the check for alphaType. blink::ImageFrame may have changed the |
| // owning SkBitmap to kOpaque_SkAlphaType after fully decoding the image |
| // frame, so if we see a request for opaque, that is ok even if our initial |
| // alpha type was not opaque. |
| |
| // Pass decodeColorSpace to the decoder. That is what we can expect the |
| // output to be. |
| sk_sp<SkColorSpace> decode_color_space = GetSkImageInfo().refColorSpace(); |
| SkImageInfo decode_info = target_info.makeColorSpace(decode_color_space); |
| |
| const bool needs_color_xform = !ApproximatelyEqualSkColorSpaces( |
| decode_color_space, target_info.refColorSpace()); |
| ImageDecoder::AlphaOption alpha_option = ImageDecoder::kAlphaPremultiplied; |
| if (needs_color_xform && !decode_info.isOpaque()) { |
| alpha_option = ImageDecoder::kAlphaNotPremultiplied; |
| decode_info = decode_info.makeAlphaType(kUnpremul_SkAlphaType); |
| } |
| |
| bool decoded = false; |
| { |
| TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), |
| "Decode LazyPixelRef", "LazyPixelRef", lazy_pixel_ref); |
| decoded = frame_generator_->DecodeAndScale( |
| data_.get(), all_data_received_, frame_index, decode_info, memory, |
| adjusted_row_bytes, alpha_option, client_id); |
| } |
| |
| if (decoded && needs_color_xform) { |
| TRACE_EVENT0("blink", "DecodingImageGenerator::getPixels - apply xform"); |
| SkPixmap src(decode_info, memory, adjusted_row_bytes); |
| decoded = src.readPixels(target_info, memory, adjusted_row_bytes); |
| DCHECK(decoded); |
| } |
| |
| // Convert the color type to the requested one if necessary |
| if (decoded && target_info.colorType() != dst_info.colorType()) { |
| auto canvas = SkCanvas::MakeRasterDirect(dst_info, pixels, row_bytes); |
| DCHECK(canvas); |
| SkPaint paint; |
| if (dst_info.colorType() == kARGB_4444_SkColorType || |
| dst_info.colorType() == kRGB_565_SkColorType) { |
| paint.setDither(true); |
| } |
| paint.setBlendMode(SkBlendMode::kSrc); |
| SkBitmap bitmap; |
| decoded = bitmap.installPixels(target_info, memory, adjusted_row_bytes); |
| DCHECK(decoded); |
| canvas->drawBitmap(bitmap, 0, 0, &paint); |
| } |
| return decoded; |
| } |
| |
| bool DecodingImageGenerator::QueryYUVA8( |
| SkYUVASizeInfo* size_info, |
| SkYUVAIndex indices[SkYUVAIndex::kIndexCount], |
| SkYUVColorSpace* color_space) const { |
| if (!can_yuv_decode_) |
| return false; |
| |
| TRACE_EVENT0("blink", "DecodingImageGenerator::queryYUVA8"); |
| |
| // TODO(crbug.com/915707): Set the colorspace based on image type. |
| // Can pass |color_space| to GetYUVComponentSizes and query a method |
| // in ImageDecoder (to be added) to get the proper value. |
| if (color_space) |
| *color_space = kJPEG_SkYUVColorSpace; |
| |
| // Indicate that we have three separate planes |
| indices[SkYUVAIndex::kY_Index] = {0, SkColorChannel::kR}; |
| indices[SkYUVAIndex::kU_Index] = {1, SkColorChannel::kR}; |
| indices[SkYUVAIndex::kV_Index] = {2, SkColorChannel::kR}; |
| indices[SkYUVAIndex::kA_Index] = {-1, SkColorChannel::kR}; |
| |
| DCHECK(all_data_received_); |
| return frame_generator_->GetYUVComponentSizes(data_.get(), size_info); |
| } |
| |
| bool DecodingImageGenerator::GetYUVA8Planes(const SkYUVASizeInfo& size_info, |
| const SkYUVAIndex indices[4], |
| void* planes[3], |
| size_t frame_index, |
| uint32_t lazy_pixel_ref) { |
| // TODO(crbug.com/943519): YUV decoding does not currently support incremental |
| // decoding. See comment in image_frame_generator.h. |
| DCHECK(can_yuv_decode_); |
| DCHECK(all_data_received_); |
| |
| TRACE_EVENT0("blink", "DecodingImageGenerator::getYUVA8Planes"); |
| TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), |
| "Decode LazyPixelRef", "LazyPixelRef", lazy_pixel_ref); |
| |
| // Verify sizes and indices |
| for (int i = 0; i < 3; ++i) { |
| if (size_info.fSizes[i].isEmpty() || !size_info.fWidthBytes[i]) { |
| return false; |
| } |
| } |
| if (!size_info.fSizes[3].isEmpty() || size_info.fWidthBytes[3]) { |
| return false; |
| } |
| int numPlanes; |
| if (!SkYUVAIndex::AreValidIndices(indices, &numPlanes) || numPlanes != 3) { |
| return false; |
| } |
| |
| bool decoded = |
| frame_generator_->DecodeToYUV(data_.get(), frame_index, size_info.fSizes, |
| planes, size_info.fWidthBytes); |
| return decoded; |
| } |
| |
| SkISize DecodingImageGenerator::GetSupportedDecodeSize( |
| const SkISize& requested_size) const { |
| return frame_generator_->GetSupportedDecodeSize(requested_size); |
| } |
| |
| PaintImage::ContentId DecodingImageGenerator::GetContentIdForFrame( |
| size_t frame_index) const { |
| DCHECK_LT(frame_index, GetFrameMetadata().size()); |
| |
| // If we have all the data for the image, or this particular frame, we can |
| // consider the decoded frame constant. |
| if (all_data_received_ || GetFrameMetadata().at(frame_index).complete) |
| return complete_frame_content_id_; |
| |
| return PaintImageGenerator::GetContentIdForFrame(frame_index); |
| } |
| |
| } // namespace blink |