• 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/GrBitmapTextureMaker.h"
27 #include "src/gpu/GrClip.h"
28 #include "src/gpu/GrContextPriv.h"
29 #include "src/gpu/GrFragmentProcessor.h"
30 #include "src/gpu/GrPaint.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         SkImageInfo yinfo = SkImageInfo::MakeA8(YSIZE, YSIZE);
71         fBitmaps[0].allocPixels(yinfo);
72         SkImageInfo uinfo = SkImageInfo::MakeA8(USIZE, USIZE);
73         fBitmaps[1].allocPixels(uinfo);
74         SkImageInfo vinfo = SkImageInfo::MakeA8(VSIZE, VSIZE);
75         fBitmaps[2].allocPixels(vinfo);
76         unsigned char* pixels[3];
77         for (int i = 0; i < 3; ++i) {
78             pixels[i] = (unsigned char*)fBitmaps[i].getPixels();
79         }
80         int color[] = {0, 85, 170};
81         const int limit[] = {255, 0, 255};
82         const int invl[]  = {0, 255, 0};
83         const int inc[]   = {1, -1, 1};
84         for (int i = 0; i < 3; ++i) {
85             const size_t nbBytes = fBitmaps[i].rowBytes() * fBitmaps[i].height();
86             for (size_t j = 0; j < nbBytes; ++j) {
87                 pixels[i][j] = (unsigned char)color[i];
88                 color[i] = (color[i] == limit[i]) ? invl[i] : color[i] + inc[i];
89             }
90         }
91         for (int i = 0; i < 3; ++i) {
92             fBitmaps[i].setImmutable();
93         }
94     }
95 
onDraw(GrContext * context,GrRenderTargetContext * renderTargetContext,SkCanvas * canvas,SkString * errorMsg)96     DrawResult onDraw(GrContext* context, GrRenderTargetContext* renderTargetContext,
97                       SkCanvas* canvas, SkString* errorMsg) override {
98         GrSurfaceProxyView views[3];
99 
100         for (int i = 0; i < 3; ++i) {
101             GrBitmapTextureMaker maker(context, fBitmaps[i]);
102             std::tie(views[i], std::ignore) = maker.view(GrMipMapped::kNo);
103             if (!views[i]) {
104                 *errorMsg = "Failed to create proxy";
105                 return DrawResult::kFail;
106             }
107         }
108 
109         for (int space = kJPEG_SkYUVColorSpace; space <= kLastEnum_SkYUVColorSpace; ++space) {
110             SkRect renderRect = SkRect::MakeWH(SkIntToScalar(fBitmaps[0].width()),
111                                                SkIntToScalar(fBitmaps[0].height()));
112             renderRect.outset(kDrawPad, kDrawPad);
113 
114             SkScalar y = kDrawPad + kTestPad + space * kColorSpaceOffset;
115             SkScalar x = kDrawPad + kTestPad;
116 
117             const int indices[6][3] = {{0, 1, 2}, {0, 2, 1}, {1, 0, 2},
118                                        {1, 2, 0}, {2, 0, 1}, {2, 1, 0}};
119 
120             for (int i = 0; i < 6; ++i) {
121                 SkYUVAIndex yuvaIndices[4] = {
122                     { indices[i][0], SkColorChannel::kR },
123                     { indices[i][1], SkColorChannel::kR },
124                     { indices[i][2], SkColorChannel::kR },
125                     { -1, SkColorChannel::kA }
126                 };
127                 const auto& caps = *context->priv().caps();
128                 std::unique_ptr<GrFragmentProcessor> fp(GrYUVtoRGBEffect::Make(
129                         views, yuvaIndices, static_cast<SkYUVColorSpace>(space),
130                         GrSamplerState::Filter::kNearest, caps));
131                 if (fp) {
132                     GrPaint grPaint;
133                     grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
134                     grPaint.addColorFragmentProcessor(std::move(fp));
135                     SkMatrix viewMatrix;
136                     viewMatrix.setTranslate(x, y);
137                     renderTargetContext->priv().testingOnly_addDrawOp(
138                             GrFillRectOp::MakeNonAARect(context, std::move(grPaint),
139                                                         viewMatrix, renderRect));
140                 }
141                 x += renderRect.width() + kTestPad;
142             }
143         }
144         return DrawResult::kOk;
145      }
146 
147 private:
148     SkBitmap fBitmaps[3];
149 
150     static constexpr SkScalar kDrawPad = 10.f;
151     static constexpr SkScalar kTestPad = 10.f;
152     static constexpr SkScalar kColorSpaceOffset = 36.f;
153 
154     typedef GM INHERITED;
155 };
156 
157 DEF_GM(return new YUVtoRGBEffect;)
158 
159 //////////////////////////////////////////////////////////////////////////////
160 
161 class YUVNV12toRGBEffect : public GpuGM {
162 public:
YUVNV12toRGBEffect()163     YUVNV12toRGBEffect() {
164         this->setBGColor(0xFFFFFFFF);
165     }
166 
167 protected:
onShortName()168     SkString onShortName() override {
169         return SkString("yuv_nv12_to_rgb_effect");
170     }
171 
onISize()172     SkISize onISize() override {
173         int numRows = kLastEnum_SkYUVColorSpace + 1;
174         return SkISize::Make(48, kDrawPad + numRows * kColorSpaceOffset);
175     }
176 
onOnceBeforeDraw()177     void onOnceBeforeDraw() override {
178         SkImageInfo yinfo = SkImageInfo::MakeA8(YSIZE, YSIZE);
179         fBitmaps[0].allocPixels(yinfo);
180         SkImageInfo uvinfo = SkImageInfo::MakeN32Premul(USIZE, USIZE);
181         fBitmaps[1].allocPixels(uvinfo);
182         int color[] = {0, 85, 170};
183         const int limit[] = {255, 0, 255};
184         const int invl[] = {0, 255, 0};
185         const int inc[] = {1, -1, 1};
186 
187         {
188             unsigned char* pixels = (unsigned char*)fBitmaps[0].getPixels();
189             const size_t nbBytes = fBitmaps[0].rowBytes() * fBitmaps[0].height();
190             for (size_t j = 0; j < nbBytes; ++j) {
191                 pixels[j] = (unsigned char)color[0];
192                 color[0] = (color[0] == limit[0]) ? invl[0] : color[0] + inc[0];
193             }
194         }
195 
196         {
197             for (int y = 0; y < fBitmaps[1].height(); ++y) {
198                 uint32_t* pixels = fBitmaps[1].getAddr32(0, y);
199                 for (int j = 0; j < fBitmaps[1].width(); ++j) {
200                     pixels[j] = SkColorSetARGB(0, color[1], color[2], 0);
201                     color[1] = (color[1] == limit[1]) ? invl[1] : color[1] + inc[1];
202                     color[2] = (color[2] == limit[2]) ? invl[2] : color[2] + inc[2];
203                 }
204             }
205         }
206 
207         for (int i = 0; i < 2; ++i) {
208             fBitmaps[i].setImmutable();
209         }
210     }
211 
onDraw(GrContext * context,GrRenderTargetContext * renderTargetContext,SkCanvas * canvas,SkString * errorMsg)212     DrawResult onDraw(GrContext* context, GrRenderTargetContext* renderTargetContext,
213                       SkCanvas* canvas, SkString* errorMsg) override {
214         GrSurfaceProxyView views[2];
215 
216         for (int i = 0; i < 2; ++i) {
217             GrBitmapTextureMaker maker(context, fBitmaps[i]);
218             std::tie(views[i], std::ignore) = maker.view(GrMipMapped::kNo);
219             if (!views[i]) {
220                 *errorMsg = "Failed to create proxy";
221                 return DrawResult::kFail;
222             }
223         }
224 
225         SkYUVAIndex yuvaIndices[4] = {
226             {  0, SkColorChannel::kR },
227             {  1, SkColorChannel::kR },
228             {  1, SkColorChannel::kG },
229             { -1, SkColorChannel::kA }
230         };
231 
232         for (int space = kJPEG_SkYUVColorSpace; space <= kLastEnum_SkYUVColorSpace; ++space) {
233             SkRect renderRect = SkRect::MakeWH(SkIntToScalar(fBitmaps[0].width()),
234                                                SkIntToScalar(fBitmaps[0].height()));
235             renderRect.outset(kDrawPad, kDrawPad);
236 
237             SkScalar y = kDrawPad + kTestPad + space * kColorSpaceOffset;
238             SkScalar x = kDrawPad + kTestPad;
239 
240             GrPaint grPaint;
241             grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
242             const auto& caps = *context->priv().caps();
243             auto fp = GrYUVtoRGBEffect::Make(views, yuvaIndices,
244                                              static_cast<SkYUVColorSpace>(space),
245                                              GrSamplerState::Filter::kNearest, caps);
246             if (fp) {
247                 SkMatrix viewMatrix;
248                 viewMatrix.setTranslate(x, y);
249                 grPaint.addColorFragmentProcessor(std::move(fp));
250                 std::unique_ptr<GrDrawOp> op(GrFillRectOp::MakeNonAARect(
251                         context, std::move(grPaint), viewMatrix, renderRect));
252                 renderTargetContext->priv().testingOnly_addDrawOp(std::move(op));
253             }
254         }
255         return DrawResult::kOk;
256     }
257 
258 private:
259     SkBitmap fBitmaps[2];
260 
261     static constexpr SkScalar kDrawPad = 10.f;
262     static constexpr SkScalar kTestPad = 10.f;
263     static constexpr SkScalar kColorSpaceOffset = 36.f;
264 
265     typedef GM INHERITED;
266 };
267 
268 DEF_GM(return new YUVNV12toRGBEffect;)
269 
270 //////////////////////////////////////////////////////////////////////////////
271 
272 // This GM tests domain clamping on YUV multiplanar images where the U and V
273 // planes have different resolution from Y. See skbug:8959
274 
275 class YUVtoRGBDomainEffect : public GpuGM {
276 public:
YUVtoRGBDomainEffect()277     YUVtoRGBDomainEffect() {
278         this->setBGColor(0xFFFFFFFF);
279     }
280 
281 protected:
onShortName()282     SkString onShortName() override {
283         return SkString("yuv_to_rgb_domain_effect");
284     }
285 
onISize()286     SkISize onISize() override {
287         return SkISize::Make((YSIZE + kTestPad) * 3 + kDrawPad, (YSIZE + kTestPad) * 2 + kDrawPad);
288     }
289 
onOnceBeforeDraw()290     void onOnceBeforeDraw() override {
291         SkImageInfo yinfo = SkImageInfo::MakeA8(YSIZE, YSIZE);
292         fBitmaps[0].allocPixels(yinfo);
293         SkImageInfo uinfo = SkImageInfo::MakeA8(USIZE, USIZE);
294         fBitmaps[1].allocPixels(uinfo);
295         SkImageInfo vinfo = SkImageInfo::MakeA8(VSIZE, VSIZE);
296         fBitmaps[2].allocPixels(vinfo);
297 
298         int innerColor[] = {149, 43, 21};
299         int outerColor[] = {128, 128, 128};
300         for (int i = 0; i < 3; ++i) {
301             fBitmaps[i].eraseColor(SkColorSetARGB(outerColor[i], 0, 0, 0));
302             SkIRect innerRect = i == 0 ? SkIRect::MakeLTRB(2, 2, 6, 6) : SkIRect::MakeLTRB(1, 1, 3, 3);
303             fBitmaps[i].erase(SkColorSetARGB(innerColor[i], 0, 0, 0), innerRect);
304             fBitmaps[i].setImmutable();
305         }
306     }
307 
onDraw(GrContext * context,GrRenderTargetContext * renderTargetContext,SkCanvas * canvas,SkString * errorMsg)308     DrawResult onDraw(GrContext* context, GrRenderTargetContext* renderTargetContext,
309                       SkCanvas* canvas, SkString* errorMsg) override {
310         GrSurfaceProxyView views[3];
311 
312         for (int i = 0; i < 3; ++i) {
313             GrBitmapTextureMaker maker(context, fBitmaps[i]);
314             std::tie(views[i], std::ignore) = maker.view(GrMipMapped::kNo);
315             if (!views[i]) {
316                 *errorMsg = "Failed to create proxy";
317                 return DrawResult::kFail;
318             }
319         }
320 
321         // Draw a 2x2 grid of the YUV images.
322         // Rows = kNearest, kBilerp, Cols = No clamp, clamp
323         static const GrSamplerState::Filter kFilters[] = {
324                 GrSamplerState::Filter::kNearest, GrSamplerState::Filter::kBilerp };
325         static const SkRect kGreenRect = SkRect::MakeLTRB(2.f, 2.f, 6.f, 6.f);
326 
327         SkYUVAIndex yuvaIndices[4] = {
328             { SkYUVAIndex::kY_Index, SkColorChannel::kR },
329             { SkYUVAIndex::kU_Index, SkColorChannel::kR },
330             { SkYUVAIndex::kV_Index, SkColorChannel::kR },
331             { -1, SkColorChannel::kA }
332         };
333         SkRect rect = SkRect::MakeWH(YSIZE, YSIZE);
334 
335         SkScalar y = kDrawPad + kTestPad;
336         for (uint32_t i = 0; i < SK_ARRAY_COUNT(kFilters); ++i) {
337             SkScalar x = kDrawPad + kTestPad;
338 
339             for (uint32_t j = 0; j < 2; ++j) {
340                 SkMatrix ctm = SkMatrix::MakeTrans(x, y);
341                 ctm.postScale(10.f, 10.f);
342 
343                 SkRect domain = kGreenRect;
344                 if (kFilters[i] == GrSamplerState::Filter::kNearest) {
345                     // Make a very small inset for nearest-neighbor filtering so that 0.5px
346                     // centers don't round out beyond the green pixels.
347                     domain.inset(0.01f, 0.01f);
348                 }
349 
350                 const SkRect* domainPtr = j > 0 ? &domain : nullptr;
351                 const auto& caps = *context->priv().caps();
352                 std::unique_ptr<GrFragmentProcessor> fp(
353                         GrYUVtoRGBEffect::Make(views, yuvaIndices, kJPEG_SkYUVColorSpace,
354                                                kFilters[i], caps, SkMatrix::I(), domainPtr));
355                 if (fp) {
356                     GrPaint grPaint;
357                     grPaint.addColorFragmentProcessor(std::move(fp));
358                     renderTargetContext->drawRect(
359                             GrNoClip(), std::move(grPaint), GrAA::kYes, ctm, rect);
360                 }
361                 x += rect.width() + kTestPad;
362             }
363 
364             y += rect.height() + kTestPad;
365         }
366 
367         return DrawResult::kOk;
368      }
369 
370 private:
371     SkBitmap fBitmaps[3];
372 
373     static constexpr SkScalar kDrawPad = 10.f;
374     static constexpr SkScalar kTestPad = 10.f;
375 
376     typedef GM INHERITED;
377 };
378 
379 DEF_GM(return new YUVtoRGBDomainEffect;)
380 }
381