| // Copyright 2021 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "skia/ext/rgba_to_yuva.h" |
| |
| #include <array> |
| |
| #include "base/check_op.h" |
| #include "base/compiler_specific.h" |
| #include "base/containers/span.h" |
| #include "base/logging.h" |
| #include "base/notreached.h" |
| #include "third_party/skia/include/core/SkBlendMode.h" |
| #include "third_party/skia/include/core/SkCanvas.h" |
| #include "third_party/skia/include/core/SkClipOp.h" |
| #include "third_party/skia/include/core/SkColor.h" |
| #include "third_party/skia/include/core/SkColorFilter.h" |
| #include "third_party/skia/include/core/SkPaint.h" |
| #include "third_party/skia/include/core/SkRect.h" |
| #include "third_party/skia/include/effects/SkColorMatrix.h" |
| |
| namespace skia { |
| |
| void BlitRGBAToYUVA(SkImage* src_image, |
| base::span<SkSurface* const> dst_surfaces, |
| const SkYUVAInfo& dst_yuva_info, |
| const SkRect& dst_region, |
| bool clear_destination, |
| const SkRect& src_region) { |
| // Rectangle representing the entire destination image: |
| const SkRect dst_image_rect = SkRect::Make(dst_yuva_info.dimensions()); |
| const SkRect src_rect = src_image && src_region.isEmpty() |
| ? SkRect::Make(src_image->bounds()) |
| : src_region; |
| // Region of destination image that is supposed to be populated: |
| const SkRect dst_rect = dst_region.isEmpty() ? dst_image_rect : dst_region; |
| |
| DCHECK(dst_image_rect.contains(dst_rect)); |
| |
| // Permutation matrices to select the appropriate YUVA channels for each |
| // output plane. |
| constexpr SkColorMatrix xxxY(0, 0, 0, 0, 0, // |
| 0, 0, 0, 0, 0, // |
| 0, 0, 0, 0, 0, // |
| 1, 0, 0, 0, 0); |
| constexpr SkColorMatrix UVx1(0, 1, 0, 0, 0, // |
| 0, 0, 1, 0, 0, // |
| 0, 0, 0, 0, 0, // |
| 0, 0, 0, 1, 0); |
| |
| // Only Y_UV has been tested. |
| std::array<SkColorMatrix, SkYUVAInfo::kMaxPlanes> permutation_matrices; |
| switch (dst_yuva_info.planeConfig()) { |
| case SkYUVAInfo::PlaneConfig::kY_UV: |
| permutation_matrices[0] = xxxY; |
| permutation_matrices[1] = UVx1; |
| break; |
| default: |
| DLOG(ERROR) << "Unsupported plane configuration."; |
| return; |
| } |
| SkColorMatrix rgb_to_yuv_matrix = |
| SkColorMatrix::RGBtoYUV(dst_yuva_info.yuvColorSpace()); |
| |
| // Blit each plane. |
| for (int plane = 0; plane < dst_yuva_info.numPlanes(); ++plane) { |
| SkCanvas* plane_canvas = dst_surfaces[plane]->getCanvas(); |
| |
| SkColorMatrix color_matrix = rgb_to_yuv_matrix; |
| color_matrix.postConcat(permutation_matrices[plane]); |
| |
| SkSamplingOptions sampling_options(SkFilterMode::kLinear); |
| |
| SkPaint paint; |
| paint.setBlendMode(SkBlendMode::kSrc); |
| |
| // Blend the input image over black before performing RGB to YUV |
| // conversion, to match un-accelerated versions. |
| paint.setColorFilter(SkColorFilters::Compose( |
| SkColorFilters::Matrix(color_matrix), |
| SkColorFilters::Blend(SK_ColorBLACK, SkBlendMode::kDstOver))); |
| |
| auto [ssHoriz, ssVert] = dst_yuva_info.planeSubsamplingFactors(plane); |
| const SkRect plane_dst_rect = SkRect::MakeXYWH( |
| dst_rect.x() / ssHoriz, dst_rect.y() / ssVert, |
| dst_rect.width() / ssHoriz, dst_rect.height() / ssVert); |
| |
| if (!src_image) { |
| paint.setColor(SK_ColorBLACK); |
| plane_canvas->drawRect(plane_dst_rect, paint); |
| } else { |
| if (clear_destination && dst_image_rect != dst_rect) { |
| // If we were told to clear the destination prior to blitting and we |
| // know the blit won't populate the entire destination image, issue the |
| // draw call that fills the destination with black and takes into |
| // account the color conversion needed. |
| SkPaint clear_paint(paint); |
| clear_paint.setColor(SK_ColorBLACK); |
| |
| plane_canvas->drawPaint(clear_paint); |
| } |
| |
| SkCanvas::SrcRectConstraint constraint = |
| SkCanvas::kFast_SrcRectConstraint; |
| if (src_rect != SkRect::Make(src_image->bounds())) { |
| constraint = SkCanvas::kStrict_SrcRectConstraint; |
| } |
| plane_canvas->drawImageRect(src_image, src_rect, plane_dst_rect, |
| sampling_options, &paint, constraint); |
| } |
| } |
| } |
| |
| } // namespace skia |