blob: 934499832558c3910497881382cb92ce6a7ba157 [file] [log] [blame]
Michael Ludwigd9958f82019-03-21 13:08:36 -04001/*
2 * Copyright 2019 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "tools/gpu/YUVUtils.h"
Michael Ludwigd9958f82019-03-21 13:08:36 -04009
Brian Salomon7db71392020-10-16 10:05:21 -040010#include "include/core/SkColorPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "include/core/SkData.h"
Brian Salomonefb5f072020-07-28 21:06:43 -040012#include "include/gpu/GrRecordingContext.h"
Brian Salomonc1a249d2020-10-19 10:55:45 -040013#include "include/gpu/GrYUVABackendTextures.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050014#include "src/codec/SkCodecImageGenerator.h"
Brian Salomon0c0b5a62021-01-11 14:40:44 -050015#include "src/core/SkYUVAInfoLocation.h"
Brian Salomon7db71392020-10-16 10:05:21 -040016#include "src/core/SkYUVMath.h"
Adlai Hollera0693042020-10-14 11:23:11 -040017#include "src/gpu/GrDirectContextPriv.h"
Brian Salomonefb5f072020-07-28 21:06:43 -040018#include "src/gpu/GrRecordingContextPriv.h"
Brian Salomon7db71392020-10-16 10:05:21 -040019#include "tools/gpu/ManagedBackendTexture.h"
20
21namespace {
22
23static SkPMColor convert_yuva_to_rgba(const float mtx[20], uint8_t yuva[4]) {
24 uint8_t y = yuva[0];
25 uint8_t u = yuva[1];
26 uint8_t v = yuva[2];
27 uint8_t a = yuva[3];
28
29 uint8_t r = SkTPin(SkScalarRoundToInt(mtx[ 0]*y + mtx[ 1]*u + mtx[ 2]*v + mtx[ 4]*255), 0, 255);
30 uint8_t g = SkTPin(SkScalarRoundToInt(mtx[ 5]*y + mtx[ 6]*u + mtx[ 7]*v + mtx[ 9]*255), 0, 255);
31 uint8_t b = SkTPin(SkScalarRoundToInt(mtx[10]*y + mtx[11]*u + mtx[12]*v + mtx[14]*255), 0, 255);
32
33 return SkPremultiplyARGBInline(a, r, g, b);
34}
35
Brian Salomon96796122021-01-19 12:11:07 -050036static uint8_t look_up(SkPoint normPt, const SkPixmap& pmap, SkColorChannel channel) {
37 SkASSERT(normPt.x() > 0 && normPt.x() < 1.0f);
38 SkASSERT(normPt.y() > 0 && normPt.y() < 1.0f);
39 int x = SkScalarFloorToInt(normPt.x() * pmap.width());
40 int y = SkScalarFloorToInt(normPt.y() * pmap.height());
Brian Salomon7db71392020-10-16 10:05:21 -040041
42 auto ii = pmap.info().makeColorType(kRGBA_8888_SkColorType).makeWH(1, 1);
43 uint32_t pixel;
44 SkAssertResult(pmap.readPixels(ii, &pixel, sizeof(pixel), x, y));
45 int shift = static_cast<int>(channel) * 8;
46 return static_cast<uint8_t>((pixel >> shift) & 0xff);
47}
48
49class Generator : public SkImageGenerator {
50public:
51 Generator(SkYUVAPixmaps pixmaps, sk_sp<SkColorSpace> cs)
52 : SkImageGenerator(SkImageInfo::Make(pixmaps.yuvaInfo().dimensions(),
53 kN32_SkColorType,
54 kPremul_SkAlphaType,
55 std::move(cs)))
56 , fPixmaps(std::move(pixmaps)) {}
57
58protected:
59 bool onGetPixels(const SkImageInfo& info,
60 void* pixels,
61 size_t rowBytes,
62 const Options&) override {
63 if (kUnknown_SkColorType == fFlattened.colorType()) {
64 fFlattened.allocPixels(info);
65 SkASSERT(info == this->getInfo());
66
67 float mtx[20];
68 SkColorMatrix_YUV2RGB(fPixmaps.yuvaInfo().yuvColorSpace(), mtx);
Brian Salomon0c0b5a62021-01-11 14:40:44 -050069 SkYUVAInfo::YUVALocations yuvaLocations = fPixmaps.toYUVALocations();
70 SkASSERT(SkYUVAInfo::YUVALocation::AreValidLocations(yuvaLocations));
Brian Salomon7db71392020-10-16 10:05:21 -040071
Brian Salomon96796122021-01-19 12:11:07 -050072 SkMatrix om = fPixmaps.yuvaInfo().originMatrix();
73 SkAssertResult(om.invert(&om));
74 float normX = 1.f/info.width();
75 float normY = 1.f/info.height();
76 if (SkEncodedOriginSwapsWidthHeight(fPixmaps.yuvaInfo().origin())) {
77 using std::swap;
78 swap(normX, normY);
79 }
Brian Salomon7db71392020-10-16 10:05:21 -040080 for (int y = 0; y < info.height(); ++y) {
81 for (int x = 0; x < info.width(); ++x) {
Brian Salomon96796122021-01-19 12:11:07 -050082 SkPoint xy1 {(x + 0.5f),
83 (y + 0.5f)};
84 xy1 = om.mapPoint(xy1);
85 xy1.fX *= normX;
86 xy1.fY *= normY;
Brian Salomon7db71392020-10-16 10:05:21 -040087
88 uint8_t yuva[4] = {0, 0, 0, 255};
89
Brian Salomon0c0b5a62021-01-11 14:40:44 -050090 for (auto c : {SkYUVAInfo::YUVAChannels::kY,
91 SkYUVAInfo::YUVAChannels::kU,
92 SkYUVAInfo::YUVAChannels::kV}) {
93 const auto& pmap = fPixmaps.plane(yuvaLocations[c].fPlane);
Brian Salomon96796122021-01-19 12:11:07 -050094 yuva[c] = look_up(xy1, pmap, yuvaLocations[c].fChannel);
Brian Salomon7db71392020-10-16 10:05:21 -040095 }
Brian Salomon96796122021-01-19 12:11:07 -050096 auto [aPlane, aChan] = yuvaLocations[SkYUVAInfo::YUVAChannels::kA];
97 if (aPlane >= 0) {
98 const auto& pmap = fPixmaps.plane(aPlane);
99 yuva[3] = look_up(xy1, pmap, aChan);
Brian Salomon7db71392020-10-16 10:05:21 -0400100 }
101
102 // Making premul here.
103 *fFlattened.getAddr32(x, y) = convert_yuva_to_rgba(mtx, yuva);
104 }
105 }
106 }
107
108 return fFlattened.readPixels(info, pixels, rowBytes, 0, 0);
109 }
110
111 bool onQueryYUVAInfo(const SkYUVAPixmapInfo::SupportedDataTypes& types,
112 SkYUVAPixmapInfo* info) const override {
113 *info = fPixmaps.pixmapsInfo();
114 return info->isValid();
115 }
116
117 bool onGetYUVAPlanes(const SkYUVAPixmaps& pixmaps) override {
118 SkASSERT(pixmaps.yuvaInfo() == fPixmaps.yuvaInfo());
119 for (int i = 0; i < pixmaps.numPlanes(); ++i) {
120 SkASSERT(fPixmaps.plane(i).colorType() == pixmaps.plane(i).colorType());
121 SkASSERT(fPixmaps.plane(i).dimensions() == pixmaps.plane(i).dimensions());
122 SkASSERT(fPixmaps.plane(i).rowBytes() == pixmaps.plane(i).rowBytes());
123 fPixmaps.plane(i).readPixels(pixmaps.plane(i));
124 }
125 return true;
126 }
127
128private:
129 SkYUVAPixmaps fPixmaps;
130 SkBitmap fFlattened;
131};
132
133} // anonymous namespace
Michael Ludwigd9958f82019-03-21 13:08:36 -0400134
135namespace sk_gpu_test {
136
Brian Salomon7db71392020-10-16 10:05:21 -0400137std::unique_ptr<LazyYUVImage> LazyYUVImage::Make(sk_sp<SkData> data,
138 GrMipmapped mipmapped,
139 sk_sp<SkColorSpace> cs) {
Michael Ludwigd9958f82019-03-21 13:08:36 -0400140 std::unique_ptr<LazyYUVImage> image(new LazyYUVImage());
Brian Salomon7db71392020-10-16 10:05:21 -0400141 if (image->reset(std::move(data), mipmapped, std::move(cs))) {
Michael Ludwigd9958f82019-03-21 13:08:36 -0400142 return image;
143 } else {
144 return nullptr;
145 }
146}
147
Brian Salomon7db71392020-10-16 10:05:21 -0400148std::unique_ptr<LazyYUVImage> LazyYUVImage::Make(SkYUVAPixmaps pixmaps,
149 GrMipmapped mipmapped,
150 sk_sp<SkColorSpace> cs) {
151 std::unique_ptr<LazyYUVImage> image(new LazyYUVImage());
152 if (image->reset(std::move(pixmaps), mipmapped, std::move(cs))) {
153 return image;
Michael Ludwigd9958f82019-03-21 13:08:36 -0400154 } else {
155 return nullptr;
156 }
157}
158
Brian Salomon7db71392020-10-16 10:05:21 -0400159sk_sp<SkImage> LazyYUVImage::refImage(GrRecordingContext* rContext, Type type) {
160 if (this->ensureYUVImage(rContext, type)) {
161 size_t idx = static_cast<size_t>(type);
Brian Salomon6c11ba22020-10-16 11:11:40 -0400162 SkASSERT(idx < SK_ARRAY_COUNT(fYUVImage));
Brian Salomon7db71392020-10-16 10:05:21 -0400163 return fYUVImage[idx];
Michael Ludwigd9958f82019-03-21 13:08:36 -0400164 } else {
165 return nullptr;
166 }
167}
168
Brian Salomon7db71392020-10-16 10:05:21 -0400169bool LazyYUVImage::reset(sk_sp<SkData> data, GrMipmapped mipmapped, sk_sp<SkColorSpace> cs) {
Brian Salomon6db78b82020-07-31 08:57:48 -0400170 fMipmapped = mipmapped;
Michael Ludwigd9958f82019-03-21 13:08:36 -0400171 auto codec = SkCodecImageGenerator::MakeFromEncodedCodec(data);
172 if (!codec) {
173 return false;
174 }
175
Brian Salomonbe0e42c2020-08-27 11:00:04 -0400176 SkYUVAPixmapInfo yuvaPixmapInfo;
Brian Salomon59c60b02020-09-01 15:01:15 -0400177 if (!codec->queryYUVAInfo(SkYUVAPixmapInfo::SupportedDataTypes::All(), &yuvaPixmapInfo)) {
Brian Salomon87d42e52020-08-24 09:18:16 -0400178 return false;
179 }
Brian Salomonbe0e42c2020-08-27 11:00:04 -0400180 fPixmaps = SkYUVAPixmaps::Allocate(yuvaPixmapInfo);
Brian Salomon87d42e52020-08-24 09:18:16 -0400181 if (!fPixmaps.isValid()) {
Michael Ludwigd9958f82019-03-21 13:08:36 -0400182 return false;
183 }
184
Brian Salomonbe0e42c2020-08-27 11:00:04 -0400185 if (!codec->getYUVAPlanes(fPixmaps)) {
Michael Ludwigd9958f82019-03-21 13:08:36 -0400186 return false;
187 }
188
Brian Salomon7db71392020-10-16 10:05:21 -0400189 fColorSpace = std::move(cs);
190
Brian Salomondb0288d2020-10-15 10:31:43 -0400191 // The SkPixmap data is fully configured now for MakeFromYUVAPixmaps once we get a GrContext
192 return true;
193}
194
Brian Salomon7db71392020-10-16 10:05:21 -0400195bool LazyYUVImage::reset(SkYUVAPixmaps pixmaps, GrMipmapped mipmapped, sk_sp<SkColorSpace> cs) {
196 if (!pixmaps.isValid()) {
197 return false;
Brian Salomon839fb222020-10-16 13:30:54 +0000198 }
Brian Salomon7db71392020-10-16 10:05:21 -0400199 fMipmapped = mipmapped;
200 if (pixmaps.ownsStorage()) {
201 fPixmaps = std::move(pixmaps);
202 } else {
203 fPixmaps = SkYUVAPixmaps::MakeCopy(std::move(pixmaps));
204 }
205 fColorSpace = std::move(cs);
206 // The SkPixmap data is fully configured now for MakeFromYUVAPixmaps once we get a GrContext
207 return true;
208}
209
210bool LazyYUVImage::ensureYUVImage(GrRecordingContext* rContext, Type type) {
211 size_t idx = static_cast<size_t>(type);
Brian Salomon6c11ba22020-10-16 11:11:40 -0400212 SkASSERT(idx < SK_ARRAY_COUNT(fYUVImage));
Brian Salomon7db71392020-10-16 10:05:21 -0400213 if (fYUVImage[idx] && fYUVImage[idx]->isValid(rContext)) {
214 return true; // Have already made a YUV image valid for this context.
Michael Ludwigd9958f82019-03-21 13:08:36 -0400215 }
Brian Salomonefb5f072020-07-28 21:06:43 -0400216 // Try to make a new YUV image for this context.
Brian Salomon7db71392020-10-16 10:05:21 -0400217 switch (type) {
218 case Type::kFromPixmaps:
219 if (!rContext || rContext->abandoned()) {
220 return false;
221 }
222 fYUVImage[idx] = SkImage::MakeFromYUVAPixmaps(rContext,
223 fPixmaps,
224 fMipmapped,
225 /*limit to max tex size*/ false,
226 fColorSpace);
227 break;
228 case Type::kFromGenerator: {
229 // Make sure the generator has ownership of its backing planes.
230 auto generator = std::make_unique<Generator>(fPixmaps, fColorSpace);
231 fYUVImage[idx] = SkImage::MakeFromGenerator(std::move(generator));
232 break;
Brian Salomon839fb222020-10-16 13:30:54 +0000233 }
Brian Salomon7db71392020-10-16 10:05:21 -0400234 case Type::kFromTextures:
235 if (!rContext || rContext->abandoned()) {
236 return false;
237 }
Brian Salomon694ff172020-11-04 16:54:28 -0500238 if (fMipmapped == GrMipmapped::kYes) {
239 // If this becomes necessary we should invoke SkMipmapBuilder here to make mip
240 // maps from our src data (and then pass a pixmaps array to initialize the planar
241 // textures.
242 return false;
243 }
Brian Salomon7db71392020-10-16 10:05:21 -0400244 if (auto direct = rContext->asDirectContext()) {
245 sk_sp<sk_gpu_test::ManagedBackendTexture> mbets[SkYUVAInfo::kMaxPlanes];
246 GrBackendTexture textures[SkYUVAInfo::kMaxPlanes];
Brian Salomon7db71392020-10-16 10:05:21 -0400247 for (int i = 0; i < fPixmaps.numPlanes(); ++i) {
248 mbets[i] = sk_gpu_test::ManagedBackendTexture::MakeWithData(
Brian Salomonb5f880a2020-12-07 11:30:16 -0500249 direct,
250 fPixmaps.plane(i),
251 kTopLeft_GrSurfaceOrigin,
252 GrRenderable::kNo,
253 GrProtected::kNo);
Brian Salomon7db71392020-10-16 10:05:21 -0400254 if (mbets[i]) {
255 textures[i] = mbets[i]->texture();
Brian Salomon7db71392020-10-16 10:05:21 -0400256 } else {
257 return false;
258 }
259 }
Brian Salomonc1a249d2020-10-19 10:55:45 -0400260 GrYUVABackendTextures yuvaTextures(fPixmaps.yuvaInfo(),
261 textures,
262 kTopLeft_GrSurfaceOrigin);
263 if (!yuvaTextures.isValid()) {
Brian Salomonbe8004d2020-10-16 22:32:35 +0000264 return false;
265 }
Brian Salomon8670c982020-12-08 16:39:32 -0500266 void* planeRelContext =
267 sk_gpu_test::ManagedBackendTexture::MakeYUVAReleaseContext(mbets);
268 fYUVImage[idx] = SkImage::MakeFromYUVATextures(
269 direct,
270 yuvaTextures,
271 fColorSpace,
272 sk_gpu_test::ManagedBackendTexture::ReleaseProc,
273 planeRelContext);
Brian Salomon7db71392020-10-16 10:05:21 -0400274 }
Brian Salomon839fb222020-10-16 13:30:54 +0000275 }
Brian Salomon7db71392020-10-16 10:05:21 -0400276 return fYUVImage[idx] != nullptr;
Brian Salomon839fb222020-10-16 13:30:54 +0000277}
Michael Ludwigd9958f82019-03-21 13:08:36 -0400278} // namespace sk_gpu_test