xref: /aosp_15_r20/external/skia/gm/yuvtorgbsubset.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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/SkColor.h"
13 #include "include/core/SkImageInfo.h"
14 #include "include/core/SkMatrix.h"
15 #include "include/core/SkRect.h"
16 #include "include/core/SkScalar.h"
17 #include "include/core/SkShader.h"
18 #include "include/core/SkSize.h"
19 #include "include/core/SkString.h"
20 #include "include/core/SkYUVAInfo.h"
21 #include "include/core/SkYUVAPixmaps.h"
22 #include "include/gpu/ganesh/GrDirectContext.h"
23 #include "src/core/SkCanvasPriv.h"
24 #include "src/gpu/ganesh/SkGr.h"
25 #include "tools/gpu/YUVUtils.h"
26 
27 #include <memory>
28 #include <utility>
29 
30 class SkCanvas;
31 
32 namespace skiagm {
33 
34 //////////////////////////////////////////////////////////////////////////////
35 
36 // This GM tests subsetting YUV multiplanar images where the U and V
37 // planes have different resolution from Y. See skbug:8959
38 
39 class YUVtoRGBSubsetEffect : public GM {
40 public:
YUVtoRGBSubsetEffect()41     YUVtoRGBSubsetEffect() {
42         this->setBGColor(0xFFFFFFFF);
43     }
44 
45 protected:
getName() const46     SkString getName() const override { return SkString("yuv_to_rgb_subset_effect"); }
47 
getISize()48     SkISize getISize() override { return {1310, 540}; }
49 
makePixmaps()50     void makePixmaps() {
51         SkYUVAInfo yuvaInfo = SkYUVAInfo({8, 8},
52                                          SkYUVAInfo::PlaneConfig::kY_U_V,
53                                          SkYUVAInfo::Subsampling::k420,
54                                          kJPEG_Full_SkYUVColorSpace);
55         SkColorType colorTypes[] = {kAlpha_8_SkColorType,
56                                     kAlpha_8_SkColorType,
57                                     kAlpha_8_SkColorType};
58         SkYUVAPixmapInfo pmapInfo(yuvaInfo, colorTypes, nullptr);
59         fPixmaps = SkYUVAPixmaps::Allocate(pmapInfo);
60 
61         unsigned char innerY[16] = {149, 160, 130, 105,
62                                     160, 130, 105, 149,
63                                     130, 105, 149, 160,
64                                     105, 149, 160, 130};
65         unsigned char innerU[4] = {43, 75, 145, 200};
66         unsigned char innerV[4] = {88, 180, 200, 43};
67         int outerYUV[] = {128, 128, 128};
68         SkBitmap bitmaps[3];
69         for (int i = 0; i < 3; ++i) {
70             bitmaps[i].installPixels(fPixmaps.plane(i));
71             bitmaps[i].eraseColor(SkColorSetARGB(outerYUV[i], 0, 0, 0));
72         }
73         SkPixmap innerYPM(SkImageInfo::MakeA8(4, 4), innerY, 4);
74         SkPixmap innerUPM(SkImageInfo::MakeA8(2, 2), innerU, 2);
75         SkPixmap innerVPM(SkImageInfo::MakeA8(2, 2), innerV, 2);
76         bitmaps[0].writePixels(innerYPM, 2, 2);
77         bitmaps[1].writePixels(innerUPM, 1, 1);
78         bitmaps[2].writePixels(innerVPM, 1, 1);
79     }
80 
onGpuSetup(SkCanvas * canvas,SkString * errorMsg,GraphiteTestContext *)81     DrawResult onGpuSetup(SkCanvas* canvas, SkString* errorMsg, GraphiteTestContext*) override {
82         auto context = GrAsDirectContext(canvas->recordingContext());
83         skgpu::graphite::Recorder* recorder = nullptr;
84 #if defined(SK_GRAPHITE)
85         recorder = canvas->recorder();
86 #endif
87         if (!context && !recorder) {
88             return DrawResult::kSkip;
89         }
90 
91         if (!fPixmaps.isValid()) {
92             this->makePixmaps();
93         }
94 
95         auto lazyYUV = sk_gpu_test::LazyYUVImage::Make(fPixmaps);
96 #if defined(SK_GRAPHITE)
97         if (recorder) {
98             fYUVImage = lazyYUV->refImage(recorder, sk_gpu_test::LazyYUVImage::Type::kFromPixmaps);
99         } else
100 #endif
101         {
102             fYUVImage = lazyYUV->refImage(context, sk_gpu_test::LazyYUVImage::Type::kFromPixmaps);
103         }
104 
105         return DrawResult::kOk;
106     }
107 
onGpuTeardown()108     void onGpuTeardown() override { fYUVImage.reset(); }
109 
onDraw(SkCanvas * canvas,SkString * errorMsg)110     DrawResult onDraw(SkCanvas* canvas,
111                       SkString* errorMsg) override {
112         auto context = GrAsDirectContext(canvas->recordingContext());
113         skgpu::graphite::Recorder* recorder = nullptr;
114 #if defined(SK_GRAPHITE)
115         recorder = canvas->recorder();
116 #endif
117         if (!context && !recorder) {
118             *errorMsg = kErrorMsg_DrawSkippedGpuOnly;
119             return DrawResult::kSkip;
120         }
121 
122         if (!fYUVImage) {
123             *errorMsg = "No valid YUV image generated -- skipping";
124             return DrawResult::kSkip;
125         }
126 
127         static const SkFilterMode kFilters[] = {SkFilterMode::kNearest,
128                                                 SkFilterMode::kLinear};
129         static const SkIRect kColorRect = SkIRect::MakeLTRB(2, 2, 6, 6);
130 
131         // Outset to visualize wrap modes.
132         SkRect rect = SkRect::Make(fYUVImage->dimensions());
133         rect = rect.makeOutset(fYUVImage->width()/2.f, fYUVImage->height()/2.f);
134 
135         SkScalar y = kTestPad;
136         // Rows are filter modes.
137         for (uint32_t i = 0; i < std::size(kFilters); ++i) {
138             SkScalar x = kTestPad;
139             // Columns are non-subsetted followed by subsetted with each TileMode in a row
140             for (uint32_t j = 0; j < kSkTileModeCount + 1; ++j) {
141                 SkMatrix ctm = SkMatrix::Translate(x, y);
142                 ctm.postScale(10.f, 10.f);
143 
144                 const SkIRect* subset = j > 0 ? &kColorRect : nullptr;
145 
146                 auto tm = SkTileMode::kClamp;
147                 if (j > 0) {
148                     tm = static_cast<SkTileMode>(j - 1);
149                 }
150 
151                 canvas->save();
152                 canvas->concat(ctm);
153                 SkSamplingOptions sampling(kFilters[i]);
154                 SkPaint paint;
155                 // Draw black rectangle in background so rendering with Decal tilemode matches
156                 // the previously used ClampToBorder wrapmode.
157                 paint.setColor(SK_ColorBLACK);
158                 canvas->drawRect(rect, paint);
159                 if (subset) {
160                     sk_sp<SkImage> subsetImg;
161 #if defined(SK_GRAPHITE)
162                     if (recorder) {
163                         subsetImg = fYUVImage->makeSubset(recorder, *subset, {false});
164                     } else
165 #endif
166                     {
167                         subsetImg = fYUVImage->makeSubset(context, *subset);
168                     }
169                     SkASSERT(subsetImg);
170                     paint.setShader(subsetImg->makeShader(tm, tm,
171                                                           sampling, SkMatrix::Translate(2, 2)));
172                 } else {
173                     paint.setShader(fYUVImage->makeShader(tm, tm,
174                                                           sampling, SkMatrix::I()));
175                 }
176                 canvas->drawRect(rect, paint);
177                 canvas->restore();
178                 x += rect.width() + kTestPad;
179             }
180 
181             y += rect.height() + kTestPad;
182         }
183 
184         return DrawResult::kOk;
185     }
186 
187 private:
188     SkYUVAPixmaps fPixmaps;
189     sk_sp<SkImage> fYUVImage;
190 
191     inline static constexpr SkScalar kTestPad = 10.f;
192 
193     using INHERITED = GM;
194 };
195 
196 DEF_GM(return new YUVtoRGBSubsetEffect;)
197 }  // namespace skiagm
198