blob: 51f6a3cf6d67fc137fe68cd7c3bc3523bd45b904 [file] [log] [blame]
bsalomon993a4212015-05-29 11:37:25 -07001/*
2 * Copyright 2015 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
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "gm/gm.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "include/core/SkBitmap.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040012#include "include/core/SkCanvas.h"
13#include "include/core/SkColor.h"
14#include "include/core/SkColorFilter.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050015#include "include/core/SkImage.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040016#include "include/core/SkImageInfo.h"
17#include "include/core/SkPaint.h"
18#include "include/core/SkPixmap.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040019#include "include/core/SkRefCnt.h"
20#include "include/core/SkScalar.h"
Kevin Lubickc69d9992023-02-15 08:04:24 -050021#include "include/core/SkShader.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040022#include "include/core/SkSize.h"
23#include "include/core/SkString.h"
Brian Salomon86d07fd2020-10-02 14:37:57 -040024#include "include/core/SkSurface.h"
Kevin Lubickc69d9992023-02-15 08:04:24 -050025#include "include/core/SkTileMode.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040026#include "include/core/SkTypes.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050027#include "include/gpu/GrBackendSurface.h"
Robert Phillipsb87b39b2020-07-01 14:45:24 -040028#include "include/gpu/GrDirectContext.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040029#include "include/gpu/GrTypes.h"
Kevin Lubick5c93acf2023-05-09 12:11:43 -040030#include "include/gpu/ganesh/SkSurfaceGanesh.h"
Kevin Lubick19936eb2023-01-05 09:00:37 -050031#include "include/private/base/SkTo.h"
Kevin Lubick1b3aa8b2023-01-19 14:03:31 -050032#include "src/base/SkMathPriv.h"
Brian Salomon7c94d182020-03-18 17:24:02 -040033#include "src/core/SkYUVMath.h"
Kevin Lubick8b741882023-10-06 11:41:38 -040034#include "tools/DecodeUtils.h"
Brian Salomon5c4c61e2020-03-25 14:26:01 -040035#include "tools/Resources.h"
Robert Phillipsf105d382020-06-19 14:27:14 -040036#include "tools/gpu/YUVUtils.h"
37
bsalomon993a4212015-05-29 11:37:25 -070038namespace skiagm {
Robert Phillipsedcd4312021-06-03 10:14:16 -040039class ImageFromYUVTextures : public GM {
bsalomon993a4212015-05-29 11:37:25 -070040public:
41 ImageFromYUVTextures() {
42 this->setBGColor(0xFFFFFFFF);
43 }
44
45protected:
Leandro Lovisolo24fa2112023-08-15 19:05:17 +000046 SkString getName() const override { return SkString("image_from_yuv_textures"); }
bsalomon993a4212015-05-29 11:37:25 -070047
Leandro Lovisolo8f023882023-08-15 21:13:52 +000048 SkISize getISize() override { return {1420, 610}; }
Brian Salomon9b228382020-03-24 12:55:29 -040049
Brian Salomon7db71392020-10-16 10:05:21 -040050 static std::unique_ptr<sk_gpu_test::LazyYUVImage> CreatePlanes(const char* name) {
Brian Salomon5c4c61e2020-03-25 14:26:01 -040051 SkBitmap bmp;
Kevin Lubick8b741882023-10-06 11:41:38 -040052 if (!ToolUtils::GetResourceAsBitmap(name, &bmp)) {
Brian Salomon5c4c61e2020-03-25 14:26:01 -040053 return {};
54 }
Brian Salomon7db71392020-10-16 10:05:21 -040055 if (bmp.colorType() != kRGBA_8888_SkColorType) {
56 auto info = bmp.info().makeColorType(kRGBA_8888_SkColorType);
57 SkBitmap copy;
58 copy.allocPixels(info);
59 SkAssertResult(bmp.readPixels(copy.pixmap()));
60 bmp = copy;
61 }
62 SkYUVAPixmapInfo pixmapInfo({bmp.dimensions(),
Brian Salomone4387382020-11-11 16:34:19 -050063 SkYUVAInfo::PlaneConfig::kY_U_V_A,
64 SkYUVAInfo::Subsampling::k420,
Brian Salomon7db71392020-10-16 10:05:21 -040065 kJPEG_Full_SkYUVColorSpace},
66 SkYUVAPixmapInfo::DataType::kUnorm8,
67 nullptr);
68 auto pixmaps = SkYUVAPixmaps::Allocate(pixmapInfo);
Brian Salomon5c4c61e2020-03-25 14:26:01 -040069
70 unsigned char* yuvPixels[] = {
Brian Salomon7db71392020-10-16 10:05:21 -040071 static_cast<unsigned char*>(pixmaps.planes()[0].writable_addr()),
72 static_cast<unsigned char*>(pixmaps.planes()[1].writable_addr()),
73 static_cast<unsigned char*>(pixmaps.planes()[2].writable_addr()),
74 static_cast<unsigned char*>(pixmaps.planes()[3].writable_addr()),
Brian Salomon5c4c61e2020-03-25 14:26:01 -040075 };
bsalomon993a4212015-05-29 11:37:25 -070076
Brian Salomon7c94d182020-03-18 17:24:02 -040077 float m[20];
Brian Salomon7db71392020-10-16 10:05:21 -040078 SkColorMatrix_RGB2YUV(pixmaps.yuvaInfo().yuvColorSpace(), m);
Robert Phillips0a22ba82019-03-06 12:36:47 -050079 // Here we encode using the kJPEG_SkYUVColorSpace (i.e., full-swing Rec 601) even though
80 // we will draw it with all the supported yuv color spaces when converted back to RGB
Brian Salomon7db71392020-10-16 10:05:21 -040081 for (int j = 0; j < pixmaps.planes()[0].height(); ++j) {
82 for (int i = 0; i < pixmaps.planes()[0].width(); ++i) {
83 auto rgba = *bmp.getAddr32(i, j);
Brian Salomon5c4c61e2020-03-25 14:26:01 -040084 auto r = (rgba & 0x000000ff) >> 0;
85 auto g = (rgba & 0x0000ff00) >> 8;
86 auto b = (rgba & 0x00ff0000) >> 16;
87 auto a = (rgba & 0xff000000) >> 24;
Brian Salomon7db71392020-10-16 10:05:21 -040088 yuvPixels[0][j*pixmaps.planes()[0].width() + i] = SkToU8(
Brian Salomon5c4c61e2020-03-25 14:26:01 -040089 sk_float_round2int(m[0]*r + m[1]*g + m[2]*b + m[3]*a + 255*m[4]));
Brian Salomon7db71392020-10-16 10:05:21 -040090 yuvPixels[3][j*pixmaps.planes()[0].width() + i] = SkToU8(sk_float_round2int(
Brian Salomon5c4c61e2020-03-25 14:26:01 -040091 m[15]*r + m[16]*g + m[17]*b + m[18]*a + 255*m[19]));
92 }
bsalomon993a4212015-05-29 11:37:25 -070093 }
Brian Salomon7db71392020-10-16 10:05:21 -040094 for (int j = 0; j < pixmaps.planes()[1].height(); ++j) {
95 for (int i = 0; i < pixmaps.planes()[1].width(); ++i) {
bsalomon993a4212015-05-29 11:37:25 -070096 // Average together 4 pixels of RGB.
Brian Salomon7c94d182020-03-18 17:24:02 -040097 int rgba[] = {0, 0, 0, 0};
Brian Salomon7db71392020-10-16 10:05:21 -040098 int denom = 0;
99 int ylimit = std::min(2*j + 2, pixmaps.planes()[0].height());
100 int xlimit = std::min(2*i + 2, pixmaps.planes()[0].width());
101 for (int y = 2*j; y < ylimit; ++y) {
102 for (int x = 2*i; x < xlimit; ++x) {
103 auto src = *bmp.getAddr32(x, y);
Brian Salomon5c4c61e2020-03-25 14:26:01 -0400104 rgba[0] += (src & 0x000000ff) >> 0;
105 rgba[1] += (src & 0x0000ff00) >> 8;
106 rgba[2] += (src & 0x00ff0000) >> 16;
107 rgba[3] += (src & 0xff000000) >> 24;
Brian Salomon7db71392020-10-16 10:05:21 -0400108 ++denom;
bsalomon993a4212015-05-29 11:37:25 -0700109 }
110 }
Brian Salomon7c94d182020-03-18 17:24:02 -0400111 for (int c = 0; c < 4; ++c) {
Brian Salomon7db71392020-10-16 10:05:21 -0400112 rgba[c] /= denom;
bsalomon993a4212015-05-29 11:37:25 -0700113 }
Brian Salomon7db71392020-10-16 10:05:21 -0400114 int uvIndex = j*pixmaps.planes()[1].width() + i;
Brian Salomon5c4c61e2020-03-25 14:26:01 -0400115 yuvPixels[1][uvIndex] = SkToU8(sk_float_round2int(
Brian Salomon7c94d182020-03-18 17:24:02 -0400116 m[5]*rgba[0] + m[6]*rgba[1] + m[7]*rgba[2] + m[8]*rgba[3] + 255*m[9]));
Brian Salomon5c4c61e2020-03-25 14:26:01 -0400117 yuvPixels[2][uvIndex] = SkToU8(sk_float_round2int(
Brian Salomon7c94d182020-03-18 17:24:02 -0400118 m[10]*rgba[0] + m[11]*rgba[1] + m[12]*rgba[2] + m[13]*rgba[3] + 255*m[14]));
bsalomon993a4212015-05-29 11:37:25 -0700119 }
120 }
Brian Salomon7db71392020-10-16 10:05:21 -0400121 return sk_gpu_test::LazyYUVImage::Make(std::move(pixmaps));
Robert Phillipsf105d382020-06-19 14:27:14 -0400122 }
bsalomon993a4212015-05-29 11:37:25 -0700123
Robert Phillipsb87b39b2020-07-01 14:45:24 -0400124 sk_sp<SkImage> makeYUVAImage(GrDirectContext* context) {
Brian Salomon7db71392020-10-16 10:05:21 -0400125 return fLazyYUVImage->refImage(context, sk_gpu_test::LazyYUVImage::Type::kFromTextures);
Brian Salomon839fb222020-10-16 13:30:54 +0000126 }
127
128 sk_sp<SkImage> createReferenceImage(GrDirectContext* dContext) {
Brian Salomon7db71392020-10-16 10:05:21 -0400129 auto planarImage = this->makeYUVAImage(dContext);
130 if (!planarImage) {
131 return nullptr;
132 }
133
134 auto resultInfo = SkImageInfo::Make(fLazyYUVImage->dimensions(),
Brian Salomon86d07fd2020-10-02 14:37:57 -0400135 kRGBA_8888_SkColorType,
136 kPremul_SkAlphaType);
Kevin Lubick5c93acf2023-05-09 12:11:43 -0400137 auto resultSurface = SkSurfaces::RenderTarget(
Kevin Lubickecd3a2f2023-01-05 08:17:45 -0500138 dContext, skgpu::Budgeted::kYes, resultInfo, 1, kTopLeft_GrSurfaceOrigin, nullptr);
Brian Salomon86d07fd2020-10-02 14:37:57 -0400139 if (!resultSurface) {
140 return nullptr;
141 }
142
Brian Salomon7db71392020-10-16 10:05:21 -0400143 resultSurface->getCanvas()->drawImage(std::move(planarImage), 0, 0);
Brian Salomon86d07fd2020-10-02 14:37:57 -0400144 return resultSurface->makeImageSnapshot();
Robert Phillipsf105d382020-06-19 14:27:14 -0400145 }
146
Brian Salomonc759bbf2023-12-05 11:11:27 -0500147 DrawResult onGpuSetup(SkCanvas* canvas, SkString* errorMsg, GraphiteTestContext*) override {
Jim Van Vertha8624432023-02-13 16:48:09 -0500148 auto dContext = GrAsDirectContext(canvas->recordingContext());
Robert Phillipsedcd4312021-06-03 10:14:16 -0400149 if (!dContext || dContext->abandoned()) {
150 *errorMsg = "DirectContext required to create YUV images";
Robert Phillipsf105d382020-06-19 14:27:14 -0400151 return DrawResult::kSkip;
152 }
153
Brian Salomon7db71392020-10-16 10:05:21 -0400154 if (!fLazyYUVImage) {
155 fLazyYUVImage = CreatePlanes("images/mandrill_32.png");
156 }
Robert Phillipsf105d382020-06-19 14:27:14 -0400157
158 // We make a version of this image for each draw because, if any draw flattens it to
159 // RGBA, then all subsequent draws would use the RGBA texture.
160 for (int i = 0; i < kNumImages; ++i) {
Robert Phillipsedcd4312021-06-03 10:14:16 -0400161 fYUVAImages[i] = this->makeYUVAImage(dContext);
Robert Phillipsf105d382020-06-19 14:27:14 -0400162 if (!fYUVAImages[i]) {
163 *errorMsg = "Couldn't create src YUVA image.";
164 return DrawResult::kFail;
165 }
166 }
167
Robert Phillipsedcd4312021-06-03 10:14:16 -0400168 fReferenceImage = this->createReferenceImage(dContext);
Robert Phillipsf105d382020-06-19 14:27:14 -0400169 if (!fReferenceImage) {
170 *errorMsg = "Couldn't create reference YUVA image.";
171 return DrawResult::kFail;
172 }
173
174 // Some backends (e.g., Vulkan) require all work be completed for backend textures
175 // before they are deleted. Since we don't know when we'll next have access to a
176 // direct context, flush all the work now.
Robert Phillipsedcd4312021-06-03 10:14:16 -0400177 dContext->flush();
Kevin Lubick99bcee22023-09-06 10:09:08 -0400178 dContext->submit(GrSyncCpu::kYes);
Robert Phillipsf105d382020-06-19 14:27:14 -0400179
180 return DrawResult::kOk;
181 }
182
Robert Phillipsb795bea2020-06-25 12:38:53 -0400183 void onGpuTeardown() override {
184 for (sk_sp<SkImage>& image : fYUVAImages) {
185 image.reset();
186 }
187 fReferenceImage.reset();
188 }
189
Robert Phillipsf105d382020-06-19 14:27:14 -0400190 SkImage* getYUVAImage(int index) {
191 SkASSERT(index >= 0 && index < kNumImages);
192 return fYUVAImages[index].get();
193 }
194
Robert Phillipsedcd4312021-06-03 10:14:16 -0400195 void onDraw(SkCanvas* canvas) override {
Mike Reedd396cd52021-01-23 21:14:47 -0500196 auto draw_image = [canvas](SkImage* image, const SkSamplingOptions& sampling) -> SkSize {
Brian Salomon5c4c61e2020-03-25 14:26:01 -0400197 if (!image) {
198 return {0, 0};
Brian Salomon36737482020-03-24 16:47:52 -0400199 }
Mike Reedd396cd52021-01-23 21:14:47 -0500200 canvas->drawImage(image, 0, 0, sampling, nullptr);
Brian Salomon5c4c61e2020-03-25 14:26:01 -0400201 return {SkIntToScalar(image->width()), SkIntToScalar(image->height())};
202 };
Brian Salomon36737482020-03-24 16:47:52 -0400203
Mike Reedd396cd52021-01-23 21:14:47 -0500204 auto draw_image_rect = [canvas](SkImage* image,
205 const SkSamplingOptions& sampling) -> SkSize {
Brian Salomon5c4c61e2020-03-25 14:26:01 -0400206 if (!image) {
207 return {0, 0};
208 }
Brian Salomon5c4c61e2020-03-25 14:26:01 -0400209 auto subset = SkRect::Make(image->dimensions());
210 subset.inset(subset.width() * .05f, subset.height() * .1f);
211 auto dst = SkRect::MakeWH(subset.width(), subset.height());
Mike Reedd396cd52021-01-23 21:14:47 -0500212 canvas->drawImageRect(image, subset, dst, sampling, nullptr,
213 SkCanvas::kStrict_SrcRectConstraint);
Brian Salomon5c4c61e2020-03-25 14:26:01 -0400214 return {dst.width(), dst.height()};
215 };
216
Mike Reedd396cd52021-01-23 21:14:47 -0500217 auto draw_image_shader = [canvas](SkImage* image,
218 const SkSamplingOptions& sampling) -> SkSize {
Brian Salomon5c4c61e2020-03-25 14:26:01 -0400219 if (!image) {
220 return {0, 0};
221 }
222 SkMatrix m;
223 m.setRotate(45, image->width()/2.f, image->height()/2.f);
Brian Salomon5c4c61e2020-03-25 14:26:01 -0400224 SkPaint paint;
Mike Reedb86cd3d2020-12-10 14:55:43 -0500225 paint.setShader(image->makeShader(SkTileMode::kMirror, SkTileMode::kDecal,
Mike Reedd396cd52021-01-23 21:14:47 -0500226 sampling, m));
Brian Salomon5c4c61e2020-03-25 14:26:01 -0400227 auto rect = SkRect::MakeWH(image->width() * 1.3f, image->height());
228 canvas->drawRect(rect, paint);
229 return {rect.width(), rect.height()};
230 };
231
232 canvas->translate(kPad, kPad);
Robert Phillipsf105d382020-06-19 14:27:14 -0400233 int imageIndex = 0;
Mike Reedd396cd52021-01-23 21:14:47 -0500234 using DrawSig = SkSize(SkImage* image, const SkSamplingOptions&);
Brian Salomon5c4c61e2020-03-25 14:26:01 -0400235 using DF = std::function<DrawSig>;
236 for (const auto& draw : {DF(draw_image), DF(draw_image_rect), DF(draw_image_shader)}) {
237 for (auto scale : {1.f, 4.f, 0.75f}) {
238 SkScalar h = 0;
239 canvas->save();
Mike Reedd396cd52021-01-23 21:14:47 -0500240 for (const auto& sampling : {
241 SkSamplingOptions(SkFilterMode::kNearest),
242 SkSamplingOptions(SkFilterMode::kLinear),
243 SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNearest),
Mike Reedf3ac2af2021-02-05 12:55:38 -0500244 SkSamplingOptions(SkCubicResampler::Mitchell())})
Mike Reedd396cd52021-01-23 21:14:47 -0500245 {
Brian Salomon5c4c61e2020-03-25 14:26:01 -0400246 canvas->save();
247 canvas->scale(scale, scale);
Mike Reedd396cd52021-01-23 21:14:47 -0500248 auto s1 = draw(this->getYUVAImage(imageIndex++), sampling);
Brian Salomon5c4c61e2020-03-25 14:26:01 -0400249 canvas->restore();
250 canvas->translate(kPad + SkScalarCeilToScalar(scale*s1.width()), 0);
251 canvas->save();
252 canvas->scale(scale, scale);
Mike Reedd396cd52021-01-23 21:14:47 -0500253 auto s2 = draw(fReferenceImage.get(), sampling);
Brian Salomon5c4c61e2020-03-25 14:26:01 -0400254 canvas->restore();
255 canvas->translate(kPad + SkScalarCeilToScalar(scale*s2.width()), 0);
256 h = std::max({h, s1.height(), s2.height()});
257 }
258 canvas->restore();
259 canvas->translate(0, kPad + SkScalarCeilToScalar(scale*h));
260 }
bsalomon993a4212015-05-29 11:37:25 -0700261 }
262 }
263
264private:
Brian Salomon7db71392020-10-16 10:05:21 -0400265 std::unique_ptr<sk_gpu_test::LazyYUVImage> fLazyYUVImage;
bsalomon993a4212015-05-29 11:37:25 -0700266
Robert Phillipsf105d382020-06-19 14:27:14 -0400267 // 3 draws x 3 scales x 4 filter qualities
Brian Salomon9fa47cc2021-10-08 18:48:26 -0400268 inline static constexpr int kNumImages = 3 * 3 * 4;
Robert Phillipsf105d382020-06-19 14:27:14 -0400269 sk_sp<SkImage> fYUVAImages[kNumImages];
270 sk_sp<SkImage> fReferenceImage;
271
Brian Salomon9fa47cc2021-10-08 18:48:26 -0400272 inline static constexpr SkScalar kPad = 10.0f;
bsalomon993a4212015-05-29 11:37:25 -0700273
John Stiles7571f9e2020-09-02 22:42:33 -0400274 using INHERITED = GM;
bsalomon993a4212015-05-29 11:37:25 -0700275};
276
halcanary385fe4d2015-08-26 13:07:48 -0700277DEF_GM(return new ImageFromYUVTextures;)
John Stilesa6841be2020-08-06 14:11:56 -0400278} // namespace skiagm