| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/viz/common/frame_sinks/copy_output_result.h" |
| |
| #include "base/logging.h" |
| #include "third_party/libyuv/include/libyuv.h" |
| #include "ui/gfx/color_space.h" |
| |
| namespace viz { |
| |
| CopyOutputResult::CopyOutputResult(Format format, const gfx::Rect& rect) |
| : format_(format), rect_(rect) { |
| DCHECK(format_ == Format::RGBA_BITMAP || format_ == Format::RGBA_TEXTURE || |
| format_ == Format::I420_PLANES); |
| } |
| |
| CopyOutputResult::~CopyOutputResult() = default; |
| |
| bool CopyOutputResult::IsEmpty() const { |
| if (rect_.IsEmpty()) |
| return true; |
| switch (format_) { |
| case Format::RGBA_BITMAP: |
| case Format::I420_PLANES: |
| return false; |
| case Format::RGBA_TEXTURE: |
| if (const TextureResult* result = GetTextureResult()) |
| return result->mailbox.IsZero(); |
| else |
| return true; |
| } |
| NOTREACHED(); |
| return true; |
| } |
| |
| const SkBitmap& CopyOutputResult::AsSkBitmap() const { |
| DCHECK(!cached_bitmap_.readyToDraw() || cached_bitmap_.colorSpace()); |
| return cached_bitmap_; |
| } |
| |
| const CopyOutputResult::TextureResult* CopyOutputResult::GetTextureResult() |
| const { |
| return nullptr; |
| } |
| |
| std::unique_ptr<SingleReleaseCallback> |
| CopyOutputResult::TakeTextureOwnership() { |
| return nullptr; |
| } |
| |
| bool CopyOutputResult::ReadI420Planes(uint8_t* y_out, |
| int y_out_stride, |
| uint8_t* u_out, |
| int u_out_stride, |
| uint8_t* v_out, |
| int v_out_stride) const { |
| const SkBitmap& bitmap = AsSkBitmap(); |
| if (!bitmap.readyToDraw()) |
| return false; |
| const uint8_t* pixels = static_cast<uint8_t*>(bitmap.getPixels()); |
| // The conversion below ignores color space completely, and it's not even |
| // sRGB→Rec.709. Unfortunately, hand-optimized routines are not available, and |
| // a perfect conversion using gfx::ColorTransform would execute way too |
| // slowly. See SoftwareRenderer for related comments on its lack of color |
| // space management (due to performance concerns). |
| if (bitmap.colorType() == kBGRA_8888_SkColorType) { |
| return 0 == libyuv::ARGBToI420(pixels, bitmap.rowBytes(), y_out, |
| y_out_stride, u_out, u_out_stride, v_out, |
| v_out_stride, bitmap.width(), |
| bitmap.height()); |
| } else if (bitmap.colorType() == kRGBA_8888_SkColorType) { |
| return 0 == libyuv::ABGRToI420(pixels, bitmap.rowBytes(), y_out, |
| y_out_stride, u_out, u_out_stride, v_out, |
| v_out_stride, bitmap.width(), |
| bitmap.height()); |
| } |
| |
| // Other SkBitmap color types could be supported, but are currently never |
| // being used. |
| NOTIMPLEMENTED(); |
| return false; |
| } |
| |
| bool CopyOutputResult::ReadRGBAPlane(uint8_t* dest, int stride) const { |
| const SkBitmap& bitmap = AsSkBitmap(); |
| if (!bitmap.readyToDraw()) |
| return false; |
| DCHECK(bitmap.colorSpace()); |
| SkImageInfo image_info = |
| SkImageInfo::MakeN32(bitmap.width(), bitmap.height(), kPremul_SkAlphaType, |
| bitmap.refColorSpace()); |
| bitmap.readPixels(image_info, dest, stride, 0, 0); |
| return true; |
| } |
| |
| gfx::ColorSpace CopyOutputResult::GetRGBAColorSpace() const { |
| const SkBitmap& bitmap = AsSkBitmap(); |
| if (!bitmap.readyToDraw()) |
| return gfx::ColorSpace(); |
| DCHECK(bitmap.colorSpace()); |
| return gfx::ColorSpace(*(bitmap.colorSpace())); |
| } |
| |
| CopyOutputSkBitmapResult::CopyOutputSkBitmapResult(const gfx::Rect& rect, |
| const SkBitmap& bitmap) |
| : CopyOutputSkBitmapResult(Format::RGBA_BITMAP, rect, bitmap) {} |
| |
| CopyOutputSkBitmapResult::CopyOutputSkBitmapResult( |
| CopyOutputResult::Format format, |
| const gfx::Rect& rect, |
| const SkBitmap& bitmap) |
| : CopyOutputResult(format, rect) { |
| DCHECK(format == Format::RGBA_BITMAP || format == Format::I420_PLANES); |
| if (!rect.IsEmpty()) { |
| DCHECK(!bitmap.readyToDraw() || bitmap.colorSpace()); |
| // Hold a reference to the |bitmap|'s pixels, for AsSkBitmap(). |
| *(cached_bitmap()) = bitmap; |
| } |
| } |
| |
| const SkBitmap& CopyOutputSkBitmapResult::AsSkBitmap() const { |
| SkBitmap* const bitmap = cached_bitmap(); |
| |
| if (rect().IsEmpty()) |
| return *bitmap; // Return "null" bitmap for empty result. |
| |
| const SkImageInfo image_info = SkImageInfo::MakeN32Premul( |
| rect().width(), rect().height(), bitmap->refColorSpace()); |
| if (bitmap->info() == image_info && bitmap->readyToDraw()) |
| return *bitmap; // Return bitmap in expected format. |
| |
| // The bitmap is not in the "native optimized" format. Convert it once for |
| // this and all future calls of this method. |
| SkBitmap replacement; |
| replacement.allocPixels(image_info); |
| replacement.eraseColor(SK_ColorBLACK); |
| SkPixmap src_pixmap; |
| if (bitmap->peekPixels(&src_pixmap)) { |
| // Note: writePixels() can fail, but then the replacement bitmap will be |
| // left with part/all solid black due to the eraseColor() call above. |
| replacement.writePixels(src_pixmap); |
| } |
| *bitmap = replacement; |
| |
| return *bitmap; |
| } |
| |
| CopyOutputSkBitmapResult::~CopyOutputSkBitmapResult() = default; |
| |
| CopyOutputTextureResult::CopyOutputTextureResult( |
| const gfx::Rect& rect, |
| const gpu::Mailbox& mailbox, |
| const gpu::SyncToken& sync_token, |
| const gfx::ColorSpace& color_space, |
| std::unique_ptr<SingleReleaseCallback> release_callback) |
| : CopyOutputResult(Format::RGBA_TEXTURE, rect), |
| texture_result_(mailbox, sync_token, color_space), |
| release_callback_(std::move(release_callback)) { |
| DCHECK_EQ(rect.IsEmpty(), mailbox.IsZero()); |
| DCHECK_EQ(!release_callback_, mailbox.IsZero()); |
| DCHECK(texture_result_.mailbox.IsZero() || |
| texture_result_.color_space.IsValid()); |
| } |
| |
| CopyOutputTextureResult::~CopyOutputTextureResult() { |
| if (release_callback_) |
| release_callback_->Run(gpu::SyncToken(), false); |
| } |
| |
| const CopyOutputResult::TextureResult* |
| CopyOutputTextureResult::GetTextureResult() const { |
| return &texture_result_; |
| } |
| |
| std::unique_ptr<SingleReleaseCallback> |
| CopyOutputTextureResult::TakeTextureOwnership() { |
| texture_result_.mailbox = gpu::Mailbox(); |
| texture_result_.sync_token = gpu::SyncToken(); |
| texture_result_.color_space = gfx::ColorSpace(); |
| return std::move(release_callback_); |
| } |
| |
| } // namespace viz |