xref: /aosp_15_r20/external/skia/gm/yuv420_odd_dim.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2019 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "gm/gm.h"
9*c8dee2aaSAndroid Build Coastguard Worker 
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkBitmap.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkCanvas.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImage.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPixmap.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkShader.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkStream.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSurface.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTileMode.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/encode/SkJpegEncoder.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/GrRecordingContext.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/SkImageGanesh.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkRandom.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkCachedData.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "src/image/SkImage_Base.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "tools/DecodeUtils.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "tools/Resources.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "tools/gpu/YUVUtils.h"
27*c8dee2aaSAndroid Build Coastguard Worker 
28*c8dee2aaSAndroid Build Coastguard Worker static constexpr int kScale = 10;
29*c8dee2aaSAndroid Build Coastguard Worker static constexpr SkISize kImageDim = {5, 5};
30*c8dee2aaSAndroid Build Coastguard Worker 
make_image(GrRecordingContext * rContext,skgpu::graphite::Recorder * recorder)31*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<SkImage> make_image(GrRecordingContext* rContext,
32*c8dee2aaSAndroid Build Coastguard Worker                                  skgpu::graphite::Recorder* recorder) {
33*c8dee2aaSAndroid Build Coastguard Worker     // Generate a small jpeg with odd dimensions.
34*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap bmp;
35*c8dee2aaSAndroid Build Coastguard Worker     bmp.allocPixels(SkImageInfo::Make(kImageDim, kRGBA_8888_SkColorType, kPremul_SkAlphaType));
36*c8dee2aaSAndroid Build Coastguard Worker     SkRandom random;
37*c8dee2aaSAndroid Build Coastguard Worker     // These random values won't compress well, but it doesn't matter. This test exists to
38*c8dee2aaSAndroid Build Coastguard Worker     // compare the GPU YUV code path to the SW.
39*c8dee2aaSAndroid Build Coastguard Worker     for (int y = 0; y < bmp.height(); ++y) {
40*c8dee2aaSAndroid Build Coastguard Worker         for (int x = 0; x < bmp.width(); ++x) {
41*c8dee2aaSAndroid Build Coastguard Worker             *bmp.getAddr32(x, y) = random.nextU() | 0xFF000000;
42*c8dee2aaSAndroid Build Coastguard Worker         }
43*c8dee2aaSAndroid Build Coastguard Worker     }
44*c8dee2aaSAndroid Build Coastguard Worker     bmp.notifyPixelsChanged();
45*c8dee2aaSAndroid Build Coastguard Worker     SkDynamicMemoryWStream stream;
46*c8dee2aaSAndroid Build Coastguard Worker     SkJpegEncoder::Options options;
47*c8dee2aaSAndroid Build Coastguard Worker     options.fDownsample = SkJpegEncoder::Downsample::k420;
48*c8dee2aaSAndroid Build Coastguard Worker     options.fQuality = 100;
49*c8dee2aaSAndroid Build Coastguard Worker     if (!SkJpegEncoder::Encode(&stream, bmp.pixmap(), options)) {
50*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
51*c8dee2aaSAndroid Build Coastguard Worker     }
52*c8dee2aaSAndroid Build Coastguard Worker     auto imageHelper = sk_gpu_test::LazyYUVImage::Make(stream.detachAsData());
53*c8dee2aaSAndroid Build Coastguard Worker     if (!imageHelper) {
54*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
55*c8dee2aaSAndroid Build Coastguard Worker     }
56*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_GRAPHITE)
57*c8dee2aaSAndroid Build Coastguard Worker     if (recorder) {
58*c8dee2aaSAndroid Build Coastguard Worker         return imageHelper->refImage(recorder, sk_gpu_test::LazyYUVImage::Type::kFromPixmaps);
59*c8dee2aaSAndroid Build Coastguard Worker     } else
60*c8dee2aaSAndroid Build Coastguard Worker #endif
61*c8dee2aaSAndroid Build Coastguard Worker     {
62*c8dee2aaSAndroid Build Coastguard Worker         return imageHelper->refImage(rContext, sk_gpu_test::LazyYUVImage::Type::kFromPixmaps);
63*c8dee2aaSAndroid Build Coastguard Worker     }
64*c8dee2aaSAndroid Build Coastguard Worker }
65*c8dee2aaSAndroid Build Coastguard Worker 
66*c8dee2aaSAndroid Build Coastguard Worker // This GM tests that the YUVA image code path in the GPU backend handles odd sized images with
67*c8dee2aaSAndroid Build Coastguard Worker // 420 chroma subsampling correctly.
68*c8dee2aaSAndroid Build Coastguard Worker DEF_SIMPLE_GM_CAN_FAIL(yuv420_odd_dim, canvas, errMsg,
69*c8dee2aaSAndroid Build Coastguard Worker                        kScale* kImageDim.width(), kScale* kImageDim.height()) {
70*c8dee2aaSAndroid Build Coastguard Worker     auto rContext = canvas->recordingContext();
71*c8dee2aaSAndroid Build Coastguard Worker     skgpu::graphite::Recorder* recorder = nullptr;
72*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_GRAPHITE)
73*c8dee2aaSAndroid Build Coastguard Worker     recorder = canvas->recorder();
74*c8dee2aaSAndroid Build Coastguard Worker #endif
75*c8dee2aaSAndroid Build Coastguard Worker     if (!rContext && !recorder) {
76*c8dee2aaSAndroid Build Coastguard Worker         // This GM exists to exercise GPU planar images.
77*c8dee2aaSAndroid Build Coastguard Worker         return skiagm::DrawResult::kSkip;
78*c8dee2aaSAndroid Build Coastguard Worker     }
79*c8dee2aaSAndroid Build Coastguard Worker     auto image = make_image(rContext, recorder);
80*c8dee2aaSAndroid Build Coastguard Worker     if (!image) {
81*c8dee2aaSAndroid Build Coastguard Worker         if (rContext) {
82*c8dee2aaSAndroid Build Coastguard Worker             return rContext->abandoned() ? skiagm::DrawResult::kOk : skiagm::DrawResult::kFail;
83*c8dee2aaSAndroid Build Coastguard Worker         } else {
84*c8dee2aaSAndroid Build Coastguard Worker             return skiagm::DrawResult::kFail;
85*c8dee2aaSAndroid Build Coastguard Worker         }
86*c8dee2aaSAndroid Build Coastguard Worker     }
87*c8dee2aaSAndroid Build Coastguard Worker     // We draw the image offscreen and then blow it up using nearest filtering by kScale.
88*c8dee2aaSAndroid Build Coastguard Worker     // This avoids skbug.com/9693
89*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkSurface> surface;
90*c8dee2aaSAndroid Build Coastguard Worker     if (auto origSurface = canvas->getSurface()) {
91*c8dee2aaSAndroid Build Coastguard Worker         surface = origSurface->makeSurface(image->width(), image->height());
92*c8dee2aaSAndroid Build Coastguard Worker     } else {
93*c8dee2aaSAndroid Build Coastguard Worker         auto ct = canvas->imageInfo().colorType();
94*c8dee2aaSAndroid Build Coastguard Worker         if (ct == kUnknown_SkColorType) {
95*c8dee2aaSAndroid Build Coastguard Worker             ct = image->colorType();
96*c8dee2aaSAndroid Build Coastguard Worker         }
97*c8dee2aaSAndroid Build Coastguard Worker         auto info = canvas->imageInfo().makeColorType(ct);
98*c8dee2aaSAndroid Build Coastguard Worker         info = info.makeAlphaType(kPremul_SkAlphaType);
99*c8dee2aaSAndroid Build Coastguard Worker         surface = SkSurfaces::Raster(info);
100*c8dee2aaSAndroid Build Coastguard Worker     }
101*c8dee2aaSAndroid Build Coastguard Worker     surface->getCanvas()->drawImage(image, 0, 0);
102*c8dee2aaSAndroid Build Coastguard Worker     canvas->scale(kScale, kScale);
103*c8dee2aaSAndroid Build Coastguard Worker     canvas->drawImage(surface->makeImageSnapshot(), 0, 0);
104*c8dee2aaSAndroid Build Coastguard Worker     return skiagm::DrawResult::kOk;
105*c8dee2aaSAndroid Build Coastguard Worker }
106*c8dee2aaSAndroid Build Coastguard Worker 
107*c8dee2aaSAndroid Build Coastguard Worker // crbug.com/1210557 Subsampled planes weren't repeated at the correct frequency.
108*c8dee2aaSAndroid Build Coastguard Worker DEF_SIMPLE_GM_CAN_FAIL(yuv420_odd_dim_repeat, canvas, errMsg,
109*c8dee2aaSAndroid Build Coastguard Worker                        1000,
110*c8dee2aaSAndroid Build Coastguard Worker                        500) {
111*c8dee2aaSAndroid Build Coastguard Worker     auto rContext = canvas->recordingContext();
112*c8dee2aaSAndroid Build Coastguard Worker     skgpu::graphite::Recorder* recorder = nullptr;
113*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_GRAPHITE)
114*c8dee2aaSAndroid Build Coastguard Worker     recorder = canvas->recorder();
115*c8dee2aaSAndroid Build Coastguard Worker #endif
116*c8dee2aaSAndroid Build Coastguard Worker     if (!rContext && !recorder) {
117*c8dee2aaSAndroid Build Coastguard Worker         // This GM exists to exercise GPU planar images.
118*c8dee2aaSAndroid Build Coastguard Worker         return skiagm::DrawResult::kSkip;
119*c8dee2aaSAndroid Build Coastguard Worker     }
120*c8dee2aaSAndroid Build Coastguard Worker     auto image = ToolUtils::GetResourceAsImage("images/mandrill_256.png");
121*c8dee2aaSAndroid Build Coastguard Worker     if (!image) {
122*c8dee2aaSAndroid Build Coastguard Worker         if (rContext) {
123*c8dee2aaSAndroid Build Coastguard Worker             return rContext->abandoned() ? skiagm::DrawResult::kOk : skiagm::DrawResult::kFail;
124*c8dee2aaSAndroid Build Coastguard Worker         } else {
125*c8dee2aaSAndroid Build Coastguard Worker             return skiagm::DrawResult::kFail;
126*c8dee2aaSAndroid Build Coastguard Worker         }
127*c8dee2aaSAndroid Build Coastguard Worker     }
128*c8dee2aaSAndroid Build Coastguard Worker     // Make sure the image is odd dimensioned.
129*c8dee2aaSAndroid Build Coastguard Worker     int w = image->width()  & 0b1 ? image->width()  : image->width()  - 1;
130*c8dee2aaSAndroid Build Coastguard Worker     int h = image->height() & 0b1 ? image->height() : image->height() - 1;
131*c8dee2aaSAndroid Build Coastguard Worker     image = image->makeSubset(nullptr, SkIRect::MakeWH(w, h));
132*c8dee2aaSAndroid Build Coastguard Worker 
133*c8dee2aaSAndroid Build Coastguard Worker     auto [planes, yuvaInfo] = sk_gpu_test::MakeYUVAPlanesAsA8(image.get(),
134*c8dee2aaSAndroid Build Coastguard Worker                                                               kJPEG_SkYUVColorSpace,
135*c8dee2aaSAndroid Build Coastguard Worker                                                               SkYUVAInfo::Subsampling::k420,
136*c8dee2aaSAndroid Build Coastguard Worker                                                               nullptr);
137*c8dee2aaSAndroid Build Coastguard Worker     SkPixmap pixmaps[4];
138*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < yuvaInfo.numPlanes(); ++i) {
139*c8dee2aaSAndroid Build Coastguard Worker         planes[i]->peekPixels(&pixmaps[i]);
140*c8dee2aaSAndroid Build Coastguard Worker     }
141*c8dee2aaSAndroid Build Coastguard Worker     auto yuvaPixmaps = SkYUVAPixmaps::FromExternalPixmaps(yuvaInfo, pixmaps);
142*c8dee2aaSAndroid Build Coastguard Worker     auto lazyYUV = sk_gpu_test::LazyYUVImage::Make(yuvaPixmaps, skgpu::Mipmapped::kYes);
143*c8dee2aaSAndroid Build Coastguard Worker 
144*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_GRAPHITE)
145*c8dee2aaSAndroid Build Coastguard Worker     if (recorder) {
146*c8dee2aaSAndroid Build Coastguard Worker         image = lazyYUV->refImage(recorder, sk_gpu_test::LazyYUVImage::Type::kFromPixmaps);
147*c8dee2aaSAndroid Build Coastguard Worker     } else
148*c8dee2aaSAndroid Build Coastguard Worker #endif
149*c8dee2aaSAndroid Build Coastguard Worker     {
150*c8dee2aaSAndroid Build Coastguard Worker         image = lazyYUV->refImage(rContext, sk_gpu_test::LazyYUVImage::Type::kFromPixmaps);
151*c8dee2aaSAndroid Build Coastguard Worker     }
152*c8dee2aaSAndroid Build Coastguard Worker     if (!image) {
153*c8dee2aaSAndroid Build Coastguard Worker         *errMsg = "Could not make YUVA image";
154*c8dee2aaSAndroid Build Coastguard Worker         if (rContext) {
155*c8dee2aaSAndroid Build Coastguard Worker             return rContext->abandoned() ? skiagm::DrawResult::kOk : skiagm::DrawResult::kFail;
156*c8dee2aaSAndroid Build Coastguard Worker         } else {
157*c8dee2aaSAndroid Build Coastguard Worker             return skiagm::DrawResult::kFail;
158*c8dee2aaSAndroid Build Coastguard Worker         }
159*c8dee2aaSAndroid Build Coastguard Worker     }
160*c8dee2aaSAndroid Build Coastguard Worker     int i = 0;
161*c8dee2aaSAndroid Build Coastguard Worker     for (SkMipmapMode mm : {SkMipmapMode::kNone, SkMipmapMode::kLinear}) {
162*c8dee2aaSAndroid Build Coastguard Worker         int j = 0;
163*c8dee2aaSAndroid Build Coastguard Worker         for (SkFilterMode filter : {SkFilterMode::kNearest, SkFilterMode::kLinear}) {
164*c8dee2aaSAndroid Build Coastguard Worker             canvas->save();
165*c8dee2aaSAndroid Build Coastguard Worker             canvas->clipRect(SkRect::MakeXYWH(500.f*j, 250.f*i, 500.f, 250.f));
166*c8dee2aaSAndroid Build Coastguard Worker             canvas->rotate(30.f);
167*c8dee2aaSAndroid Build Coastguard Worker             canvas->scale(0.4f, 0.4f);  // so mipmaps sampling doesn't just use base level.
168*c8dee2aaSAndroid Build Coastguard Worker             // Large translation so that if U/V planes aren't repeated correctly WRT to Y plane we
169*c8dee2aaSAndroid Build Coastguard Worker             // accumulate a lot of error.
170*c8dee2aaSAndroid Build Coastguard Worker             canvas->translate(-240000.f, -240000.f);
171*c8dee2aaSAndroid Build Coastguard Worker             auto shader = image->makeShader(SkTileMode::kRepeat,
172*c8dee2aaSAndroid Build Coastguard Worker                                             SkTileMode::kRepeat,
173*c8dee2aaSAndroid Build Coastguard Worker                                             SkSamplingOptions(filter, mm));
174*c8dee2aaSAndroid Build Coastguard Worker             SkPaint paint;
175*c8dee2aaSAndroid Build Coastguard Worker             paint.setShader(std::move(shader));
176*c8dee2aaSAndroid Build Coastguard Worker             canvas->drawPaint(paint);
177*c8dee2aaSAndroid Build Coastguard Worker             canvas->restore();
178*c8dee2aaSAndroid Build Coastguard Worker             ++j;
179*c8dee2aaSAndroid Build Coastguard Worker         }
180*c8dee2aaSAndroid Build Coastguard Worker         ++i;
181*c8dee2aaSAndroid Build Coastguard Worker     }
182*c8dee2aaSAndroid Build Coastguard Worker     return skiagm::DrawResult::kOk;
183*c8dee2aaSAndroid Build Coastguard Worker }
184