blob: 0d448b55457bd424a19f18cabde2d0b61afe4a0d [file] [log] [blame]
Brian Salomon201700f2019-05-17 12:05:44 -04001/*
2 * Copyright 2019 Google LLC
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#include "gm/gm.h"
9#include "include/core/SkCanvas.h"
10#include "include/core/SkColor.h"
11#include "include/core/SkPaint.h"
12#include "include/core/SkRect.h"
13#include "include/core/SkSurface.h"
Brian Salomon7db71392020-10-16 10:05:21 -040014#include "include/core/SkYUVAInfo.h"
15#include "include/core/SkYUVAPixmaps.h"
Brian Salomonbacbb922021-01-21 19:48:00 -050016#include "include/effects/SkGradientShader.h"
Robert Phillips2a4acf32020-07-06 15:50:15 -040017#include "include/gpu/GrDirectContext.h"
Robert Phillipsb7bfbc22020-07-01 12:55:01 -040018#include "include/gpu/GrRecordingContext.h"
Kevin Lubick7a2d1b32023-04-04 09:50:57 -040019#include "include/gpu/ganesh/SkImageGanesh.h"
Kevin Lubick1b3aa8b2023-01-19 14:03:31 -050020#include "src/base/SkScopeExit.h"
Brian Salomon201700f2019-05-17 12:05:44 -040021#include "src/core/SkAutoPixmapStorage.h"
Kevin Lubick8b741882023-10-06 11:41:38 -040022#include "tools/DecodeUtils.h"
Brian Salomon201700f2019-05-17 12:05:44 -040023#include "tools/Resources.h"
24#include "tools/ToolUtils.h"
Brian Salomon7db71392020-10-16 10:05:21 -040025#include "tools/gpu/YUVUtils.h"
Brian Salomon201700f2019-05-17 12:05:44 -040026
Jim Van Verthc1effc02023-06-26 15:04:04 -040027#if defined(SK_GRAPHITE)
28#include "include/gpu/graphite/Context.h"
Jim Van Verthc2118062023-07-06 12:45:33 -040029#include "include/gpu/graphite/Image.h"
Jim Van Verthc1effc02023-06-26 15:04:04 -040030#include "src/gpu/graphite/RecorderPriv.h"
Brian Salomonc759bbf2023-12-05 11:11:27 -050031#include "tools/graphite/GraphiteTestContext.h"
Jim Van Verthc1effc02023-06-26 15:04:04 -040032#endif
33
Brian Salomonc759bbf2023-12-05 11:11:27 -050034#include <variant>
35
Brian Salomon9241a6d2019-10-03 13:26:54 -040036namespace {
Brian Salomonc759bbf2023-12-05 11:11:27 -050037/// We test reading from images and surfaces
Brian Salomon6f9ee612023-07-21 22:09:04 -040038enum class ReadSource {
39 kImage,
40 kSurface,
41};
42
Brian Salomonc759bbf2023-12-05 11:11:27 -050043// We test reading to RGBA, YUV, and YUVA
Brian Salomon6f9ee612023-07-21 22:09:04 -040044enum class Type {
45 kRGBA,
46 kYUV,
47 kYUVA
48};
49
Brian Salomonc759bbf2023-12-05 11:11:27 -050050template <ReadSource> struct SourceS;
51template <> struct SourceS<ReadSource::kImage> { using Type = SkImage; };
52template <> struct SourceS<ReadSource::kSurface> { using Type = SkSurface; };
Brian Salomona34e0fc2019-05-17 19:56:59 +000053
Brian Salomonc759bbf2023-12-05 11:11:27 -050054template <ReadSource RS> using Source = typename SourceS<RS>::Type;
Brian Salomon451b01f2019-05-17 12:39:17 -040055
Brian Salomonc759bbf2023-12-05 11:11:27 -050056// Converts a source image to either an SkImage or SkSurface, backed by GPU if canvas is. Returns
57// kSkip or kFail if the image cannot be converted.
58template <ReadSource RS>
59std::variant<sk_sp<Source<RS>>, skiagm::DrawResult> convert_image_to_source(SkCanvas* canvas,
60 sk_sp<SkImage> image,
61 SkString* errorMsg);
Robert Phillips41fc1742020-10-15 10:04:03 -040062
Brian Salomonc759bbf2023-12-05 11:11:27 -050063template <>
64std::variant<sk_sp<SkImage>, skiagm::DrawResult> convert_image_to_source<ReadSource::kImage>(
65 SkCanvas* canvas,
66 sk_sp<SkImage> image,
67 SkString* errorMsg) {
Jim Van Verthc2118062023-07-06 12:45:33 -040068#if defined(SK_GRAPHITE)
Brian Salomonc759bbf2023-12-05 11:11:27 -050069 if (auto recorder = canvas->recorder()) {
70 image = SkImages::TextureFromImage(recorder, image);
71 if (image) {
72 return image;
73 }
74 *errorMsg = "Could not create Graphite image";
Brian Salomona7e5c7c2020-01-27 15:41:40 -050075 return skiagm::DrawResult::kFail;
76 }
Brian Salomonc759bbf2023-12-05 11:11:27 -050077#endif
Robert Phillips27f283f2020-10-14 11:46:51 -040078 auto dContext = GrAsDirectContext(canvas->recordingContext());
Robert Phillips41fc1742020-10-15 10:04:03 -040079 if (!dContext && canvas->recordingContext()) {
Robert Phillips27f283f2020-10-14 11:46:51 -040080 *errorMsg = "Not supported in DDL mode";
81 return skiagm::DrawResult::kSkip;
82 }
Brian Salomonc759bbf2023-12-05 11:11:27 -050083 if (dContext) {
84 image = SkImages::TextureFromImage(dContext, image);
85 if (image) {
86 return image;
87 }
88 // When testing abandoned GrContext we expect surface creation to fail.
89 if (dContext && dContext->abandoned()) {
90 return skiagm::DrawResult::kSkip;
91 }
92 *errorMsg = "Could not create Ganesh image";
93 return skiagm::DrawResult::kFail;
94 }
95 return image;
96}
Robert Phillips27f283f2020-10-14 11:46:51 -040097
Brian Salomonc759bbf2023-12-05 11:11:27 -050098template <>
99std::variant<sk_sp<SkSurface>, skiagm::DrawResult> convert_image_to_source<ReadSource::kSurface>(
100 SkCanvas* canvas,
101 sk_sp<SkImage> image,
102 SkString* errorMsg) {
103 // Turn the image into a surface in order to call the read and rescale API
104 auto surfInfo = image->imageInfo().makeDimensions(image->dimensions());
105 auto surface = canvas->makeSurface(surfInfo);
106 if (!surface && surfInfo.colorType() == kBGRA_8888_SkColorType) {
107 surfInfo = surfInfo.makeColorType(kRGBA_8888_SkColorType);
108 surface = canvas->makeSurface(surfInfo);
109 }
Brian Salomonaf9b7b92019-05-20 12:19:37 -0400110 if (!surface) {
111 *errorMsg = "Could not create surface for image.";
Brian Salomon9be911c2019-05-20 14:01:21 -0400112 // When testing abandoned GrContext we expect surface creation to fail.
Robert Phillipsd436b782020-07-10 11:49:06 -0400113 if (canvas->recordingContext() && canvas->recordingContext()->abandoned()) {
Brian Salomon9be911c2019-05-20 14:01:21 -0400114 return skiagm::DrawResult::kSkip;
115 }
Brian Salomonaf9b7b92019-05-20 12:19:37 -0400116 return skiagm::DrawResult::kFail;
117 }
Brian Salomonc759bbf2023-12-05 11:11:27 -0500118 SkPaint paint;
119 paint.setBlendMode(SkBlendMode::kSrc);
120 surface->getCanvas()->drawImage(image, 0, 0, SkSamplingOptions(), &paint);
121 return surface;
Brian Salomon286b5572019-05-20 10:25:50 -0400122}
Brian Salomonbacbb922021-01-21 19:48:00 -0500123
Brian Salomonc759bbf2023-12-05 11:11:27 -0500124class AsyncReadGMBase : public skiagm::GM {
125public:
126 AsyncReadGMBase(const char* name) : fName(name) {}
Brian Salomonbacbb922021-01-21 19:48:00 -0500127
Brian Salomonc759bbf2023-12-05 11:11:27 -0500128 SkString getName() const override { return fName; }
Brian Salomonbacbb922021-01-21 19:48:00 -0500129
Brian Salomonc759bbf2023-12-05 11:11:27 -0500130protected:
131 // Does a rescale and read using Graphite, Ganesh, or CPU and returns the result as a pixmap
132 // image.
133 template <ReadSource ReadSource>
134 sk_sp<SkImage> readAndScaleRGBA(Source<ReadSource>* src,
135 SkIRect srcRect,
136 GrDirectContext* direct,
137 skgpu::graphite::Recorder* recorder,
138 const SkImageInfo& ii,
139 SkImage::RescaleGamma rescaleGamma,
140 SkImage::RescaleMode rescaleMode) {
141 auto* asyncContext = new AsyncContext();
142 if (recorder) {
143#if defined(SK_GRAPHITE)
144 skgpu::graphite::Context* graphiteContext = recorder->priv().context();
145 if (!graphiteContext) {
146 return nullptr;
147 }
148 // We need to flush the existing drawing commands before we try to read
149 std::unique_ptr<skgpu::graphite::Recording> recording = recorder->snap();
150 if (!recording) {
151 return nullptr;
152 }
153 skgpu::graphite::InsertRecordingInfo recordingInfo;
154 recordingInfo.fRecording = recording.get();
155 if (!graphiteContext->insertRecording(recordingInfo)) {
156 return nullptr;
157 }
Brian Salomonbacbb922021-01-21 19:48:00 -0500158
Brian Salomonc759bbf2023-12-05 11:11:27 -0500159 graphiteContext->asyncRescaleAndReadPixels(src,
160 ii,
161 srcRect,
162 rescaleGamma,
163 rescaleMode,
164 AsyncCallback,
165 asyncContext);
166 graphiteContext->submit();
167 while (!asyncContext->fCalled) {
168 graphiteContext->checkAsyncWorkCompletion();
169 this->graphiteTestContext()->tick();
170 }
171#endif
172 } else {
173 src->asyncRescaleAndReadPixels(ii,
174 srcRect,
175 rescaleGamma,
176 rescaleMode,
177 AsyncCallback,
Brian Salomonbacbb922021-01-21 19:48:00 -0500178 asyncContext);
Brian Salomonc759bbf2023-12-05 11:11:27 -0500179 if (direct) {
180 direct->submit();
Brian Salomonbacbb922021-01-21 19:48:00 -0500181 }
182 while (!asyncContext->fCalled) {
183 // Only GPU should actually be asynchronous.
Brian Salomonc759bbf2023-12-05 11:11:27 -0500184 SkASSERT(direct);
185 direct->checkAsyncWorkCompletion();
Brian Salomonbacbb922021-01-21 19:48:00 -0500186 }
Brian Salomonc759bbf2023-12-05 11:11:27 -0500187 }
188 if (!asyncContext->fResult) {
189 return nullptr;
190 }
191 SkPixmap pixmap(ii, asyncContext->fResult->data(0), asyncContext->fResult->rowBytes(0));
192 auto releasePixels = [](const void*, void* c) { delete static_cast<AsyncContext*>(c); };
193 return SkImages::RasterFromPixmap(pixmap, releasePixels, asyncContext);
194 }
Brian Salomonbacbb922021-01-21 19:48:00 -0500195
Brian Salomonc759bbf2023-12-05 11:11:27 -0500196 // Does a YUV[A] rescale and read using Graphite or Ganesh (no CPU support) and returns the
197 // result as a YUVA planar texture image.
198 template <ReadSource ReadSource>
199 sk_sp<SkImage> readAndScaleYUVA(Source<ReadSource>* src,
200 SkIRect srcRect,
201 SkISize resultSize,
202 bool readAlpha,
203 GrDirectContext* direct,
204 skgpu::graphite::Recorder* recorder,
205 SkYUVColorSpace yuvCS,
206 SkImage::RescaleGamma rescaleGamma,
207 SkImage::RescaleMode rescaleMode,
208 SkScopeExit* cleanup) {
209 SkASSERT(!(resultSize.width() & 0b1) && !(resultSize.height() & 0b1));
210
211 SkISize uvSize = {resultSize.width() / 2, resultSize.height() / 2};
212 SkImageInfo yaII = SkImageInfo::Make(resultSize, kGray_8_SkColorType, kPremul_SkAlphaType);
213 SkImageInfo uvII = SkImageInfo::Make(uvSize, kGray_8_SkColorType, kPremul_SkAlphaType);
214
215 AsyncContext asyncContext;
216 if (recorder) {
217#if defined(SK_GRAPHITE)
218 skgpu::graphite::Context* graphiteContext = recorder->priv().context();
219 if (!graphiteContext) {
220 return nullptr;
Brian Salomonbacbb922021-01-21 19:48:00 -0500221 }
Brian Salomonc759bbf2023-12-05 11:11:27 -0500222 // We need to flush the existing drawing commands before we try to read
223 std::unique_ptr<skgpu::graphite::Recording> recording = recorder->snap();
224 if (!recording) {
225 return nullptr;
226 }
227 skgpu::graphite::InsertRecordingInfo recordingInfo;
228 recordingInfo.fRecording = recording.get();
229 if (!graphiteContext->insertRecording(recordingInfo)) {
230 return nullptr;
231 }
232
233 if (readAlpha) {
234 graphiteContext->asyncRescaleAndReadPixelsYUVA420(src,
235 yuvCS,
236 SkColorSpace::MakeSRGB(),
237 srcRect,
238 resultSize,
239 rescaleGamma,
240 rescaleMode,
241 AsyncCallback,
242 &asyncContext);
243 } else {
244 graphiteContext->asyncRescaleAndReadPixelsYUV420(src,
245 yuvCS,
246 SkColorSpace::MakeSRGB(),
247 srcRect,
248 resultSize,
249 rescaleGamma,
250 rescaleMode,
251 AsyncCallback,
252 &asyncContext);
253 }
254 graphiteContext->submit();
255 while (!asyncContext.fCalled) {
256 graphiteContext->checkAsyncWorkCompletion();
257 graphiteTestContext()->tick();
258 }
259#endif
260 } else {
261 if (readAlpha) {
262 src->asyncRescaleAndReadPixelsYUVA420(yuvCS,
263 SkColorSpace::MakeSRGB(),
264 srcRect,
265 resultSize,
266 rescaleGamma,
267 rescaleMode,
268 AsyncCallback,
269 &asyncContext);
270 } else {
271 src->asyncRescaleAndReadPixelsYUV420(yuvCS,
272 SkColorSpace::MakeSRGB(),
273 srcRect,
274 resultSize,
275 rescaleGamma,
276 rescaleMode,
277 AsyncCallback,
278 &asyncContext);
279 }
280 if (direct) {
281 direct->submit();
282 }
283 while (!asyncContext.fCalled) {
284 // Only GPU should actually be asynchronous.
285 SkASSERT(direct);
286 direct->checkAsyncWorkCompletion();
287 }
288 }
289 if (!asyncContext.fResult) {
290 return nullptr;
291 }
292 auto planeConfig = readAlpha ? SkYUVAInfo::PlaneConfig::kY_U_V_A :
293 SkYUVAInfo::PlaneConfig::kY_U_V;
294 SkYUVAInfo yuvaInfo(resultSize,
295 planeConfig,
296 SkYUVAInfo::Subsampling::k420,
297 yuvCS);
298 SkPixmap yuvPMs[4] = {
299 {yaII, asyncContext.fResult->data(0), asyncContext.fResult->rowBytes(0)},
300 {uvII, asyncContext.fResult->data(1), asyncContext.fResult->rowBytes(1)},
301 {uvII, asyncContext.fResult->data(2), asyncContext.fResult->rowBytes(2)},
302 {},
303 };
304 if (readAlpha) {
305 yuvPMs[3] = {yaII, asyncContext.fResult->data(3), asyncContext.fResult->rowBytes(3)};
306 }
307 auto pixmaps = SkYUVAPixmaps::FromExternalPixmaps(yuvaInfo, yuvPMs);
308 SkASSERT(pixmaps.isValid());
309 auto lazyYUVImage = sk_gpu_test::LazyYUVImage::Make(pixmaps);
310 SkASSERT(lazyYUVImage);
311#if defined(SK_GRAPHITE)
312 if (recorder) {
313 return lazyYUVImage->refImage(recorder, sk_gpu_test::LazyYUVImage::Type::kFromTextures);
314 } else
315#endif
316 {
317 return lazyYUVImage->refImage(direct, sk_gpu_test::LazyYUVImage::Type::kFromTextures);
318 }
319 }
320
321 // Draws a 3x2 grid of rescales. The columns are none, low, and high filter quality. The rows
322 // are rescale in src gamma and rescale in linear gamma.
323 template <ReadSource ReadSource>
324 skiagm::DrawResult drawRescaleGrid(SkCanvas* canvas,
325 Source<ReadSource>* src,
326 SkIRect srcRect,
327 SkISize readSize,
328 Type type,
329 SkString* errorMsg,
330 int pad = 0) {
331 SkASSERT(canvas->imageInfo().colorType() != kUnknown_SkColorType);
332
333 auto direct = GrAsDirectContext(canvas->recordingContext());
334 auto recorder = canvas->recorder();
335 SkASSERT(direct || !canvas->recordingContext());
336
337 SkYUVColorSpace yuvColorSpace = kRec601_SkYUVColorSpace;
338 canvas->save();
339 for (auto gamma : {SkImage::RescaleGamma::kSrc, SkImage::RescaleGamma::kLinear}) {
340 canvas->save();
341 for (auto mode : {SkImage::RescaleMode::kNearest,
342 SkImage::RescaleMode::kRepeatedLinear,
343 SkImage::RescaleMode::kRepeatedCubic}) {
344 SkScopeExit cleanup;
345 sk_sp<SkImage> result;
346 switch (type) {
347 case Type::kRGBA: {
348 const auto ii = canvas->imageInfo().makeDimensions(readSize);
349 result = readAndScaleRGBA<ReadSource>(src,
350 srcRect,
351 direct,
352 recorder,
353 ii,
354 gamma,
355 mode);
356 if (!result) {
357 errorMsg->printf("async read call failed.");
358 return skiagm::DrawResult::kFail;
359 }
360 break;
361 }
362 case Type::kYUV:
363 case Type::kYUVA:
364 result = readAndScaleYUVA<ReadSource>(src,
365 srcRect,
366 readSize,
367 /*readAlpha=*/type == Type::kYUVA,
368 direct,
369 recorder,
370 yuvColorSpace,
371 gamma,
372 mode,
373 &cleanup);
374 if (!result) {
375 errorMsg->printf("YUV[A]420 async call failed. Allowed for now.");
376 return skiagm::DrawResult::kSkip;
377 }
378 int nextCS = static_cast<int>(yuvColorSpace + 1) %
379 (kLastEnum_SkYUVColorSpace + 1);
380 yuvColorSpace = static_cast<SkYUVColorSpace>(nextCS);
381 break;
382 }
383 canvas->drawImage(result, 0, 0);
384 canvas->translate(readSize.width() + pad, 0);
385 }
386 canvas->restore();
387 canvas->translate(0, readSize.height() + pad);
Brian Salomonbacbb922021-01-21 19:48:00 -0500388 }
389 canvas->restore();
Brian Salomonc759bbf2023-12-05 11:11:27 -0500390 return skiagm::DrawResult::kOk;
Brian Salomonbacbb922021-01-21 19:48:00 -0500391 }
Brian Salomonc759bbf2023-12-05 11:11:27 -0500392
393private:
394 struct AsyncContext {
395 bool fCalled = false;
396 std::unique_ptr<const SkImage::AsyncReadResult> fResult;
397 };
398
399 // Making this a lambda in the test functions caused:
400 // "error: cannot compile this forwarded non-trivially copyable parameter yet"
401 // on x86/Win/Clang bot, referring to 'result'.
402 static void AsyncCallback(void* c, std::unique_ptr<const SkImage::AsyncReadResult> result) {
403 auto context = static_cast<AsyncContext*>(c);
404 context->fResult = std::move(result);
405 context->fCalled = true;
406 }
407
408 SkString fName;
409};
410
411template <ReadSource ReadSource, Type Type>
412class AsyncRescaleAndReadGridGM : public AsyncReadGMBase {
413public:
414 AsyncRescaleAndReadGridGM(const char* name,
415 const char* imageFile,
416 SkIRect srcRect,
417 SkISize readSize)
418 : AsyncReadGMBase(name)
419 , fImageFile(imageFile)
420 , fSrcRect(srcRect)
421 , fReadSize(readSize) {}
422
423 DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override {
424 ToolUtils::draw_checkerboard(canvas, SK_ColorDKGRAY, SK_ColorLTGRAY, 25);
425 auto image = ToolUtils::GetResourceAsImage(fImageFile.c_str());
426 if (!image) {
427 errorMsg->printf("Could not load image file %s.", fImageFile.c_str());
428 return skiagm::DrawResult::kFail;
429 }
430 if (canvas->imageInfo().colorType() == kUnknown_SkColorType) {
431 *errorMsg = "Not supported on recording/vector backends.";
432 return skiagm::DrawResult::kSkip;
433 }
434
435 auto sourceOrResult = convert_image_to_source<ReadSource>(canvas, image, errorMsg);
436 if (auto dr = std::get_if<skiagm::DrawResult>(&sourceOrResult)) {
437 return *dr;
438 }
439
440 using Src = sk_sp<Source<ReadSource>>;
441 return drawRescaleGrid<ReadSource>(canvas,
442 std::get<Src>(sourceOrResult).get(),
443 fSrcRect,
444 fReadSize,
445 Type,
446 errorMsg);
447 }
448
449 SkISize getISize() override { return {3 * fReadSize.width(), 2 * fReadSize.height()}; }
450
451private:
452 SkString fImageFile;
453 SkIRect fSrcRect;
454 SkISize fReadSize;
455};
456} // anonymous namespace
457
458#define DEF_RESCALE_AND_READ_GRID_GM(IMAGE_FILE, TAG, SRC_RECT, W, H, SOURCE, TYPE) \
459 DEF_GM(return new (AsyncRescaleAndReadGridGM<SOURCE, TYPE>)( \
460 "async_rescale_and_read_" #TAG, #IMAGE_FILE, SRC_RECT, SkISize{W, H});)
461
462DEF_RESCALE_AND_READ_GRID_GM(images/yellow_rose.webp,
463 yuv420_rose,
464 SkIRect::MakeXYWH(50, 5, 200, 150),
465 410,
466 376,
467 ReadSource::kSurface,
468 Type::kYUVA)
469
470DEF_RESCALE_AND_READ_GRID_GM(images/yellow_rose.webp,
471 yuv420_rose_down,
472 SkIRect::MakeXYWH(50, 5, 200, 150),
473 106,
474 60,
475 ReadSource::kImage,
476 Type::kYUV)
477
478DEF_RESCALE_AND_READ_GRID_GM(images/yellow_rose.webp,
479 rose,
480 SkIRect::MakeXYWH(100, 20, 100, 100),
481 410,
482 410,
483 ReadSource::kSurface,
484 Type::kRGBA)
485
486DEF_RESCALE_AND_READ_GRID_GM(images/dog.jpg,
487 dog_down,
488 SkIRect::MakeXYWH(0, 10, 180, 150),
489 45,
490 45,
491 ReadSource::kSurface,
492 Type::kRGBA)
493
494DEF_RESCALE_AND_READ_GRID_GM(images/dog.jpg,
495 dog_up,
496 SkIRect::MakeWH(180, 180),
497 800,
498 400,
499 ReadSource::kImage,
500 Type::kRGBA)
501
502DEF_RESCALE_AND_READ_GRID_GM(images/text.png,
503 text_down,
504 SkIRect::MakeWH(637, 105),
505 (int)(0.7 * 637),
506 (int)(0.7 * 105),
507 ReadSource::kImage,
508 Type::kRGBA)
509
510DEF_RESCALE_AND_READ_GRID_GM(images/text.png,
511 text_up,
512 SkIRect::MakeWH(637, 105),
513 (int)(1.2 * 637),
514 (int)(1.2 * 105),
515 ReadSource::kSurface,
516 Type::kRGBA)
517
518DEF_RESCALE_AND_READ_GRID_GM(images/text.png,
519 text_up_large,
520 SkIRect::MakeXYWH(300, 0, 300, 105),
521 (int)(2.4 * 300),
522 (int)(2.4 * 105),
523 ReadSource::kImage,
524 Type::kRGBA)
525
526namespace {
527class AyncYUVNoScaleGM : public AsyncReadGMBase {
528public:
529 AyncYUVNoScaleGM() : AsyncReadGMBase("async_yuv_no_scale") {}
530 DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override {
531 auto surface = canvas->getSurface();
532 if (!surface) {
533 *errorMsg = "Not supported on recording/vector backends.";
534 return skiagm::DrawResult::kSkip;
535 }
536
537 auto dContext = GrAsDirectContext(surface->recordingContext());
538 if (!dContext && surface->recordingContext()) {
539 *errorMsg = "Not supported in DDL mode";
540 return skiagm::DrawResult::kSkip;
541 }
542
543 auto image = ToolUtils::GetResourceAsImage("images/yellow_rose.webp");
544 if (!image) {
545 return skiagm::DrawResult::kFail;
546 }
547 SkPaint paint;
548 canvas->drawImage(image.get(), 0, 0);
549
550 skgpu::graphite::Recorder* recorder = canvas->recorder();
551 SkScopeExit scopeExit;
552 auto yuvImage = readAndScaleYUVA<ReadSource::kSurface>(surface,
553 SkIRect::MakeWH(400, 300),
554 SkISize{400, 300},
555 /*readAlpha=*/false,
556 dContext,
557 recorder,
558 kRec601_SkYUVColorSpace,
559 SkImage::RescaleGamma::kSrc,
560 SkImage::RescaleMode::kNearest,
561 &scopeExit);
562
563 canvas->clear(SK_ColorWHITE);
564 canvas->drawImage(yuvImage.get(), 0, 0);
565
566 return skiagm::DrawResult::kOk;
567 }
568 SkISize getISize() override { return {400, 300}; }
569};
570} // namespace
571
572DEF_GM(return new AyncYUVNoScaleGM();)
573
574namespace {
575class AsyncRescaleAndReadNoBleedGM : public AsyncReadGMBase {
576public:
577 AsyncRescaleAndReadNoBleedGM() : AsyncReadGMBase("async_rescale_and_read_no_bleed") {}
578
579 SkISize getISize() override { return {60, 60}; }
580
581 DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override {
582 if (canvas->imageInfo().colorType() == kUnknown_SkColorType) {
583 *errorMsg = "Not supported on recording/vector backends.";
584 return skiagm::DrawResult::kSkip;
585 }
586
587 auto dContext = GrAsDirectContext(canvas->recordingContext());
588 if (!dContext && canvas->recordingContext()) {
589 *errorMsg = "Not supported in DDL mode";
590 return skiagm::DrawResult::kSkip;
591 }
592
593 static constexpr int kBorder = 5;
594 static constexpr int kInner = 5;
595 const auto srcRect = SkIRect::MakeXYWH(kBorder, kBorder, kInner, kInner);
596 auto surfaceII = SkImageInfo::Make(kInner + 2 * kBorder,
597 kInner + 2 * kBorder,
598 kRGBA_8888_SkColorType,
599 kPremul_SkAlphaType,
600 SkColorSpace::MakeSRGB());
601 auto surface = canvas->makeSurface(surfaceII);
602 if (!surface) {
603 *errorMsg = "Could not create surface for image.";
604 // When testing abandoned GrContext we expect surface creation to fail.
605 if (canvas->recordingContext() && canvas->recordingContext()->abandoned()) {
606 return skiagm::DrawResult::kSkip;
607 }
608 return skiagm::DrawResult::kFail;
609 }
610 surface->getCanvas()->clear(SK_ColorRED);
611 surface->getCanvas()->save();
612 surface->getCanvas()->clipRect(SkRect::Make(srcRect), SkClipOp::kIntersect, false);
613 surface->getCanvas()->clear(SK_ColorBLUE);
614 surface->getCanvas()->restore();
615 static constexpr int kPad = 2;
616 canvas->translate(kPad, kPad);
617 skiagm::DrawResult result;
618 SkISize downSize = {static_cast<int>(kInner / 2), static_cast<int>(kInner / 2)};
619 result = drawRescaleGrid<ReadSource::kSurface>(canvas,
620 surface.get(),
621 srcRect,
622 downSize,
623 Type::kRGBA,
624 errorMsg,
625 kPad);
626 if (result != skiagm::DrawResult::kOk) {
627 return result;
628 }
629 canvas->translate(0, 4 * downSize.height());
630 SkISize upSize = {static_cast<int>(kInner * 3.5), static_cast<int>(kInner * 4.6)};
631 result = drawRescaleGrid<ReadSource::kSurface>(canvas,
632 surface.get(),
633 srcRect,
634 upSize,
635 Type::kRGBA,
636 errorMsg,
637 kPad);
638 if (result != skiagm::DrawResult::kOk) {
639 return result;
640 }
641 return skiagm::DrawResult::kOk;
642 }
643};
644} // namespace
645
646DEF_GM(return new AsyncRescaleAndReadNoBleedGM();)
647
648namespace {
649class AsyncRescaleAndReadAlphaTypeGM : public AsyncReadGMBase {
650public:
651 AsyncRescaleAndReadAlphaTypeGM() : AsyncReadGMBase("async_rescale_and_read_alpha_type") {}
652
653 SkISize getISize() override { return {512, 512}; }
654
655 DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override {
656 auto dContext = GrAsDirectContext(canvas->recordingContext());
657 if (!dContext && canvas->recordingContext()) {
658 *errorMsg = "Not supported in DDL mode";
659 return skiagm::DrawResult::kSkip;
660 }
661
662 if (canvas->recorder()) {
663 *errorMsg = "Reading to unpremul not supported in Graphite.";
664 return skiagm::DrawResult::kSkip;
665 }
666
667 auto upmII = SkImageInfo::Make(200, 200, kRGBA_8888_SkColorType, kUnpremul_SkAlphaType);
668
669 auto pmII = upmII.makeAlphaType(kPremul_SkAlphaType);
670
671 auto upmSurf = SkSurfaces::Raster(upmII);
672 auto pmSurf = SkSurfaces::Raster(pmII);
673
674 SkColor4f colors[] = {
675 {.3f, .3f, .3f, .3f},
676 {1.f, .2f, .6f, .9f},
677 {0.f, .1f, 1.f, .1f},
678 {.7f, .8f, .2f, .7f},
679 };
680 auto shader = SkGradientShader::MakeRadial({100, 100},
681 230,
682 colors,
683 nullptr,
684 nullptr,
685 std::size(colors),
686 SkTileMode::kRepeat);
687 SkPaint paint;
688 paint.setShader(std::move(shader));
689
690 upmSurf->getCanvas()->drawPaint(paint);
691 pmSurf ->getCanvas()->drawPaint(paint);
692
693 auto pmImg = pmSurf->makeImageSnapshot();
694 auto upmImg = upmSurf->makeImageSnapshot();
695
696 auto imageOrResult = convert_image_to_source<ReadSource::kImage>(canvas,
697 std::move(pmImg),
698 errorMsg);
699 if (const auto* dr = std::get_if<skiagm::DrawResult>(&imageOrResult)) {
700 return *dr;
701 }
702 pmImg = std::move(std::get<sk_sp<SkImage>>(imageOrResult));
703
704 imageOrResult = convert_image_to_source<ReadSource::kImage>(canvas,
705 std::move(upmImg),
706 errorMsg);
707 if (const auto* dr = std::get_if<skiagm::DrawResult>(&imageOrResult)) {
708 return *dr;
709 }
710 upmImg = std::move(std::get<sk_sp<SkImage>>(imageOrResult));
711
712 int size = 256;
713
714 ToolUtils::draw_checkerboard(canvas, SK_ColorWHITE, SK_ColorBLACK, 32);
715
716 for (const auto& img : {pmImg, upmImg}) {
717 canvas->save();
718 for (auto readAT : {kPremul_SkAlphaType, kUnpremul_SkAlphaType}) {
719 auto readInfo = img->imageInfo().makeAlphaType(readAT).makeWH(size, size);
720 auto result =
721 readAndScaleRGBA<ReadSource::kImage>(img.get(),
722 SkIRect::MakeSize(img->dimensions()),
723 dContext,
724 canvas->recorder(),
725 readInfo,
726 SkImage::RescaleGamma::kSrc,
727 SkImage::RescaleMode::kRepeatedCubic);
728 if (!result) {
729 *errorMsg = "async readback failed";
730 return skiagm::DrawResult::kFail;
731 }
732 canvas->drawImage(result, 0, 0);
733 canvas->translate(size, 0);
734 }
735 canvas->restore();
736 canvas->translate(0, size);
737 }
738 return skiagm::DrawResult::kOk;
739 }
740};
741} // namespace
742
743DEF_GM(return new AsyncRescaleAndReadAlphaTypeGM();)