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], GrImageTexGenPolicy::kDraw);
102             views[i] = 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], GrImageTexGenPolicy::kDraw);
218             views[i] = 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 subsetting YUV multiplanar images where the U and V
273 // planes have different resolution from Y. See skbug:8959
274 
275 class YUVtoRGBSubsetEffect : public GpuGM {
276 public:
YUVtoRGBSubsetEffect()277     YUVtoRGBSubsetEffect() {
278         this->setBGColor(0xFFFFFFFF);
279     }
280 
281 protected:
onShortName()282     SkString onShortName() override {
283         return SkString("yuv_to_rgb_subset_effect");
284     }
285 
onISize()286     SkISize onISize() override { return {1310, 540}; }
287 
onOnceBeforeDraw()288     void onOnceBeforeDraw() override {
289         SkImageInfo yinfo = SkImageInfo::MakeA8(YSIZE, YSIZE);
290         fBitmaps[0].allocPixels(yinfo);
291         SkImageInfo uinfo = SkImageInfo::MakeA8(USIZE, USIZE);
292         fBitmaps[1].allocPixels(uinfo);
293         SkImageInfo vinfo = SkImageInfo::MakeA8(VSIZE, VSIZE);
294         fBitmaps[2].allocPixels(vinfo);
295 
296         int innerY = 149;
297         unsigned char innerU[4] = {43, 75, 145, 200};
298         unsigned char innerV[4] = {88, 180, 200, 43};
299         int outerYUV[] = {128, 128, 128};
300         for (int i = 0; i < 3; ++i) {
301             fBitmaps[i].eraseColor(SkColorSetARGB(outerYUV[i], 0, 0, 0));
302         }
303         fBitmaps[0].eraseARGB(innerY, 0, 0, 0);
304         SkPixmap innerUPM(SkImageInfo::MakeA8(2, 2), innerU, 2);
305         SkPixmap innerVPM(SkImageInfo::MakeA8(2, 2), innerV, 2);
306         fBitmaps[1].writePixels(innerUPM, 1, 1);
307         fBitmaps[2].writePixels(innerVPM, 1, 1);
308         for (auto& fBitmap : fBitmaps) {
309             fBitmap.setImmutable();
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         GrSurfaceProxyView views[3];
316 
317         for (int i = 0; i < 3; ++i) {
318             GrBitmapTextureMaker maker(context, fBitmaps[i], GrImageTexGenPolicy::kDraw);
319             views[i] = maker.view(GrMipMapped::kNo);
320             if (!views[i]) {
321                 *errorMsg = "Failed to create proxy";
322                 return DrawResult::kFail;
323             }
324         }
325 
326         static const GrSamplerState::Filter kFilters[] = {
327                 GrSamplerState::Filter::kNearest, GrSamplerState::Filter::kBilerp };
328         static const SkRect kColorRect = SkRect::MakeLTRB(2.f, 2.f, 6.f, 6.f);
329 
330         SkYUVAIndex yuvaIndices[4] = {
331             { SkYUVAIndex::kY_Index, SkColorChannel::kR },
332             { SkYUVAIndex::kU_Index, SkColorChannel::kR },
333             { SkYUVAIndex::kV_Index, SkColorChannel::kR },
334             { -1, SkColorChannel::kA }
335         };
336         // Outset to visualize wrap modes.
337         SkRect rect = SkRect::MakeWH(YSIZE, YSIZE).makeOutset(YSIZE/2, YSIZE/2);
338 
339         SkScalar y = kTestPad;
340         // Rows are filter modes.
341         for (uint32_t i = 0; i < SK_ARRAY_COUNT(kFilters); ++i) {
342             SkScalar x = kTestPad;
343             // Columns are non-subsetted followed by subsetted with each WrapMode in a row
344             for (uint32_t j = 0; j < GrSamplerState::kWrapModeCount + 1; ++j) {
345                 SkMatrix ctm = SkMatrix::MakeTrans(x, y);
346                 ctm.postScale(10.f, 10.f);
347 
348                 const SkRect* subset = j > 0 ? &kColorRect : nullptr;
349 
350                 GrSamplerState samplerState;
351                 samplerState.setFilterMode(kFilters[i]);
352                 if (j > 0) {
353                     auto wm = static_cast<GrSamplerState::WrapMode>(j - 1);
354                     samplerState.setWrapModeX(wm);
355                     samplerState.setWrapModeY(wm);
356                 }
357                 const auto& caps = *context->priv().caps();
358                 std::unique_ptr<GrFragmentProcessor> fp(
359                         GrYUVtoRGBEffect::Make(views, yuvaIndices, kJPEG_SkYUVColorSpace,
360                                                samplerState, caps, SkMatrix::I(), subset));
361                 if (fp) {
362                     GrPaint grPaint;
363                     grPaint.addColorFragmentProcessor(std::move(fp));
364                     renderTargetContext->drawRect(
365                             GrNoClip(), std::move(grPaint), GrAA::kYes, ctm, rect);
366                 }
367                 x += rect.width() + kTestPad;
368             }
369 
370             y += rect.height() + kTestPad;
371         }
372 
373         return DrawResult::kOk;
374      }
375 
376 private:
377     SkBitmap fBitmaps[3];
378 
379     static constexpr SkScalar kTestPad = 10.f;
380 
381     typedef GM INHERITED;
382 };
383 
384 DEF_GM(return new YUVtoRGBSubsetEffect;)
385 }
386