2 * Copyright 2019 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "tools/gpu/YUVUtils.h"
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkColorFilter.h"
12 #include "include/core/SkColorPriv.h"
13 #include "include/core/SkData.h"
14 #include "include/core/SkSurface.h"
15 #include "include/gpu/GrRecordingContext.h"
16 #include "include/gpu/GrYUVABackendTextures.h"
17 #include "src/codec/SkCodecImageGenerator.h"
18 #include "src/core/SkYUVAInfoLocation.h"
19 #include "src/core/SkYUVMath.h"
20 #include "src/gpu/ganesh/GrDirectContextPriv.h"
21 #include "src/gpu/ganesh/GrRecordingContextPriv.h"
22 #include "tools/gpu/ManagedBackendTexture.h"
26 static SkPMColor convert_yuva_to_rgba(const float mtx[20], uint8_t yuva[4]) {
32 uint8_t r = SkTPin(SkScalarRoundToInt(mtx[ 0]*y + mtx[ 1]*u + mtx[ 2]*v + mtx[ 4]*255), 0, 255);
33 uint8_t g = SkTPin(SkScalarRoundToInt(mtx[ 5]*y + mtx[ 6]*u + mtx[ 7]*v + mtx[ 9]*255), 0, 255);
34 uint8_t b = SkTPin(SkScalarRoundToInt(mtx[10]*y + mtx[11]*u + mtx[12]*v + mtx[14]*255), 0, 255);
36 return SkPremultiplyARGBInline(a, r, g, b);
39 static uint8_t look_up(SkPoint normPt, const SkPixmap& pmap, SkColorChannel channel) {
40 SkASSERT(normPt.x() > 0 && normPt.x() < 1.0f);
41 SkASSERT(normPt.y() > 0 && normPt.y() < 1.0f);
42 int x = SkScalarFloorToInt(normPt.x() * pmap.width());
43 int y = SkScalarFloorToInt(normPt.y() * pmap.height());
45 auto ii = pmap.info().makeColorType(kRGBA_8888_SkColorType).makeWH(1, 1);
47 SkAssertResult(pmap.readPixels(ii, &pixel, sizeof(pixel), x, y));
48 int shift = static_cast<int>(channel) * 8;
49 return static_cast<uint8_t>((pixel >> shift) & 0xff);
52 class Generator : public SkImageGenerator {
54 Generator(SkYUVAPixmaps pixmaps, sk_sp<SkColorSpace> cs)
55 : SkImageGenerator(SkImageInfo::Make(pixmaps.yuvaInfo().dimensions(),
59 , fPixmaps(std::move(pixmaps)) {}
62 bool onGetPixels(const SkImageInfo& info,
65 const Options&) override {
66 if (kUnknown_SkColorType == fFlattened.colorType()) {
67 fFlattened.allocPixels(info);
68 SkASSERT(info == this->getInfo());
71 SkColorMatrix_YUV2RGB(fPixmaps.yuvaInfo().yuvColorSpace(), mtx);
72 SkYUVAInfo::YUVALocations yuvaLocations = fPixmaps.toYUVALocations();
73 SkASSERT(SkYUVAInfo::YUVALocation::AreValidLocations(yuvaLocations));
75 SkMatrix om = fPixmaps.yuvaInfo().originMatrix();
76 SkAssertResult(om.invert(&om));
77 float normX = 1.f/info.width();
78 float normY = 1.f/info.height();
79 if (SkEncodedOriginSwapsWidthHeight(fPixmaps.yuvaInfo().origin())) {
83 for (int y = 0; y < info.height(); ++y) {
84 for (int x = 0; x < info.width(); ++x) {
85 SkPoint xy1 {(x + 0.5f),
87 xy1 = om.mapPoint(xy1);
91 uint8_t yuva[4] = {0, 0, 0, 255};
93 for (auto c : {SkYUVAInfo::YUVAChannels::kY,
94 SkYUVAInfo::YUVAChannels::kU,
95 SkYUVAInfo::YUVAChannels::kV}) {
96 const auto& pmap = fPixmaps.plane(yuvaLocations[c].fPlane);
97 yuva[c] = look_up(xy1, pmap, yuvaLocations[c].fChannel);
99 auto [aPlane, aChan] = yuvaLocations[SkYUVAInfo::YUVAChannels::kA];
101 const auto& pmap = fPixmaps.plane(aPlane);
102 yuva[3] = look_up(xy1, pmap, aChan);
105 // Making premul here.
106 *fFlattened.getAddr32(x, y) = convert_yuva_to_rgba(mtx, yuva);
111 return fFlattened.readPixels(info, pixels, rowBytes, 0, 0);
114 bool onQueryYUVAInfo(const SkYUVAPixmapInfo::SupportedDataTypes& types,
115 SkYUVAPixmapInfo* info) const override {
116 *info = fPixmaps.pixmapsInfo();
117 return info->isValid();
120 bool onGetYUVAPlanes(const SkYUVAPixmaps& pixmaps) override {
121 SkASSERT(pixmaps.yuvaInfo() == fPixmaps.yuvaInfo());
122 for (int i = 0; i < pixmaps.numPlanes(); ++i) {
123 SkASSERT(fPixmaps.plane(i).colorType() == pixmaps.plane(i).colorType());
124 SkASSERT(fPixmaps.plane(i).dimensions() == pixmaps.plane(i).dimensions());
125 SkASSERT(fPixmaps.plane(i).rowBytes() == pixmaps.plane(i).rowBytes());
126 fPixmaps.plane(i).readPixels(pixmaps.plane(i));
132 SkYUVAPixmaps fPixmaps;
136 } // anonymous namespace
138 namespace sk_gpu_test {
140 std::tuple<std::array<sk_sp<SkImage>, SkYUVAInfo::kMaxPlanes>, SkYUVAInfo>
141 MakeYUVAPlanesAsA8(SkImage* src,
143 SkYUVAInfo::Subsampling ss,
144 GrRecordingContext* rContext) {
146 SkColorMatrix_RGB2YUV(cs, rgbToYUV);
148 SkYUVAInfo::PlaneConfig config = src->isOpaque() ? SkYUVAInfo::PlaneConfig::kY_U_V
149 : SkYUVAInfo::PlaneConfig::kY_U_V_A;
150 SkISize dims[SkYUVAInfo::kMaxPlanes];
151 int n = SkYUVAInfo::PlaneDimensions(src->dimensions(),
154 kTopLeft_SkEncodedOrigin,
156 std::array<sk_sp<SkImage>, 4> planes;
157 for (int i = 0; i < n; ++i) {
158 SkImageInfo info = SkImageInfo::MakeA8(dims[i]);
159 sk_sp<SkSurface> surf;
161 surf = SkSurface::MakeRenderTarget(rContext, SkBudgeted::kYes, info, 1, nullptr);
163 surf = SkSurface::MakeRaster(info);
170 paint.setBlendMode(SkBlendMode::kSrc);
172 // Make a matrix with the ith row of rgbToYUV copied to the A row since we're drawing to A8.
174 std::copy_n(rgbToYUV + 5*i, 5, m + 15);
175 paint.setColorFilter(SkColorFilters::Matrix(m));
176 surf->getCanvas()->drawImageRect(src,
177 SkRect::Make(dims[i]),
178 SkSamplingOptions(SkFilterMode::kLinear),
180 planes[i] = surf->makeImageSnapshot();
185 SkYUVAInfo info(src->dimensions(), config, ss, cs);
186 return {planes, info};
189 std::unique_ptr<LazyYUVImage> LazyYUVImage::Make(sk_sp<SkData> data,
190 GrMipmapped mipmapped,
191 sk_sp<SkColorSpace> cs) {
192 std::unique_ptr<LazyYUVImage> image(new LazyYUVImage());
193 if (image->reset(std::move(data), mipmapped, std::move(cs))) {
200 std::unique_ptr<LazyYUVImage> LazyYUVImage::Make(SkYUVAPixmaps pixmaps,
201 GrMipmapped mipmapped,
202 sk_sp<SkColorSpace> cs) {
203 std::unique_ptr<LazyYUVImage> image(new LazyYUVImage());
204 if (image->reset(std::move(pixmaps), mipmapped, std::move(cs))) {
211 sk_sp<SkImage> LazyYUVImage::refImage(GrRecordingContext* rContext, Type type) {
212 if (this->ensureYUVImage(rContext, type)) {
213 size_t idx = static_cast<size_t>(type);
214 SkASSERT(idx < SK_ARRAY_COUNT(fYUVImage));
215 return fYUVImage[idx];
221 bool LazyYUVImage::reset(sk_sp<SkData> data, GrMipmapped mipmapped, sk_sp<SkColorSpace> cs) {
222 fMipmapped = mipmapped;
223 auto codec = SkCodecImageGenerator::MakeFromEncodedCodec(data);
228 SkYUVAPixmapInfo yuvaPixmapInfo;
229 if (!codec->queryYUVAInfo(SkYUVAPixmapInfo::SupportedDataTypes::All(), &yuvaPixmapInfo)) {
232 fPixmaps = SkYUVAPixmaps::Allocate(yuvaPixmapInfo);
233 if (!fPixmaps.isValid()) {
237 if (!codec->getYUVAPlanes(fPixmaps)) {
241 fColorSpace = std::move(cs);
243 // The SkPixmap data is fully configured now for MakeFromYUVAPixmaps once we get a GrContext
247 bool LazyYUVImage::reset(SkYUVAPixmaps pixmaps, GrMipmapped mipmapped, sk_sp<SkColorSpace> cs) {
248 if (!pixmaps.isValid()) {
251 fMipmapped = mipmapped;
252 if (pixmaps.ownsStorage()) {
253 fPixmaps = std::move(pixmaps);
255 fPixmaps = SkYUVAPixmaps::MakeCopy(std::move(pixmaps));
257 fColorSpace = std::move(cs);
258 // The SkPixmap data is fully configured now for MakeFromYUVAPixmaps once we get a GrContext
262 bool LazyYUVImage::ensureYUVImage(GrRecordingContext* rContext, Type type) {
263 size_t idx = static_cast<size_t>(type);
264 SkASSERT(idx < SK_ARRAY_COUNT(fYUVImage));
265 if (fYUVImage[idx] && fYUVImage[idx]->isValid(rContext)) {
266 return true; // Have already made a YUV image valid for this context.
268 // Try to make a new YUV image for this context.
270 case Type::kFromPixmaps:
271 if (!rContext || rContext->abandoned()) {
274 fYUVImage[idx] = SkImage::MakeFromYUVAPixmaps(rContext,
277 /*limit to max tex size*/ false,
280 case Type::kFromGenerator: {
281 // Make sure the generator has ownership of its backing planes.
282 auto generator = std::make_unique<Generator>(fPixmaps, fColorSpace);
283 fYUVImage[idx] = SkImage::MakeFromGenerator(std::move(generator));
286 case Type::kFromTextures:
287 if (!rContext || rContext->abandoned()) {
290 if (fMipmapped == GrMipmapped::kYes) {
291 // If this becomes necessary we should invoke SkMipmapBuilder here to make mip
292 // maps from our src data (and then pass a pixmaps array to initialize the planar
296 if (auto direct = rContext->asDirectContext()) {
297 sk_sp<sk_gpu_test::ManagedBackendTexture> mbets[SkYUVAInfo::kMaxPlanes];
298 GrBackendTexture textures[SkYUVAInfo::kMaxPlanes];
299 for (int i = 0; i < fPixmaps.numPlanes(); ++i) {
300 mbets[i] = sk_gpu_test::ManagedBackendTexture::MakeWithData(
303 kTopLeft_GrSurfaceOrigin,
307 textures[i] = mbets[i]->texture();
312 GrYUVABackendTextures yuvaTextures(fPixmaps.yuvaInfo(),
314 kTopLeft_GrSurfaceOrigin);
315 if (!yuvaTextures.isValid()) {
318 void* planeRelContext =
319 sk_gpu_test::ManagedBackendTexture::MakeYUVAReleaseContext(mbets);
320 fYUVImage[idx] = SkImage::MakeFromYUVATextures(
324 sk_gpu_test::ManagedBackendTexture::ReleaseProc,
328 return fYUVImage[idx] != nullptr;
330 } // namespace sk_gpu_test