blob: d5f7ac3dfc8f5c0f3e7184ec103630b5b41e2250 [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"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050013#include "src/codec/SkCodecImageGenerator.h"
Brian Salomon7db71392020-10-16 10:05:21 -040014#include "src/core/SkYUVMath.h"
Adlai Hollera0693042020-10-14 11:23:11 -040015#include "src/gpu/GrDirectContextPriv.h"
Brian Salomonefb5f072020-07-28 21:06:43 -040016#include "src/gpu/GrRecordingContextPriv.h"
Brian Salomon7db71392020-10-16 10:05:21 -040017#include "tools/gpu/ManagedBackendTexture.h"
18
19namespace {
20
21static SkPMColor convert_yuva_to_rgba(const float mtx[20], uint8_t yuva[4]) {
22 uint8_t y = yuva[0];
23 uint8_t u = yuva[1];
24 uint8_t v = yuva[2];
25 uint8_t a = yuva[3];
26
27 uint8_t r = SkTPin(SkScalarRoundToInt(mtx[ 0]*y + mtx[ 1]*u + mtx[ 2]*v + mtx[ 4]*255), 0, 255);
28 uint8_t g = SkTPin(SkScalarRoundToInt(mtx[ 5]*y + mtx[ 6]*u + mtx[ 7]*v + mtx[ 9]*255), 0, 255);
29 uint8_t b = SkTPin(SkScalarRoundToInt(mtx[10]*y + mtx[11]*u + mtx[12]*v + mtx[14]*255), 0, 255);
30
31 return SkPremultiplyARGBInline(a, r, g, b);
32}
33
34static uint8_t look_up(float x1, float y1, const SkPixmap& pmap, SkColorChannel channel) {
35 SkASSERT(x1 > 0 && x1 < 1.0f);
36 SkASSERT(y1 > 0 && y1 < 1.0f);
37 int x = SkScalarFloorToInt(x1 * pmap.width());
38 int y = SkScalarFloorToInt(y1 * pmap.height());
39
40 auto ii = pmap.info().makeColorType(kRGBA_8888_SkColorType).makeWH(1, 1);
41 uint32_t pixel;
42 SkAssertResult(pmap.readPixels(ii, &pixel, sizeof(pixel), x, y));
43 int shift = static_cast<int>(channel) * 8;
44 return static_cast<uint8_t>((pixel >> shift) & 0xff);
45}
46
47class Generator : public SkImageGenerator {
48public:
49 Generator(SkYUVAPixmaps pixmaps, sk_sp<SkColorSpace> cs)
50 : SkImageGenerator(SkImageInfo::Make(pixmaps.yuvaInfo().dimensions(),
51 kN32_SkColorType,
52 kPremul_SkAlphaType,
53 std::move(cs)))
54 , fPixmaps(std::move(pixmaps)) {}
55
56protected:
57 bool onGetPixels(const SkImageInfo& info,
58 void* pixels,
59 size_t rowBytes,
60 const Options&) override {
61 if (kUnknown_SkColorType == fFlattened.colorType()) {
62 fFlattened.allocPixels(info);
63 SkASSERT(info == this->getInfo());
64
65 float mtx[20];
66 SkColorMatrix_YUV2RGB(fPixmaps.yuvaInfo().yuvColorSpace(), mtx);
67 SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount];
68 SkAssertResult(fPixmaps.toYUVAIndices(yuvaIndices));
69
70 for (int y = 0; y < info.height(); ++y) {
71 for (int x = 0; x < info.width(); ++x) {
72 float x1 = (x + 0.5f) / info.width();
73 float y1 = (y + 0.5f) / info.height();
74
75 uint8_t yuva[4] = {0, 0, 0, 255};
76
77 for (auto c : {SkYUVAIndex::kY_Index,
78 SkYUVAIndex::kU_Index,
79 SkYUVAIndex::kV_Index}) {
80 const auto& pmap = fPixmaps.plane(yuvaIndices[c].fIndex);
81 yuva[c] = look_up(x1, y1, pmap, yuvaIndices[c].fChannel);
82 }
83 if (yuvaIndices[SkYUVAIndex::kA_Index].fIndex >= 0) {
84 const auto& pmap =
85 fPixmaps.plane(yuvaIndices[SkYUVAIndex::kA_Index].fIndex);
86 yuva[3] =
87 look_up(x1, y1, pmap, yuvaIndices[SkYUVAIndex::kA_Index].fChannel);
88 }
89
90 // Making premul here.
91 *fFlattened.getAddr32(x, y) = convert_yuva_to_rgba(mtx, yuva);
92 }
93 }
94 }
95
96 return fFlattened.readPixels(info, pixels, rowBytes, 0, 0);
97 }
98
99 bool onQueryYUVAInfo(const SkYUVAPixmapInfo::SupportedDataTypes& types,
100 SkYUVAPixmapInfo* info) const override {
101 *info = fPixmaps.pixmapsInfo();
102 return info->isValid();
103 }
104
105 bool onGetYUVAPlanes(const SkYUVAPixmaps& pixmaps) override {
106 SkASSERT(pixmaps.yuvaInfo() == fPixmaps.yuvaInfo());
107 for (int i = 0; i < pixmaps.numPlanes(); ++i) {
108 SkASSERT(fPixmaps.plane(i).colorType() == pixmaps.plane(i).colorType());
109 SkASSERT(fPixmaps.plane(i).dimensions() == pixmaps.plane(i).dimensions());
110 SkASSERT(fPixmaps.plane(i).rowBytes() == pixmaps.plane(i).rowBytes());
111 fPixmaps.plane(i).readPixels(pixmaps.plane(i));
112 }
113 return true;
114 }
115
116private:
117 SkYUVAPixmaps fPixmaps;
118 SkBitmap fFlattened;
119};
120
121} // anonymous namespace
Michael Ludwigd9958f82019-03-21 13:08:36 -0400122
123namespace sk_gpu_test {
124
Brian Salomon7db71392020-10-16 10:05:21 -0400125std::unique_ptr<LazyYUVImage> LazyYUVImage::Make(sk_sp<SkData> data,
126 GrMipmapped mipmapped,
127 sk_sp<SkColorSpace> cs) {
Michael Ludwigd9958f82019-03-21 13:08:36 -0400128 std::unique_ptr<LazyYUVImage> image(new LazyYUVImage());
Brian Salomon7db71392020-10-16 10:05:21 -0400129 if (image->reset(std::move(data), mipmapped, std::move(cs))) {
Michael Ludwigd9958f82019-03-21 13:08:36 -0400130 return image;
131 } else {
132 return nullptr;
133 }
134}
135
Brian Salomon7db71392020-10-16 10:05:21 -0400136std::unique_ptr<LazyYUVImage> LazyYUVImage::Make(SkYUVAPixmaps pixmaps,
137 GrMipmapped mipmapped,
138 sk_sp<SkColorSpace> cs) {
139 std::unique_ptr<LazyYUVImage> image(new LazyYUVImage());
140 if (image->reset(std::move(pixmaps), mipmapped, std::move(cs))) {
141 return image;
Michael Ludwigd9958f82019-03-21 13:08:36 -0400142 } else {
143 return nullptr;
144 }
145}
146
Brian Salomon7db71392020-10-16 10:05:21 -0400147sk_sp<SkImage> LazyYUVImage::refImage(GrRecordingContext* rContext, Type type) {
148 if (this->ensureYUVImage(rContext, type)) {
149 size_t idx = static_cast<size_t>(type);
Brian Salomon6c11ba22020-10-16 11:11:40 -0400150 SkASSERT(idx < SK_ARRAY_COUNT(fYUVImage));
Brian Salomon7db71392020-10-16 10:05:21 -0400151 return fYUVImage[idx];
Michael Ludwigd9958f82019-03-21 13:08:36 -0400152 } else {
153 return nullptr;
154 }
155}
156
Brian Salomon7db71392020-10-16 10:05:21 -0400157bool LazyYUVImage::reset(sk_sp<SkData> data, GrMipmapped mipmapped, sk_sp<SkColorSpace> cs) {
Brian Salomon6db78b82020-07-31 08:57:48 -0400158 fMipmapped = mipmapped;
Michael Ludwigd9958f82019-03-21 13:08:36 -0400159 auto codec = SkCodecImageGenerator::MakeFromEncodedCodec(data);
160 if (!codec) {
161 return false;
162 }
163
Brian Salomonbe0e42c2020-08-27 11:00:04 -0400164 SkYUVAPixmapInfo yuvaPixmapInfo;
Brian Salomon59c60b02020-09-01 15:01:15 -0400165 if (!codec->queryYUVAInfo(SkYUVAPixmapInfo::SupportedDataTypes::All(), &yuvaPixmapInfo)) {
Brian Salomon87d42e52020-08-24 09:18:16 -0400166 return false;
167 }
Brian Salomonbe0e42c2020-08-27 11:00:04 -0400168 fPixmaps = SkYUVAPixmaps::Allocate(yuvaPixmapInfo);
Brian Salomon87d42e52020-08-24 09:18:16 -0400169 if (!fPixmaps.isValid()) {
Michael Ludwigd9958f82019-03-21 13:08:36 -0400170 return false;
171 }
172
Brian Salomonbe0e42c2020-08-27 11:00:04 -0400173 if (!codec->getYUVAPlanes(fPixmaps)) {
Michael Ludwigd9958f82019-03-21 13:08:36 -0400174 return false;
175 }
176
Brian Salomon7db71392020-10-16 10:05:21 -0400177 fColorSpace = std::move(cs);
178
Brian Salomondb0288d2020-10-15 10:31:43 -0400179 // The SkPixmap data is fully configured now for MakeFromYUVAPixmaps once we get a GrContext
180 return true;
181}
182
Brian Salomon7db71392020-10-16 10:05:21 -0400183bool LazyYUVImage::reset(SkYUVAPixmaps pixmaps, GrMipmapped mipmapped, sk_sp<SkColorSpace> cs) {
184 if (!pixmaps.isValid()) {
185 return false;
Brian Salomon839fb222020-10-16 13:30:54 +0000186 }
Brian Salomon7db71392020-10-16 10:05:21 -0400187 fMipmapped = mipmapped;
188 if (pixmaps.ownsStorage()) {
189 fPixmaps = std::move(pixmaps);
190 } else {
191 fPixmaps = SkYUVAPixmaps::MakeCopy(std::move(pixmaps));
192 }
193 fColorSpace = std::move(cs);
194 // The SkPixmap data is fully configured now for MakeFromYUVAPixmaps once we get a GrContext
195 return true;
196}
197
198bool LazyYUVImage::ensureYUVImage(GrRecordingContext* rContext, Type type) {
199 size_t idx = static_cast<size_t>(type);
Brian Salomon6c11ba22020-10-16 11:11:40 -0400200 SkASSERT(idx < SK_ARRAY_COUNT(fYUVImage));
Brian Salomon7db71392020-10-16 10:05:21 -0400201 if (fYUVImage[idx] && fYUVImage[idx]->isValid(rContext)) {
202 return true; // Have already made a YUV image valid for this context.
Michael Ludwigd9958f82019-03-21 13:08:36 -0400203 }
Brian Salomonefb5f072020-07-28 21:06:43 -0400204 // Try to make a new YUV image for this context.
Brian Salomon7db71392020-10-16 10:05:21 -0400205 switch (type) {
206 case Type::kFromPixmaps:
207 if (!rContext || rContext->abandoned()) {
208 return false;
209 }
210 fYUVImage[idx] = SkImage::MakeFromYUVAPixmaps(rContext,
211 fPixmaps,
212 fMipmapped,
213 /*limit to max tex size*/ false,
214 fColorSpace);
215 break;
216 case Type::kFromGenerator: {
217 // Make sure the generator has ownership of its backing planes.
218 auto generator = std::make_unique<Generator>(fPixmaps, fColorSpace);
219 fYUVImage[idx] = SkImage::MakeFromGenerator(std::move(generator));
220 break;
Brian Salomon839fb222020-10-16 13:30:54 +0000221 }
Brian Salomon7db71392020-10-16 10:05:21 -0400222 case Type::kFromTextures:
223 if (!rContext || rContext->abandoned()) {
224 return false;
225 }
226 if (auto direct = rContext->asDirectContext()) {
227 sk_sp<sk_gpu_test::ManagedBackendTexture> mbets[SkYUVAInfo::kMaxPlanes];
228 GrBackendTexture textures[SkYUVAInfo::kMaxPlanes];
229 uint32_t componentFlags[SkYUVAInfo::kMaxPlanes] = {};
230 for (int i = 0; i < fPixmaps.numPlanes(); ++i) {
231 mbets[i] = sk_gpu_test::ManagedBackendTexture::MakeWithData(
232 direct, fPixmaps.plane(i), GrRenderable::kNo, GrProtected::kNo);
233 if (mbets[i]) {
234 textures[i] = mbets[i]->texture();
235 componentFlags[i] = textures[i].getBackendFormat().channelMask();
236 } else {
237 return false;
238 }
239 }
240 SkYUVAIndex indices[SkYUVAIndex::kIndexCount];
241 if (!fPixmaps.yuvaInfo().toYUVAIndices(componentFlags, indices)) {
242 return false;
243 }
244 void* relContext =
245 sk_gpu_test::ManagedBackendTexture::MakeYUVAReleaseContext(mbets);
246 fYUVImage[idx] = SkImage::MakeFromYUVATextures(
247 direct,
248 fPixmaps.yuvaInfo().yuvColorSpace(),
249 textures,
250 indices,
251 fPixmaps.yuvaInfo().dimensions(),
252 kTopLeft_GrSurfaceOrigin,
253 fColorSpace,
254 sk_gpu_test::ManagedBackendTexture::ReleaseProc,
255 relContext);
256 }
Brian Salomon839fb222020-10-16 13:30:54 +0000257 }
Brian Salomon7db71392020-10-16 10:05:21 -0400258 return fYUVImage[idx] != nullptr;
Brian Salomon839fb222020-10-16 13:30:54 +0000259}
Michael Ludwigd9958f82019-03-21 13:08:36 -0400260} // namespace sk_gpu_test