blob: eef4397595f7759297472c3d23cc6639026f8a8b [file] [log] [blame]
// 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/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 {
namespace {
SkRect GetSubsampledRect(const SkRect& rect,
const std::array<float, 2>& subsampling_factors) {
return SkRect::MakeXYWH(rect.x() * subsampling_factors[0],
rect.y() * subsampling_factors[1],
rect.width() * subsampling_factors[0],
rect.height() * subsampling_factors[1]);
}
} // namespace
void BlitRGBAToYUVA(SkImage* src_image,
SkSurface* dst_surfaces[SkYUVAInfo::kMaxPlanes],
const SkYUVAInfo& dst_yuva_info,
const SkRect& dst_region,
bool clear_destination) {
// Rectangle representing the entire destination image:
const SkRect dst_image_rect = SkRect::Make(dst_yuva_info.dimensions());
const SkRect src_rect = SkRect::Make(src_image->bounds());
// 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.
SkColorMatrix permutation_matrices[SkYUVAInfo::kMaxPlanes];
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)));
// Subsampling factors are determined by the ratios of the entire image's
// width & height to the dimensions of the passed in surfaces (which should
// also span the entire logical image):
std::array<float, 2> subsampling_factors = {
static_cast<float>(dst_surfaces[plane]->width()) /
dst_yuva_info.dimensions().width(),
static_cast<float>(dst_surfaces[plane]->height()) /
dst_yuva_info.dimensions().height(),
};
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);
}
const SkRect plane_dst_rect =
GetSubsampledRect(dst_rect, subsampling_factors);
plane_canvas->drawImageRect(src_image, src_rect, plane_dst_rect,
sampling_options, &paint,
SkCanvas::kFast_SrcRectConstraint);
}
}
} // namespace skia