blob: f525092f298020a7a01159ac6b645f493d8fa9a1 [file] [log] [blame]
sugoi24dcac22014-07-07 15:09:48 -07001/*
2 * Copyright 2014 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
8// This test only works with the GPU backend.
9
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "gm/gm.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "include/core/SkBitmap.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040012#include "include/core/SkBlendMode.h"
13#include "include/core/SkColor.h"
14#include "include/core/SkImage.h"
15#include "include/core/SkImageInfo.h"
16#include "include/core/SkMatrix.h"
17#include "include/core/SkRect.h"
18#include "include/core/SkRefCnt.h"
19#include "include/core/SkScalar.h"
20#include "include/core/SkSize.h"
21#include "include/core/SkString.h"
22#include "include/core/SkTypes.h"
23#include "include/core/SkYUVAIndex.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050024#include "include/gpu/GrContext.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040025#include "include/gpu/GrSamplerState.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050026#include "include/private/GrTextureProxy.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040027#include "include/private/GrTypesPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050028#include "src/gpu/GrClip.h"
29#include "src/gpu/GrContextPriv.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040030#include "src/gpu/GrFragmentProcessor.h"
31#include "src/gpu/GrPaint.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050032#include "src/gpu/GrProxyProvider.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040033#include "src/gpu/GrRenderTargetContext.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050034#include "src/gpu/GrRenderTargetContextPriv.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040035#include "src/gpu/effects/GrPorterDuffXferProcessor.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050036#include "src/gpu/effects/GrYUVtoRGBEffect.h"
37#include "src/gpu/ops/GrDrawOp.h"
38#include "src/gpu/ops/GrFillRectOp.h"
sugoi24dcac22014-07-07 15:09:48 -070039
Ben Wagner7fde8e12019-05-01 17:28:53 -040040#include <memory>
41#include <utility>
42
43class SkCanvas;
44
sugoi4ccce7e2015-02-13 13:57:09 -080045#define YSIZE 8
46#define USIZE 4
47#define VSIZE 4
48
sugoi24dcac22014-07-07 15:09:48 -070049namespace skiagm {
50/**
51 * This GM directly exercises GrYUVtoRGBEffect.
52 */
Chris Dalton3a778372019-02-07 15:23:36 -070053class YUVtoRGBEffect : public GpuGM {
sugoi24dcac22014-07-07 15:09:48 -070054public:
55 YUVtoRGBEffect() {
56 this->setBGColor(0xFFFFFFFF);
57 }
58
59protected:
mtklein36352bf2015-03-25 18:17:31 -070060 SkString onShortName() override {
sugoi24dcac22014-07-07 15:09:48 -070061 return SkString("yuv_to_rgb_effect");
62 }
63
mtklein36352bf2015-03-25 18:17:31 -070064 SkISize onISize() override {
Robert Phillips0a22ba82019-03-06 12:36:47 -050065 int numRows = kLastEnum_SkYUVColorSpace + 1;
66 return SkISize::Make(238, kDrawPad + numRows * kColorSpaceOffset);
sugoi24dcac22014-07-07 15:09:48 -070067 }
68
mtklein36352bf2015-03-25 18:17:31 -070069 void onOnceBeforeDraw() override {
Brian Osman2700abc2018-09-12 10:19:41 -040070 SkBitmap bmp[3];
sugoi4ccce7e2015-02-13 13:57:09 -080071 SkImageInfo yinfo = SkImageInfo::MakeA8(YSIZE, YSIZE);
Brian Osman2700abc2018-09-12 10:19:41 -040072 bmp[0].allocPixels(yinfo);
sugoi4ccce7e2015-02-13 13:57:09 -080073 SkImageInfo uinfo = SkImageInfo::MakeA8(USIZE, USIZE);
Brian Osman2700abc2018-09-12 10:19:41 -040074 bmp[1].allocPixels(uinfo);
sugoi4ccce7e2015-02-13 13:57:09 -080075 SkImageInfo vinfo = SkImageInfo::MakeA8(VSIZE, VSIZE);
Brian Osman2700abc2018-09-12 10:19:41 -040076 bmp[2].allocPixels(vinfo);
sugoi24dcac22014-07-07 15:09:48 -070077 unsigned char* pixels[3];
78 for (int i = 0; i < 3; ++i) {
Brian Osman2700abc2018-09-12 10:19:41 -040079 pixels[i] = (unsigned char*)bmp[i].getPixels();
sugoi24dcac22014-07-07 15:09:48 -070080 }
81 int color[] = {0, 85, 170};
82 const int limit[] = {255, 0, 255};
83 const int invl[] = {0, 255, 0};
84 const int inc[] = {1, -1, 1};
sugoi4ccce7e2015-02-13 13:57:09 -080085 for (int i = 0; i < 3; ++i) {
Brian Osman2700abc2018-09-12 10:19:41 -040086 const size_t nbBytes = bmp[i].rowBytes() * bmp[i].height();
sugoi4ccce7e2015-02-13 13:57:09 -080087 for (size_t j = 0; j < nbBytes; ++j) {
sugoi24dcac22014-07-07 15:09:48 -070088 pixels[i][j] = (unsigned char)color[i];
89 color[i] = (color[i] == limit[i]) ? invl[i] : color[i] + inc[i];
90 }
91 }
Brian Osman2700abc2018-09-12 10:19:41 -040092 for (int i = 0; i < 3; ++i) {
93 fImage[i] = SkImage::MakeFromBitmap(bmp[i]);
94 }
sugoi24dcac22014-07-07 15:09:48 -070095 }
96
Chris Dalton50e24d72019-02-07 16:20:09 -070097 DrawResult onDraw(GrContext* context, GrRenderTargetContext* renderTargetContext,
98 SkCanvas* canvas, SkString* errorMsg) override {
Robert Phillips9da87e02019-02-04 13:26:26 -050099 GrProxyProvider* proxyProvider = context->priv().proxyProvider();
Robert Phillips94ade752018-10-09 12:32:31 -0400100 sk_sp<GrTextureProxy> proxies[3];
bsalomonbcf0a522014-10-08 08:40:09 -0700101
Brian Salomon2a4f9832018-03-03 22:43:43 -0500102 for (int i = 0; i < 3; ++i) {
Robert Phillips94ade752018-10-09 12:32:31 -0400103 proxies[i] = proxyProvider->createTextureProxy(fImage[i], kNone_GrSurfaceFlags, 1,
104 SkBudgeted::kYes, SkBackingFit::kExact);
105 if (!proxies[i]) {
Chris Dalton50e24d72019-02-07 16:20:09 -0700106 *errorMsg = "Failed to create proxy";
107 return DrawResult::kFail;
Robert Phillipsbc7a4fb2017-01-23 15:30:35 -0500108 }
sugoi24dcac22014-07-07 15:09:48 -0700109 }
110
Robert Phillipsbc7a4fb2017-01-23 15:30:35 -0500111 for (int space = kJPEG_SkYUVColorSpace; space <= kLastEnum_SkYUVColorSpace; ++space) {
Brian Osman2700abc2018-09-12 10:19:41 -0400112 SkRect renderRect = SkRect::MakeWH(SkIntToScalar(fImage[0]->width()),
113 SkIntToScalar(fImage[0]->height()));
bsalomonbcf0a522014-10-08 08:40:09 -0700114 renderRect.outset(kDrawPad, kDrawPad);
sugoi24dcac22014-07-07 15:09:48 -0700115
bsalomonbcf0a522014-10-08 08:40:09 -0700116 SkScalar y = kDrawPad + kTestPad + space * kColorSpaceOffset;
117 SkScalar x = kDrawPad + kTestPad;
sugoi24dcac22014-07-07 15:09:48 -0700118
bsalomonbcf0a522014-10-08 08:40:09 -0700119 const int indices[6][3] = {{0, 1, 2}, {0, 2, 1}, {1, 0, 2},
120 {1, 2, 0}, {2, 0, 1}, {2, 1, 0}};
sugoi24dcac22014-07-07 15:09:48 -0700121
bsalomonbcf0a522014-10-08 08:40:09 -0700122 for (int i = 0; i < 6; ++i) {
Robert Phillips94ade752018-10-09 12:32:31 -0400123 SkYUVAIndex yuvaIndices[4] = {
Robert Phillips6ba8c832018-10-10 11:43:01 -0400124 { indices[i][0], SkColorChannel::kR },
125 { indices[i][1], SkColorChannel::kR },
126 { indices[i][2], SkColorChannel::kR },
Robert Phillips94ade752018-10-09 12:32:31 -0400127 { -1, SkColorChannel::kA }
128 };
129
Brian Salomonaff329b2017-08-11 09:40:37 -0400130 std::unique_ptr<GrFragmentProcessor> fp(
Robert Phillips94ade752018-10-09 12:32:31 -0400131 GrYUVtoRGBEffect::Make(proxies, yuvaIndices,
Jim Van Verth30e0d7f2018-11-02 13:36:42 -0400132 static_cast<SkYUVColorSpace>(space),
133 GrSamplerState::Filter::kNearest));
bsalomonbcf0a522014-10-08 08:40:09 -0700134 if (fp) {
Brian Salomon82f44312017-01-11 13:42:54 -0500135 GrPaint grPaint;
136 grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
137 grPaint.addColorFragmentProcessor(std::move(fp));
bsalomonbcf0a522014-10-08 08:40:09 -0700138 SkMatrix viewMatrix;
139 viewMatrix.setTranslate(x, y);
Brian Salomonac70f842017-05-08 10:43:33 -0400140 renderTargetContext->priv().testingOnly_addDrawOp(
Michael Ludwig72ab3462018-12-10 12:43:36 -0500141 GrFillRectOp::Make(context, std::move(grPaint), GrAAType::kNone,
142 viewMatrix, renderRect));
bsalomonbcf0a522014-10-08 08:40:09 -0700143 }
144 x += renderRect.width() + kTestPad;
145 }
sugoi24dcac22014-07-07 15:09:48 -0700146 }
Chris Dalton50e24d72019-02-07 16:20:09 -0700147 return DrawResult::kOk;
bsalomonbcf0a522014-10-08 08:40:09 -0700148 }
sugoi24dcac22014-07-07 15:09:48 -0700149
150private:
Brian Osman2700abc2018-09-12 10:19:41 -0400151 sk_sp<SkImage> fImage[3];
sugoi24dcac22014-07-07 15:09:48 -0700152
Robert Phillips0a22ba82019-03-06 12:36:47 -0500153 static constexpr SkScalar kDrawPad = 10.f;
154 static constexpr SkScalar kTestPad = 10.f;
155 static constexpr SkScalar kColorSpaceOffset = 36.f;
156
sugoi24dcac22014-07-07 15:09:48 -0700157 typedef GM INHERITED;
158};
159
halcanary385fe4d2015-08-26 13:07:48 -0700160DEF_GM(return new YUVtoRGBEffect;)
jbaumanb445a572016-06-09 13:24:48 -0700161
162//////////////////////////////////////////////////////////////////////////////
163
Chris Dalton3a778372019-02-07 15:23:36 -0700164class YUVNV12toRGBEffect : public GpuGM {
jbaumanb445a572016-06-09 13:24:48 -0700165public:
166 YUVNV12toRGBEffect() {
167 this->setBGColor(0xFFFFFFFF);
168 }
169
170protected:
171 SkString onShortName() override {
172 return SkString("yuv_nv12_to_rgb_effect");
173 }
174
175 SkISize onISize() override {
Robert Phillips0a22ba82019-03-06 12:36:47 -0500176 int numRows = kLastEnum_SkYUVColorSpace + 1;
177 return SkISize::Make(48, kDrawPad + numRows * kColorSpaceOffset);
jbaumanb445a572016-06-09 13:24:48 -0700178 }
179
180 void onOnceBeforeDraw() override {
Brian Osman2700abc2018-09-12 10:19:41 -0400181 SkBitmap bmp[2];
jbaumanb445a572016-06-09 13:24:48 -0700182 SkImageInfo yinfo = SkImageInfo::MakeA8(YSIZE, YSIZE);
Brian Osman2700abc2018-09-12 10:19:41 -0400183 bmp[0].allocPixels(yinfo);
jbaumanb445a572016-06-09 13:24:48 -0700184 SkImageInfo uvinfo = SkImageInfo::MakeN32Premul(USIZE, USIZE);
Brian Osman2700abc2018-09-12 10:19:41 -0400185 bmp[1].allocPixels(uvinfo);
jbaumanb445a572016-06-09 13:24:48 -0700186 int color[] = {0, 85, 170};
187 const int limit[] = {255, 0, 255};
188 const int invl[] = {0, 255, 0};
189 const int inc[] = {1, -1, 1};
190
191 {
Brian Osman2700abc2018-09-12 10:19:41 -0400192 unsigned char* pixels = (unsigned char*)bmp[0].getPixels();
193 const size_t nbBytes = bmp[0].rowBytes() * bmp[0].height();
jbaumanb445a572016-06-09 13:24:48 -0700194 for (size_t j = 0; j < nbBytes; ++j) {
195 pixels[j] = (unsigned char)color[0];
196 color[0] = (color[0] == limit[0]) ? invl[0] : color[0] + inc[0];
197 }
198 }
199
200 {
Brian Osman2700abc2018-09-12 10:19:41 -0400201 for (int y = 0; y < bmp[1].height(); ++y) {
202 uint32_t* pixels = bmp[1].getAddr32(0, y);
203 for (int j = 0; j < bmp[1].width(); ++j) {
jbaumanb445a572016-06-09 13:24:48 -0700204 pixels[j] = SkColorSetARGB(0, color[1], color[2], 0);
205 color[1] = (color[1] == limit[1]) ? invl[1] : color[1] + inc[1];
206 color[2] = (color[2] == limit[2]) ? invl[2] : color[2] + inc[2];
207 }
208 }
209 }
Brian Osman2700abc2018-09-12 10:19:41 -0400210
211 for (int i = 0; i < 2; ++i) {
212 fImage[i] = SkImage::MakeFromBitmap(bmp[i]);
213 }
jbaumanb445a572016-06-09 13:24:48 -0700214 }
215
Chris Dalton50e24d72019-02-07 16:20:09 -0700216 DrawResult onDraw(GrContext* context, GrRenderTargetContext* renderTargetContext,
217 SkCanvas* canvas, SkString* errorMsg) override {
Robert Phillips9da87e02019-02-04 13:26:26 -0500218 GrProxyProvider* proxyProvider = context->priv().proxyProvider();
Robert Phillips94ade752018-10-09 12:32:31 -0400219 sk_sp<GrTextureProxy> proxies[2];
jbaumanb445a572016-06-09 13:24:48 -0700220
Robert Phillips94ade752018-10-09 12:32:31 -0400221 for (int i = 0; i < 2; ++i) {
222 proxies[i] = proxyProvider->createTextureProxy(fImage[i], kNone_GrSurfaceFlags, 1,
223 SkBudgeted::kYes, SkBackingFit::kExact);
224 if (!proxies[i]) {
Chris Dalton50e24d72019-02-07 16:20:09 -0700225 *errorMsg = "Failed to create proxy";
226 return DrawResult::kFail;
Robert Phillipsbc7a4fb2017-01-23 15:30:35 -0500227 }
jbaumanb445a572016-06-09 13:24:48 -0700228 }
229
Robert Phillips94ade752018-10-09 12:32:31 -0400230 SkYUVAIndex yuvaIndices[4] = {
Robert Phillips6ba8c832018-10-10 11:43:01 -0400231 { 0, SkColorChannel::kR },
Robert Phillips94ade752018-10-09 12:32:31 -0400232 { 1, SkColorChannel::kR },
233 { 1, SkColorChannel::kG },
234 { -1, SkColorChannel::kA }
235 };
236
jbaumanb445a572016-06-09 13:24:48 -0700237 for (int space = kJPEG_SkYUVColorSpace; space <= kLastEnum_SkYUVColorSpace; ++space) {
Brian Osman2700abc2018-09-12 10:19:41 -0400238 SkRect renderRect = SkRect::MakeWH(SkIntToScalar(fImage[0]->width()),
239 SkIntToScalar(fImage[0]->height()));
jbaumanb445a572016-06-09 13:24:48 -0700240 renderRect.outset(kDrawPad, kDrawPad);
241
242 SkScalar y = kDrawPad + kTestPad + space * kColorSpaceOffset;
243 SkScalar x = kDrawPad + kTestPad;
244
robertphillips28a838e2016-06-23 14:07:00 -0700245 GrPaint grPaint;
Brian Salomona1633922017-01-09 11:46:10 -0500246 grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
Robert Phillips94ade752018-10-09 12:32:31 -0400247 auto fp = GrYUVtoRGBEffect::Make(proxies, yuvaIndices,
Jim Van Verth30e0d7f2018-11-02 13:36:42 -0400248 static_cast<SkYUVColorSpace>(space),
249 GrSamplerState::Filter::kNearest);
jbaumanb445a572016-06-09 13:24:48 -0700250 if (fp) {
251 SkMatrix viewMatrix;
252 viewMatrix.setTranslate(x, y);
Brian Salomonaff329b2017-08-11 09:40:37 -0400253 grPaint.addColorFragmentProcessor(std::move(fp));
Michael Ludwig72ab3462018-12-10 12:43:36 -0500254 std::unique_ptr<GrDrawOp> op(GrFillRectOp::Make(context, std::move(grPaint),
255 GrAAType::kNone, viewMatrix, renderRect));
Robert Phillips09dfc472017-09-13 15:25:47 -0400256 renderTargetContext->priv().testingOnly_addDrawOp(std::move(op));
jbaumanb445a572016-06-09 13:24:48 -0700257 }
258 }
Chris Dalton50e24d72019-02-07 16:20:09 -0700259 return DrawResult::kOk;
jbaumanb445a572016-06-09 13:24:48 -0700260 }
261
262private:
Brian Osman2700abc2018-09-12 10:19:41 -0400263 sk_sp<SkImage> fImage[2];
jbaumanb445a572016-06-09 13:24:48 -0700264
Robert Phillips0a22ba82019-03-06 12:36:47 -0500265 static constexpr SkScalar kDrawPad = 10.f;
266 static constexpr SkScalar kTestPad = 10.f;
267 static constexpr SkScalar kColorSpaceOffset = 36.f;
268
jbaumanb445a572016-06-09 13:24:48 -0700269 typedef GM INHERITED;
270};
271
272DEF_GM(return new YUVNV12toRGBEffect;)
Michael Ludwiga6a84002019-04-12 15:03:02 -0400273
274//////////////////////////////////////////////////////////////////////////////
275
276// This GM tests domain clamping on YUV multiplanar images where the U and V
277// planes have different resolution from Y. See skbug:8959
278
279class YUVtoRGBDomainEffect : public GpuGM {
280public:
281 YUVtoRGBDomainEffect() {
282 this->setBGColor(0xFFFFFFFF);
283 }
284
285protected:
286 SkString onShortName() override {
287 return SkString("yuv_to_rgb_domain_effect");
288 }
289
290 SkISize onISize() override {
291 return SkISize::Make((YSIZE + kTestPad) * 3 + kDrawPad, (YSIZE + kTestPad) * 2 + kDrawPad);
292 }
293
294 void onOnceBeforeDraw() override {
295 SkBitmap bmp[3];
296 SkImageInfo yinfo = SkImageInfo::MakeA8(YSIZE, YSIZE);
297 bmp[0].allocPixels(yinfo);
298 SkImageInfo uinfo = SkImageInfo::MakeA8(USIZE, USIZE);
299 bmp[1].allocPixels(uinfo);
300 SkImageInfo vinfo = SkImageInfo::MakeA8(VSIZE, VSIZE);
301 bmp[2].allocPixels(vinfo);
302
303 int innerColor[] = {149, 43, 21};
304 int outerColor[] = {128, 128, 128};
305 for (int i = 0; i < 3; ++i) {
306 bmp[i].eraseColor(SkColorSetARGB(outerColor[i], 0, 0, 0));
307 SkIRect innerRect = i == 0 ? SkIRect::MakeLTRB(2, 2, 6, 6) : SkIRect::MakeLTRB(1, 1, 3, 3);
308 bmp[i].erase(SkColorSetARGB(innerColor[i], 0, 0, 0), innerRect);
309 fImage[i] = SkImage::MakeFromBitmap(bmp[i]);
310 }
311 }
312
313 DrawResult onDraw(GrContext* context, GrRenderTargetContext* renderTargetContext,
314 SkCanvas* canvas, SkString* errorMsg) override {
315 GrProxyProvider* proxyProvider = context->priv().proxyProvider();
316 sk_sp<GrTextureProxy> proxies[3];
317
318 for (int i = 0; i < 3; ++i) {
319 proxies[i] = proxyProvider->createTextureProxy(fImage[i], kNone_GrSurfaceFlags, 1,
320 SkBudgeted::kYes, SkBackingFit::kExact);
321 if (!proxies[i]) {
322 *errorMsg = "Failed to create proxy";
323 return DrawResult::kFail;
324 }
325 }
326
327 // Draw a 2x2 grid of the YUV images.
328 // Rows = kNearest, kBilerp, Cols = No clamp, clamp
329 static const GrSamplerState::Filter kFilters[] = {
330 GrSamplerState::Filter::kNearest, GrSamplerState::Filter::kBilerp };
331 static const SkRect kGreenRect = SkRect::MakeLTRB(2.f, 2.f, 6.f, 6.f);
332
333 SkYUVAIndex yuvaIndices[4] = {
334 { SkYUVAIndex::kY_Index, SkColorChannel::kR },
335 { SkYUVAIndex::kU_Index, SkColorChannel::kR },
336 { SkYUVAIndex::kV_Index, SkColorChannel::kR },
337 { -1, SkColorChannel::kA }
338 };
339 SkRect rect = SkRect::MakeWH(YSIZE, YSIZE);
340
341 SkScalar y = kDrawPad + kTestPad;
342 for (uint32_t i = 0; i < SK_ARRAY_COUNT(kFilters); ++i) {
343 SkScalar x = kDrawPad + kTestPad;
344
345 for (uint32_t j = 0; j < 2; ++j) {
346 SkMatrix ctm = SkMatrix::MakeTrans(x, y);
347 ctm.postScale(10.f, 10.f);
348
349 SkRect domain = kGreenRect;
350 if (kFilters[i] == GrSamplerState::Filter::kNearest) {
351 // Make a very small inset for nearest-neighbor filtering so that 0.5px
352 // centers don't round out beyond the green pixels.
353 domain.inset(0.01f, 0.01f);
354 }
355
356 const SkRect* domainPtr = j > 0 ? &domain : nullptr;
357 std::unique_ptr<GrFragmentProcessor> fp(GrYUVtoRGBEffect::Make(proxies, yuvaIndices,
358 kJPEG_SkYUVColorSpace, kFilters[i], SkMatrix::I(), domainPtr));
359 if (fp) {
360 GrPaint grPaint;
361 grPaint.addColorFragmentProcessor(std::move(fp));
362 renderTargetContext->drawRect(
363 GrNoClip(), std::move(grPaint), GrAA::kYes, ctm, rect);
364 }
365 x += rect.width() + kTestPad;
366 }
367
368 y += rect.height() + kTestPad;
369 }
370
371 return DrawResult::kOk;
372 }
373
374private:
375 sk_sp<SkImage> fImage[3];
376
377 static constexpr SkScalar kDrawPad = 10.f;
378 static constexpr SkScalar kTestPad = 10.f;
379
380 typedef GM INHERITED;
381};
382
383DEF_GM(return new YUVtoRGBDomainEffect;)
sugoi24dcac22014-07-07 15:09:48 -0700384}