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/private/GrTypesPriv.h"
25 #include "src/gpu/GrBitmapTextureMaker.h"
26 #include "src/gpu/GrDirectContextPriv.h"
27 #include "src/gpu/GrFragmentProcessor.h"
28 #include "src/gpu/GrPaint.h"
29 #include "src/gpu/GrRenderTargetContext.h"
30 #include "src/gpu/GrRenderTargetContextPriv.h"
31 #include "src/gpu/GrSamplerState.h"
32 #include "src/gpu/GrTextureProxy.h"
33 #include "src/gpu/effects/GrPorterDuffXferProcessor.h"
34 #include "src/gpu/effects/GrYUVtoRGBEffect.h"
35 #include "src/gpu/ops/GrDrawOp.h"
36 #include "src/gpu/ops/GrFillRectOp.h"
37 
38 #include <memory>
39 #include <utility>
40 
41 class SkCanvas;
42 
43 #define YSIZE 8
44 #define USIZE 4
45 #define VSIZE 4
46 
47 namespace skiagm {
48 /**
49  * This GM directly exercises GrYUVtoRGBEffect.
50  */
51 class YUVtoRGBEffect : public GpuGM {
52 public:
YUVtoRGBEffect()53     YUVtoRGBEffect() {
54         this->setBGColor(0xFFFFFFFF);
55     }
56 
57 protected:
onShortName()58     SkString onShortName() override {
59         return SkString("yuv_to_rgb_effect");
60     }
61 
onISize()62     SkISize onISize() override {
63         int numRows = kLastEnum_SkYUVColorSpace + 1;
64         return SkISize::Make(238, kDrawPad + numRows * kColorSpaceOffset);
65     }
66 
onOnceBeforeDraw()67     void onOnceBeforeDraw() override {
68         SkImageInfo yinfo = SkImageInfo::MakeA8(YSIZE, YSIZE);
69         fBitmaps[0].allocPixels(yinfo);
70         SkImageInfo uinfo = SkImageInfo::MakeA8(USIZE, USIZE);
71         fBitmaps[1].allocPixels(uinfo);
72         SkImageInfo vinfo = SkImageInfo::MakeA8(VSIZE, VSIZE);
73         fBitmaps[2].allocPixels(vinfo);
74         unsigned char* pixels[3];
75         for (int i = 0; i < 3; ++i) {
76             pixels[i] = (unsigned char*)fBitmaps[i].getPixels();
77         }
78         int color[] = {0, 85, 170};
79         const int limit[] = {255, 0, 255};
80         const int invl[]  = {0, 255, 0};
81         const int inc[]   = {1, -1, 1};
82         for (int i = 0; i < 3; ++i) {
83             const size_t nbBytes = fBitmaps[i].rowBytes() * fBitmaps[i].height();
84             for (size_t j = 0; j < nbBytes; ++j) {
85                 pixels[i][j] = (unsigned char)color[i];
86                 color[i] = (color[i] == limit[i]) ? invl[i] : color[i] + inc[i];
87             }
88         }
89         for (int i = 0; i < 3; ++i) {
90             fBitmaps[i].setImmutable();
91         }
92     }
93 
onDraw(GrRecordingContext * context,GrRenderTargetContext * renderTargetContext,SkCanvas * canvas,SkString * errorMsg)94     DrawResult onDraw(GrRecordingContext* context, GrRenderTargetContext* renderTargetContext,
95                       SkCanvas* canvas, SkString* errorMsg) override {
96         GrSurfaceProxyView views[3];
97 
98         for (int i = 0; i < 3; ++i) {
99             GrBitmapTextureMaker maker(context, fBitmaps[i], GrImageTexGenPolicy::kDraw);
100             views[i] = maker.view(GrMipmapped::kNo);
101             if (!views[i]) {
102                 *errorMsg = "Failed to create proxy";
103                 return DrawResult::kFail;
104             }
105         }
106 
107         for (int space = kJPEG_SkYUVColorSpace; space <= kLastEnum_SkYUVColorSpace; ++space) {
108             SkRect renderRect = SkRect::MakeWH(SkIntToScalar(fBitmaps[0].width()),
109                                                SkIntToScalar(fBitmaps[0].height()));
110             renderRect.outset(kDrawPad, kDrawPad);
111 
112             SkScalar y = kDrawPad + kTestPad + space * kColorSpaceOffset;
113             SkScalar x = kDrawPad + kTestPad;
114 
115             const int indices[6][3] = {{0, 1, 2}, {0, 2, 1}, {1, 0, 2},
116                                        {1, 2, 0}, {2, 0, 1}, {2, 1, 0}};
117 
118             for (int i = 0; i < 6; ++i) {
119                 SkYUVAIndex yuvaIndices[4] = {
120                     { indices[i][0], SkColorChannel::kR },
121                     { indices[i][1], SkColorChannel::kR },
122                     { indices[i][2], SkColorChannel::kR },
123                     { -1, SkColorChannel::kA }
124                 };
125                 const auto& caps = *context->priv().caps();
126                 std::unique_ptr<GrFragmentProcessor> fp(GrYUVtoRGBEffect::Make(
127                         views, yuvaIndices, static_cast<SkYUVColorSpace>(space),
128                         GrSamplerState::Filter::kNearest, caps));
129                 if (fp) {
130                     GrPaint grPaint;
131                     grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
132                     grPaint.setColorFragmentProcessor(std::move(fp));
133                     SkMatrix viewMatrix;
134                     viewMatrix.setTranslate(x, y);
135                     renderTargetContext->priv().testingOnly_addDrawOp(
136                             GrFillRectOp::MakeNonAARect(context, std::move(grPaint),
137                                                         viewMatrix, renderRect));
138                 }
139                 x += renderRect.width() + kTestPad;
140             }
141         }
142         return DrawResult::kOk;
143      }
144 
145 private:
146     SkBitmap fBitmaps[3];
147 
148     static constexpr SkScalar kDrawPad = 10.f;
149     static constexpr SkScalar kTestPad = 10.f;
150     static constexpr SkScalar kColorSpaceOffset = 36.f;
151 
152     using INHERITED = GM;
153 };
154 
155 DEF_GM(return new YUVtoRGBEffect;)
156 
157 //////////////////////////////////////////////////////////////////////////////
158 
159 class YUVNV12toRGBEffect : public GpuGM {
160 public:
YUVNV12toRGBEffect()161     YUVNV12toRGBEffect() {
162         this->setBGColor(0xFFFFFFFF);
163     }
164 
165 protected:
onShortName()166     SkString onShortName() override {
167         return SkString("yuv_nv12_to_rgb_effect");
168     }
169 
onISize()170     SkISize onISize() override {
171         int numRows = kLastEnum_SkYUVColorSpace + 1;
172         return SkISize::Make(48, kDrawPad + numRows * kColorSpaceOffset);
173     }
174 
onOnceBeforeDraw()175     void onOnceBeforeDraw() override {
176         SkImageInfo yinfo = SkImageInfo::MakeA8(YSIZE, YSIZE);
177         fBitmaps[0].allocPixels(yinfo);
178         SkImageInfo uvinfo = SkImageInfo::MakeN32Premul(USIZE, USIZE);
179         fBitmaps[1].allocPixels(uvinfo);
180         int color[] = {0, 85, 170};
181         const int limit[] = {255, 0, 255};
182         const int invl[] = {0, 255, 0};
183         const int inc[] = {1, -1, 1};
184 
185         {
186             unsigned char* pixels = (unsigned char*)fBitmaps[0].getPixels();
187             const size_t nbBytes = fBitmaps[0].rowBytes() * fBitmaps[0].height();
188             for (size_t j = 0; j < nbBytes; ++j) {
189                 pixels[j] = (unsigned char)color[0];
190                 color[0] = (color[0] == limit[0]) ? invl[0] : color[0] + inc[0];
191             }
192         }
193 
194         {
195             for (int y = 0; y < fBitmaps[1].height(); ++y) {
196                 uint32_t* pixels = fBitmaps[1].getAddr32(0, y);
197                 for (int j = 0; j < fBitmaps[1].width(); ++j) {
198                     pixels[j] = SkColorSetARGB(0, color[1], color[2], 0);
199                     color[1] = (color[1] == limit[1]) ? invl[1] : color[1] + inc[1];
200                     color[2] = (color[2] == limit[2]) ? invl[2] : color[2] + inc[2];
201                 }
202             }
203         }
204 
205         for (int i = 0; i < 2; ++i) {
206             fBitmaps[i].setImmutable();
207         }
208     }
209 
onDraw(GrRecordingContext * context,GrRenderTargetContext * renderTargetContext,SkCanvas * canvas,SkString * errorMsg)210     DrawResult onDraw(GrRecordingContext* context, GrRenderTargetContext* renderTargetContext,
211                       SkCanvas* canvas, SkString* errorMsg) override {
212         GrSurfaceProxyView views[2];
213 
214         for (int i = 0; i < 2; ++i) {
215             GrBitmapTextureMaker maker(context, fBitmaps[i], GrImageTexGenPolicy::kDraw);
216             views[i] = maker.view(GrMipmapped::kNo);
217             if (!views[i]) {
218                 *errorMsg = "Failed to create proxy";
219                 return DrawResult::kFail;
220             }
221         }
222 
223         SkYUVAIndex yuvaIndices[4] = {
224             {  0, SkColorChannel::kR },
225             {  1, SkColorChannel::kR },
226             {  1, SkColorChannel::kG },
227             { -1, SkColorChannel::kA }
228         };
229 
230         for (int space = kJPEG_SkYUVColorSpace; space <= kLastEnum_SkYUVColorSpace; ++space) {
231             SkRect renderRect = SkRect::MakeWH(SkIntToScalar(fBitmaps[0].width()),
232                                                SkIntToScalar(fBitmaps[0].height()));
233             renderRect.outset(kDrawPad, kDrawPad);
234 
235             SkScalar y = kDrawPad + kTestPad + space * kColorSpaceOffset;
236             SkScalar x = kDrawPad + kTestPad;
237 
238             GrPaint grPaint;
239             grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
240             const auto& caps = *context->priv().caps();
241             auto fp = GrYUVtoRGBEffect::Make(views, yuvaIndices,
242                                              static_cast<SkYUVColorSpace>(space),
243                                              GrSamplerState::Filter::kNearest, caps);
244             if (fp) {
245                 SkMatrix viewMatrix;
246                 viewMatrix.setTranslate(x, y);
247                 grPaint.setColorFragmentProcessor(std::move(fp));
248                 GrOp::Owner op = GrFillRectOp::MakeNonAARect(
249                         context, std::move(grPaint), viewMatrix, renderRect);
250                 renderTargetContext->priv().testingOnly_addDrawOp(std::move(op));
251             }
252         }
253         return DrawResult::kOk;
254     }
255 
256 private:
257     SkBitmap fBitmaps[2];
258 
259     static constexpr SkScalar kDrawPad = 10.f;
260     static constexpr SkScalar kTestPad = 10.f;
261     static constexpr SkScalar kColorSpaceOffset = 36.f;
262 
263     using INHERITED = GM;
264 };
265 
266 DEF_GM(return new YUVNV12toRGBEffect;)
267 
268 //////////////////////////////////////////////////////////////////////////////
269 
270 // This GM tests subsetting YUV multiplanar images where the U and V
271 // planes have different resolution from Y. See skbug:8959
272 
273 class YUVtoRGBSubsetEffect : public GpuGM {
274 public:
YUVtoRGBSubsetEffect()275     YUVtoRGBSubsetEffect() {
276         this->setBGColor(0xFFFFFFFF);
277     }
278 
279 protected:
onShortName()280     SkString onShortName() override {
281         return SkString("yuv_to_rgb_subset_effect");
282     }
283 
onISize()284     SkISize onISize() override { return {1310, 540}; }
285 
onOnceBeforeDraw()286     void onOnceBeforeDraw() override {
287         SkImageInfo yinfo = SkImageInfo::MakeA8(YSIZE, YSIZE);
288         fBitmaps[0].allocPixels(yinfo);
289         SkImageInfo uinfo = SkImageInfo::MakeA8(USIZE, USIZE);
290         fBitmaps[1].allocPixels(uinfo);
291         SkImageInfo vinfo = SkImageInfo::MakeA8(VSIZE, VSIZE);
292         fBitmaps[2].allocPixels(vinfo);
293 
294         unsigned char innerY[16] = {149, 160, 130, 105,
295                                     160, 130, 105, 149,
296                                     130, 105, 149, 160,
297                                     105, 149, 160, 130};
298         unsigned char innerU[4] = {43, 75, 145, 200};
299         unsigned char innerV[4] = {88, 180, 200, 43};
300         int outerYUV[] = {128, 128, 128};
301         for (int i = 0; i < 3; ++i) {
302             fBitmaps[i].eraseColor(SkColorSetARGB(outerYUV[i], 0, 0, 0));
303         }
304         SkPixmap innerYPM(SkImageInfo::MakeA8(4, 4), innerY, 4);
305         SkPixmap innerUPM(SkImageInfo::MakeA8(2, 2), innerU, 2);
306         SkPixmap innerVPM(SkImageInfo::MakeA8(2, 2), innerV, 2);
307         fBitmaps[0].writePixels(innerYPM, 2, 2);
308         fBitmaps[1].writePixels(innerUPM, 1, 1);
309         fBitmaps[2].writePixels(innerVPM, 1, 1);
310         for (auto& fBitmap : fBitmaps) {
311             fBitmap.setImmutable();
312         }
313     }
314 
onDraw(GrRecordingContext * context,GrRenderTargetContext * renderTargetContext,SkCanvas * canvas,SkString * errorMsg)315     DrawResult onDraw(GrRecordingContext* context, GrRenderTargetContext* renderTargetContext,
316                       SkCanvas* canvas, SkString* errorMsg) override {
317         GrSurfaceProxyView views[3];
318 
319         for (int i = 0; i < 3; ++i) {
320             GrBitmapTextureMaker maker(context, fBitmaps[i], GrImageTexGenPolicy::kDraw);
321             views[i] = maker.view(GrMipmapped::kNo);
322             if (!views[i]) {
323                 *errorMsg = "Failed to create proxy";
324                 return DrawResult::kFail;
325             }
326         }
327 
328         static const GrSamplerState::Filter kFilters[] = {GrSamplerState::Filter::kNearest,
329                                                           GrSamplerState::Filter::kLinear};
330         static const SkRect kColorRect = SkRect::MakeLTRB(2.f, 2.f, 6.f, 6.f);
331 
332         SkYUVAIndex yuvaIndices[4] = {
333             { SkYUVAIndex::kY_Index, SkColorChannel::kR },
334             { SkYUVAIndex::kU_Index, SkColorChannel::kR },
335             { SkYUVAIndex::kV_Index, SkColorChannel::kR },
336             { -1, SkColorChannel::kA }
337         };
338         // Outset to visualize wrap modes.
339         SkRect rect = SkRect::MakeWH(YSIZE, YSIZE).makeOutset(YSIZE/2, YSIZE/2);
340 
341         SkScalar y = kTestPad;
342         // Rows are filter modes.
343         for (uint32_t i = 0; i < SK_ARRAY_COUNT(kFilters); ++i) {
344             SkScalar x = kTestPad;
345             // Columns are non-subsetted followed by subsetted with each WrapMode in a row
346             for (uint32_t j = 0; j < GrSamplerState::kWrapModeCount + 1; ++j) {
347                 SkMatrix ctm = SkMatrix::Translate(x, y);
348                 ctm.postScale(10.f, 10.f);
349 
350                 const SkRect* subset = j > 0 ? &kColorRect : nullptr;
351 
352                 GrSamplerState samplerState;
353                 samplerState.setFilterMode(kFilters[i]);
354                 if (j > 0) {
355                     auto wm = static_cast<GrSamplerState::WrapMode>(j - 1);
356                     samplerState.setWrapModeX(wm);
357                     samplerState.setWrapModeY(wm);
358                 }
359                 const auto& caps = *context->priv().caps();
360                 std::unique_ptr<GrFragmentProcessor> fp(
361                         GrYUVtoRGBEffect::Make(views, yuvaIndices, kJPEG_SkYUVColorSpace,
362                                                samplerState, caps, SkMatrix::I(), subset));
363                 if (fp) {
364                     GrPaint grPaint;
365                     grPaint.setColorFragmentProcessor(std::move(fp));
366                     renderTargetContext->drawRect(
367                             nullptr, std::move(grPaint), GrAA::kYes, ctm, rect);
368                 }
369                 x += rect.width() + kTestPad;
370             }
371 
372             y += rect.height() + kTestPad;
373         }
374 
375         return DrawResult::kOk;
376      }
377 
378 private:
379     SkBitmap fBitmaps[3];
380 
381     static constexpr SkScalar kTestPad = 10.f;
382 
383     using INHERITED = GM;
384 };
385 
386 DEF_GM(return new YUVtoRGBSubsetEffect;)
387 }  // namespace skiagm
388