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