• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
10 #include "gm/gm.h"
11 #include "include/core/SkBitmap.h"
12 #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"
24 #include "include/gpu/GrContext.h"
25 #include "include/private/GrTypesPriv.h"
26 #include "src/gpu/GrClip.h"
27 #include "src/gpu/GrContextPriv.h"
28 #include "src/gpu/GrFragmentProcessor.h"
29 #include "src/gpu/GrPaint.h"
30 #include "src/gpu/GrProxyProvider.h"
31 #include "src/gpu/GrRenderTargetContext.h"
32 #include "src/gpu/GrRenderTargetContextPriv.h"
33 #include "src/gpu/GrSamplerState.h"
34 #include "src/gpu/GrTextureProxy.h"
35 #include "src/gpu/effects/GrPorterDuffXferProcessor.h"
36 #include "src/gpu/effects/GrYUVtoRGBEffect.h"
37 #include "src/gpu/ops/GrDrawOp.h"
38 #include "src/gpu/ops/GrFillRectOp.h"
39 
40 #include <memory>
41 #include <utility>
42 
43 class SkCanvas;
44 
45 #define YSIZE 8
46 #define USIZE 4
47 #define VSIZE 4
48 
49 namespace skiagm {
50 /**
51  * This GM directly exercises GrYUVtoRGBEffect.
52  */
53 class YUVtoRGBEffect : public GpuGM {
54 public:
YUVtoRGBEffect()55     YUVtoRGBEffect() {
56         this->setBGColor(0xFFFFFFFF);
57     }
58 
59 protected:
onShortName()60     SkString onShortName() override {
61         return SkString("yuv_to_rgb_effect");
62     }
63 
onISize()64     SkISize onISize() override {
65         int numRows = kLastEnum_SkYUVColorSpace + 1;
66         return SkISize::Make(238, kDrawPad + numRows * kColorSpaceOffset);
67     }
68 
onOnceBeforeDraw()69     void onOnceBeforeDraw() override {
70         SkBitmap bmp[3];
71         SkImageInfo yinfo = SkImageInfo::MakeA8(YSIZE, YSIZE);
72         bmp[0].allocPixels(yinfo);
73         SkImageInfo uinfo = SkImageInfo::MakeA8(USIZE, USIZE);
74         bmp[1].allocPixels(uinfo);
75         SkImageInfo vinfo = SkImageInfo::MakeA8(VSIZE, VSIZE);
76         bmp[2].allocPixels(vinfo);
77         unsigned char* pixels[3];
78         for (int i = 0; i < 3; ++i) {
79             pixels[i] = (unsigned char*)bmp[i].getPixels();
80         }
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};
85         for (int i = 0; i < 3; ++i) {
86             const size_t nbBytes = bmp[i].rowBytes() * bmp[i].height();
87             for (size_t j = 0; j < nbBytes; ++j) {
88                 pixels[i][j] = (unsigned char)color[i];
89                 color[i] = (color[i] == limit[i]) ? invl[i] : color[i] + inc[i];
90             }
91         }
92         for (int i = 0; i < 3; ++i) {
93             fImage[i] = SkImage::MakeFromBitmap(bmp[i]);
94         }
95     }
96 
onDraw(GrContext * context,GrRenderTargetContext * renderTargetContext,SkCanvas * canvas,SkString * errorMsg)97     DrawResult onDraw(GrContext* context, GrRenderTargetContext* renderTargetContext,
98                       SkCanvas* canvas, SkString* errorMsg) override {
99         GrProxyProvider* proxyProvider = context->priv().proxyProvider();
100         sk_sp<GrTextureProxy> proxies[3];
101 
102         for (int i = 0; i < 3; ++i) {
103             proxies[i] = proxyProvider->createTextureProxy(fImage[i], 1, SkBudgeted::kYes,
104                                                            SkBackingFit::kExact);
105             if (!proxies[i]) {
106                 *errorMsg = "Failed to create proxy";
107                 return DrawResult::kFail;
108             }
109         }
110 
111         for (int space = kJPEG_SkYUVColorSpace; space <= kLastEnum_SkYUVColorSpace; ++space) {
112             SkRect renderRect = SkRect::MakeWH(SkIntToScalar(fImage[0]->width()),
113                                                SkIntToScalar(fImage[0]->height()));
114             renderRect.outset(kDrawPad, kDrawPad);
115 
116             SkScalar y = kDrawPad + kTestPad + space * kColorSpaceOffset;
117             SkScalar x = kDrawPad + kTestPad;
118 
119             const int indices[6][3] = {{0, 1, 2}, {0, 2, 1}, {1, 0, 2},
120                                        {1, 2, 0}, {2, 0, 1}, {2, 1, 0}};
121 
122             for (int i = 0; i < 6; ++i) {
123                 SkYUVAIndex yuvaIndices[4] = {
124                     { indices[i][0], SkColorChannel::kR },
125                     { indices[i][1], SkColorChannel::kR },
126                     { indices[i][2], SkColorChannel::kR },
127                     { -1, SkColorChannel::kA }
128                 };
129 
130                 std::unique_ptr<GrFragmentProcessor> fp(
131                         GrYUVtoRGBEffect::Make(proxies, yuvaIndices,
132                                                static_cast<SkYUVColorSpace>(space),
133                                                GrSamplerState::Filter::kNearest));
134                 if (fp) {
135                     GrPaint grPaint;
136                     grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
137                     grPaint.addColorFragmentProcessor(std::move(fp));
138                     SkMatrix viewMatrix;
139                     viewMatrix.setTranslate(x, y);
140                     renderTargetContext->priv().testingOnly_addDrawOp(
141                             GrFillRectOp::MakeNonAARect(context, std::move(grPaint),
142                                                         viewMatrix, renderRect));
143                 }
144                 x += renderRect.width() + kTestPad;
145             }
146         }
147         return DrawResult::kOk;
148      }
149 
150 private:
151     sk_sp<SkImage> fImage[3];
152 
153     static constexpr SkScalar kDrawPad = 10.f;
154     static constexpr SkScalar kTestPad = 10.f;
155     static constexpr SkScalar kColorSpaceOffset = 36.f;
156 
157     typedef GM INHERITED;
158 };
159 
160 DEF_GM(return new YUVtoRGBEffect;)
161 
162 //////////////////////////////////////////////////////////////////////////////
163 
164 class YUVNV12toRGBEffect : public GpuGM {
165 public:
YUVNV12toRGBEffect()166     YUVNV12toRGBEffect() {
167         this->setBGColor(0xFFFFFFFF);
168     }
169 
170 protected:
onShortName()171     SkString onShortName() override {
172         return SkString("yuv_nv12_to_rgb_effect");
173     }
174 
onISize()175     SkISize onISize() override {
176         int numRows = kLastEnum_SkYUVColorSpace + 1;
177         return SkISize::Make(48, kDrawPad + numRows * kColorSpaceOffset);
178     }
179 
onOnceBeforeDraw()180     void onOnceBeforeDraw() override {
181         SkBitmap bmp[2];
182         SkImageInfo yinfo = SkImageInfo::MakeA8(YSIZE, YSIZE);
183         bmp[0].allocPixels(yinfo);
184         SkImageInfo uvinfo = SkImageInfo::MakeN32Premul(USIZE, USIZE);
185         bmp[1].allocPixels(uvinfo);
186         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         {
192             unsigned char* pixels = (unsigned char*)bmp[0].getPixels();
193             const size_t nbBytes = bmp[0].rowBytes() * bmp[0].height();
194             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         {
201             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) {
204                     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         }
210 
211         for (int i = 0; i < 2; ++i) {
212             fImage[i] = SkImage::MakeFromBitmap(bmp[i]);
213         }
214     }
215 
onDraw(GrContext * context,GrRenderTargetContext * renderTargetContext,SkCanvas * canvas,SkString * errorMsg)216     DrawResult onDraw(GrContext* context, GrRenderTargetContext* renderTargetContext,
217                       SkCanvas* canvas, SkString* errorMsg) override {
218         GrProxyProvider* proxyProvider = context->priv().proxyProvider();
219         sk_sp<GrTextureProxy> proxies[2];
220 
221         for (int i = 0; i < 2; ++i) {
222             proxies[i] = proxyProvider->createTextureProxy(fImage[i], 1, SkBudgeted::kYes,
223                                                            SkBackingFit::kExact);
224             if (!proxies[i]) {
225                 *errorMsg = "Failed to create proxy";
226                 return DrawResult::kFail;
227             }
228         }
229 
230         SkYUVAIndex yuvaIndices[4] = {
231             {  0, SkColorChannel::kR },
232             {  1, SkColorChannel::kR },
233             {  1, SkColorChannel::kG },
234             { -1, SkColorChannel::kA }
235         };
236 
237         for (int space = kJPEG_SkYUVColorSpace; space <= kLastEnum_SkYUVColorSpace; ++space) {
238             SkRect renderRect = SkRect::MakeWH(SkIntToScalar(fImage[0]->width()),
239                                                SkIntToScalar(fImage[0]->height()));
240             renderRect.outset(kDrawPad, kDrawPad);
241 
242             SkScalar y = kDrawPad + kTestPad + space * kColorSpaceOffset;
243             SkScalar x = kDrawPad + kTestPad;
244 
245             GrPaint grPaint;
246             grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
247             auto fp = GrYUVtoRGBEffect::Make(proxies, yuvaIndices,
248                                              static_cast<SkYUVColorSpace>(space),
249                                              GrSamplerState::Filter::kNearest);
250             if (fp) {
251                 SkMatrix viewMatrix;
252                 viewMatrix.setTranslate(x, y);
253                 grPaint.addColorFragmentProcessor(std::move(fp));
254                 std::unique_ptr<GrDrawOp> op(GrFillRectOp::MakeNonAARect(
255                         context, std::move(grPaint), viewMatrix, renderRect));
256                 renderTargetContext->priv().testingOnly_addDrawOp(std::move(op));
257             }
258         }
259         return DrawResult::kOk;
260     }
261 
262 private:
263     sk_sp<SkImage> fImage[2];
264 
265     static constexpr SkScalar kDrawPad = 10.f;
266     static constexpr SkScalar kTestPad = 10.f;
267     static constexpr SkScalar kColorSpaceOffset = 36.f;
268 
269     typedef GM INHERITED;
270 };
271 
272 DEF_GM(return new YUVNV12toRGBEffect;)
273 
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 
279 class YUVtoRGBDomainEffect : public GpuGM {
280 public:
YUVtoRGBDomainEffect()281     YUVtoRGBDomainEffect() {
282         this->setBGColor(0xFFFFFFFF);
283     }
284 
285 protected:
onShortName()286     SkString onShortName() override {
287         return SkString("yuv_to_rgb_domain_effect");
288     }
289 
onISize()290     SkISize onISize() override {
291         return SkISize::Make((YSIZE + kTestPad) * 3 + kDrawPad, (YSIZE + kTestPad) * 2 + kDrawPad);
292     }
293 
onOnceBeforeDraw()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 
onDraw(GrContext * context,GrRenderTargetContext * renderTargetContext,SkCanvas * canvas,SkString * errorMsg)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], 1, SkBudgeted::kYes,
320                                                            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 
374 private:
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 
383 DEF_GM(return new YUVtoRGBDomainEffect;)
384 }
385