• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 #include "dm/DMSrcSink.h"
9 #include "include/codec/SkAndroidCodec.h"
10 #include "include/codec/SkCodec.h"
11 #include "include/codec/SkPixmapUtils.h"
12 #include "include/core/SkColorSpace.h"
13 #include "include/core/SkData.h"
14 #include "include/core/SkDocument.h"
15 #include "include/core/SkExecutor.h"
16 #include "include/core/SkImageGenerator.h"
17 #include "include/core/SkMallocPixelRef.h"
18 #include "include/core/SkPictureRecorder.h"
19 #include "include/core/SkSerialProcs.h"
20 #include "include/core/SkStream.h"
21 #include "include/core/SkString.h"
22 #include "include/core/SkSurface.h"
23 #include "include/core/SkSurfaceProps.h"
24 #include "include/docs/SkMultiPictureDocument.h"
25 #include "include/docs/SkPDFDocument.h"
26 #include "include/docs/SkPDFJpegHelpers.h"
27 #include "include/encode/SkPngEncoder.h"
28 #include "include/gpu/ganesh/GrBackendSurface.h"
29 #include "include/gpu/ganesh/GrDirectContext.h"
30 #include "include/gpu/ganesh/SkImageGanesh.h"
31 #include "include/gpu/ganesh/SkSurfaceGanesh.h"
32 #include "include/private/base/SkTLogic.h"
33 #include "include/private/chromium/GrDeferredDisplayList.h"
34 #include "include/private/chromium/GrSurfaceCharacterization.h"
35 #include "include/utils/SkNullCanvas.h"
36 #include "include/utils/SkPaintFilterCanvas.h"
37 #include "modules/skcms/skcms.h"
38 #include "modules/skottie/utils/SkottieUtils.h"
39 #include "modules/skshaper/utils/FactoryHelpers.h"
40 #include "src/base/SkAutoMalloc.h"
41 #include "src/base/SkRandom.h"
42 #include "src/base/SkTLazy.h"
43 #include "src/codec/SkCodecImageGenerator.h"
44 #include "src/core/SkAutoPixmapStorage.h"
45 #include "src/core/SkImageInfoPriv.h"
46 #include "src/core/SkOSFile.h"
47 #include "src/core/SkPictureData.h"
48 #include "src/core/SkPicturePriv.h"
49 #include "src/core/SkRecordDraw.h"
50 #include "src/core/SkRecorder.h"
51 #include "src/core/SkSwizzlePriv.h"
52 #include "src/core/SkTaskGroup.h"
53 #include "src/gpu/ganesh/GrDirectContextPriv.h"
54 #include "src/gpu/ganesh/GrGpu.h"
55 #include "src/gpu/ganesh/image/GrImageUtils.h"
56 #include "src/image/SkImage_Base.h"
57 #include "src/utils/SkJSONWriter.h"
58 #include "src/utils/SkMultiPictureDocumentPriv.h"
59 #include "src/utils/SkOSPath.h"
60 #include "tools/DDLPromiseImageHelper.h"
61 #include "tools/DDLTileHelper.h"
62 #include "tools/EncodeUtils.h"
63 #include "tools/GpuToolUtils.h"
64 #include "tools/Resources.h"
65 #include "tools/RuntimeBlendUtils.h"
66 #include "tools/ToolUtils.h"
67 #include "tools/UrlDataManager.h"
68 #include "tools/debugger/DebugCanvas.h"
69 #include "tools/fonts/FontToolUtils.h"
70 #include "tools/gpu/BackendSurfaceFactory.h"
71 #include "tools/gpu/MemoryCache.h"
72 #include "tools/gpu/TestCanvas.h"
73 
74 #if defined(SK_BUILD_FOR_ANDROID)
75 #include "include/ports/SkImageGeneratorNDK.h"
76 #endif
77 
78 #if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
79 #include "include/ports/SkImageGeneratorCG.h"
80 #endif
81 
82 #if defined(SK_BUILD_FOR_WIN)
83 #include "include/docs/SkXPSDocument.h"
84 #include "include/ports/SkImageGeneratorWIC.h"
85 #include "src/utils/win/SkAutoCoInitialize.h"
86 #include "src/utils/win/SkHRESULT.h"
87 #include "src/utils/win/SkTScopedComPtr.h"
88 
89 #include <XpsObjectModel.h>
90 #endif
91 
92 #if defined(SK_ENABLE_SKOTTIE)
93 #include "modules/skottie/include/Skottie.h"
94 #include "modules/skresources/include/SkResources.h"
95 #endif
96 
97 #if defined(SK_ENABLE_SVG)
98 #include "include/svg/SkSVGCanvas.h"
99 #include "modules/svg/include/SkSVGDOM.h"
100 #include "modules/svg/include/SkSVGNode.h"
101 #include "src/xml/SkXMLWriter.h"
102 #endif
103 
104 #if defined(SK_GRAPHITE)
105 #include "include/gpu/graphite/Context.h"
106 #include "include/gpu/graphite/ContextOptions.h"
107 #include "include/gpu/graphite/Recorder.h"
108 #include "include/gpu/graphite/Recording.h"
109 #include "include/gpu/graphite/Surface.h"
110 #include "src/gpu/graphite/ContextOptionsPriv.h"
111 // TODO: Remove this src include once we figure out public readPixels call for Graphite.
112 #include "src/gpu/graphite/Surface_Graphite.h"
113 #include "tools/graphite/ContextFactory.h"
114 #include "tools/graphite/GraphiteTestContext.h"
115 #include "tools/graphite/GraphiteToolUtils.h"
116 
117 #if defined(SK_ENABLE_PRECOMPILE)
118 #include "src/gpu/graphite/Caps.h"
119 #include "src/gpu/graphite/ContextPriv.h"
120 #include "src/gpu/graphite/GraphicsPipeline.h"
121 #include "src/gpu/graphite/GraphicsPipelineDesc.h"
122 #include "src/gpu/graphite/PrecompileContextPriv.h"
123 #include "src/gpu/graphite/RecorderPriv.h"
124 #include "src/gpu/graphite/RenderPassDesc.h"
125 #include "src/gpu/graphite/RendererProvider.h"
126 #include "tools/graphite/UniqueKeyUtils.h"
127 #include "tools/graphite/precompile/PipelineCallbackHandler.h"
128 #endif // SK_ENABLE_PRECOMPILE
129 
130 #endif // SK_GRAPHITE
131 
132 
133 #if defined(SK_ENABLE_ANDROID_UTILS)
134     #include "client_utils/android/BitmapRegionDecoder.h"
135 #endif
136 
137 #if !defined(SK_DISABLE_LEGACY_TESTS)
138     #include "tests/TestUtils.h"
139 #endif
140 
141 #include <cmath>
142 #include <functional>
143 
144 using namespace skia_private;
145 
146 static DEFINE_bool(RAW_threading, true, "Allow RAW decodes to run on multiple threads?");
147 static DEFINE_int(mskpFrame, 0, "Which MSKP frame to draw?");
148 
149 DECLARE_int(gpuThreads);
150 
151 using sk_gpu_test::GrContextFactory;
152 using sk_gpu_test::ContextInfo;
153 
154 namespace DM {
155 
GMSrc(skiagm::GMFactory factory)156 GMSrc::GMSrc(skiagm::GMFactory factory) : fFactory(factory) {}
157 
draw(SkCanvas * canvas,GraphiteTestContext * testContext) const158 Result GMSrc::draw(SkCanvas* canvas, GraphiteTestContext* testContext) const {
159     std::unique_ptr<skiagm::GM> gm(fFactory());
160     if (gm->isBazelOnly()) {
161         // We skip Bazel-only GMs because they might overlap with existing DM functionality. See
162         // comments in the skiagm::GM::isBazelOnly function declaration for context.
163         return Result(Result::Status::Skip, SkString("Bazel-only GM"));
164     }
165     SkString msg;
166 
167     skiagm::DrawResult gpuSetupResult = gm->gpuSetup(canvas, &msg, testContext);
168     switch (gpuSetupResult) {
169         case skiagm::DrawResult::kOk  : break;
170         case skiagm::DrawResult::kFail: return Result(Result::Status::Fatal, msg);
171         case skiagm::DrawResult::kSkip: return Result(Result::Status::Skip,  msg);
172         default: SK_ABORT("");
173     }
174 
175     skiagm::DrawResult drawResult = gm->draw(canvas, &msg);
176     switch (drawResult) {
177         case skiagm::DrawResult::kOk  : return Result(Result::Status::Ok,    msg);
178         case skiagm::DrawResult::kFail: return Result(Result::Status::Fatal, msg);
179         case skiagm::DrawResult::kSkip: return Result(Result::Status::Skip,  msg);
180         default: SK_ABORT("");
181     }
182 
183     // Note: we don't call "gpuTeardown" here because, when testing DDL recording, we want
184     // the gpu-backed images to live past the lifetime of the GM.
185 }
186 
size() const187 SkISize GMSrc::size() const {
188     std::unique_ptr<skiagm::GM> gm(fFactory());
189     return gm->getISize();
190 }
191 
name() const192 Name GMSrc::name() const {
193     std::unique_ptr<skiagm::GM> gm(fFactory());
194     return gm->getName();
195 }
196 
modifyGrContextOptions(GrContextOptions * options) const197 void GMSrc::modifyGrContextOptions(GrContextOptions* options) const {
198     std::unique_ptr<skiagm::GM> gm(fFactory());
199     gm->modifyGrContextOptions(options);
200 }
201 
202 #if defined(SK_GRAPHITE)
modifyGraphiteContextOptions(skgpu::graphite::ContextOptions * options) const203 void GMSrc::modifyGraphiteContextOptions(skgpu::graphite::ContextOptions* options) const {
204     std::unique_ptr<skiagm::GM> gm(fFactory());
205     gm->modifyGraphiteContextOptions(options);
206 }
207 #endif
208 
209 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
210 
get_scaled_name(const Path & path,float scale)211 static SkString get_scaled_name(const Path& path, float scale) {
212     return SkStringPrintf("%s_%.3f", SkOSPath::Basename(path.c_str()).c_str(), scale);
213 }
214 
215 #ifdef SK_ENABLE_ANDROID_UTILS
BRDSrc(Path path,Mode mode,CodecSrc::DstColorType dstColorType,uint32_t sampleSize)216 BRDSrc::BRDSrc(Path path, Mode mode, CodecSrc::DstColorType dstColorType, uint32_t sampleSize)
217     : fPath(path)
218     , fMode(mode)
219     , fDstColorType(dstColorType)
220     , fSampleSize(sampleSize)
221 {}
222 
veto(SinkFlags flags) const223 bool BRDSrc::veto(SinkFlags flags) const {
224     // No need to test to non-raster or indirect backends.
225     return flags.type != SinkFlags::kRaster
226         || flags.approach != SinkFlags::kDirect;
227 }
228 
create_brd(Path path)229 static std::unique_ptr<android::skia::BitmapRegionDecoder> create_brd(Path path) {
230     sk_sp<SkData> encoded(SkData::MakeFromFileName(path.c_str()));
231     return android::skia::BitmapRegionDecoder::Make(encoded);
232 }
233 
alpha8_to_gray8(SkBitmap * bitmap)234 static inline void alpha8_to_gray8(SkBitmap* bitmap) {
235     // Android requires kGray8 bitmaps to be tagged as kAlpha8.  Here we convert
236     // them back to kGray8 so our test framework can draw them correctly.
237     if (kAlpha_8_SkColorType == bitmap->info().colorType()) {
238         SkImageInfo newInfo = bitmap->info().makeColorType(kGray_8_SkColorType)
239                                             .makeAlphaType(kOpaque_SkAlphaType);
240         *const_cast<SkImageInfo*>(&bitmap->info()) = newInfo;
241     }
242 }
243 
draw(SkCanvas * canvas,GraphiteTestContext *) const244 Result BRDSrc::draw(SkCanvas* canvas, GraphiteTestContext*) const {
245     SkColorType colorType = canvas->imageInfo().colorType();
246     if (kRGB_565_SkColorType == colorType &&
247         CodecSrc::kGetFromCanvas_DstColorType != fDstColorType)
248     {
249         return Result::Skip("Testing non-565 to 565 is uninteresting.");
250     }
251     switch (fDstColorType) {
252         case CodecSrc::kGetFromCanvas_DstColorType:
253             break;
254         case CodecSrc::kGrayscale_Always_DstColorType:
255             colorType = kGray_8_SkColorType;
256             break;
257         default:
258             SkASSERT(false);
259             break;
260     }
261 
262     auto brd = create_brd(fPath);
263     if (nullptr == brd) {
264         return Result::Skip("Could not create brd for %s.", fPath.c_str());
265     }
266 
267     auto recommendedCT = brd->computeOutputColorType(colorType);
268     if (kRGB_565_SkColorType == colorType && recommendedCT != colorType) {
269         return Result::Skip("Skip decoding non-opaque to 565.");
270     }
271     colorType = recommendedCT;
272 
273     auto colorSpace = brd->computeOutputColorSpace(colorType, nullptr);
274 
275     const uint32_t width = brd->width();
276     const uint32_t height = brd->height();
277     // Visually inspecting very small output images is not necessary.
278     if ((width / fSampleSize <= 10 || height / fSampleSize <= 10) && 1 != fSampleSize) {
279         return Result::Skip("Scaling very small images is uninteresting.");
280     }
281     switch (fMode) {
282         case kFullImage_Mode: {
283             SkBitmap bitmap;
284             if (!brd->decodeRegion(&bitmap, nullptr, SkIRect::MakeXYWH(0, 0, width, height),
285                     fSampleSize, colorType, false, colorSpace)) {
286                 return Result::Fatal("Cannot decode (full) region.");
287             }
288             alpha8_to_gray8(&bitmap);
289 
290             canvas->drawImage(bitmap.asImage(), 0, 0);
291             return Result::Ok();
292         }
293         case kDivisor_Mode: {
294             const uint32_t divisor = 2;
295             if (width < divisor || height < divisor) {
296                 return Result::Skip("Divisor is larger than image dimension.");
297             }
298 
299             // Use a border to test subsets that extend outside the image.
300             // We will not allow the border to be larger than the image dimensions.  Allowing
301             // these large borders causes off by one errors that indicate a problem with the
302             // test suite, not a problem with the implementation.
303             const uint32_t maxBorder = std::min(width, height) / (fSampleSize * divisor);
304             const uint32_t scaledBorder = std::min(5u, maxBorder);
305             const uint32_t unscaledBorder = scaledBorder * fSampleSize;
306 
307             // We may need to clear the canvas to avoid uninitialized memory.
308             // Assume we are scaling a 780x780 image with sampleSize = 8.
309             // The output image should be 97x97.
310             // Each subset will be 390x390.
311             // Each scaled subset be 48x48.
312             // Four scaled subsets will only fill a 96x96 image.
313             // The bottom row and last column will not be touched.
314             // This is an unfortunate result of our rounding rules when scaling.
315             // Maybe we need to consider testing scaled subsets without trying to
316             // combine them to match the full scaled image?  Or maybe this is the
317             // best we can do?
318             canvas->clear(0);
319 
320             for (uint32_t x = 0; x < divisor; x++) {
321                 for (uint32_t y = 0; y < divisor; y++) {
322                     // Calculate the subset dimensions
323                     uint32_t subsetWidth = width / divisor;
324                     uint32_t subsetHeight = height / divisor;
325                     const int left = x * subsetWidth;
326                     const int top = y * subsetHeight;
327 
328                     // Increase the size of the last subset in each row or column, when the
329                     // divisor does not divide evenly into the image dimensions
330                     subsetWidth += (x + 1 == divisor) ? (width % divisor) : 0;
331                     subsetHeight += (y + 1 == divisor) ? (height % divisor) : 0;
332 
333                     // Increase the size of the subset in order to have a border on each side
334                     const int decodeLeft = left - unscaledBorder;
335                     const int decodeTop = top - unscaledBorder;
336                     const uint32_t decodeWidth = subsetWidth + unscaledBorder * 2;
337                     const uint32_t decodeHeight = subsetHeight + unscaledBorder * 2;
338                     SkBitmap bitmap;
339                     if (!brd->decodeRegion(&bitmap, nullptr, SkIRect::MakeXYWH(decodeLeft,
340                             decodeTop, decodeWidth, decodeHeight), fSampleSize, colorType, false,
341                             colorSpace)) {
342                         return Result::Fatal("Cannot decode region.");
343                     }
344 
345                     alpha8_to_gray8(&bitmap);
346                     canvas->drawImageRect(bitmap.asImage().get(),
347                             SkRect::MakeXYWH((SkScalar) scaledBorder, (SkScalar) scaledBorder,
348                                     (SkScalar) (subsetWidth / fSampleSize),
349                                     (SkScalar) (subsetHeight / fSampleSize)),
350                             SkRect::MakeXYWH((SkScalar) (left / fSampleSize),
351                                     (SkScalar) (top / fSampleSize),
352                                     (SkScalar) (subsetWidth / fSampleSize),
353                                     (SkScalar) (subsetHeight / fSampleSize)),
354                             SkSamplingOptions(), nullptr,
355                             SkCanvas::kStrict_SrcRectConstraint);
356                 }
357             }
358             return Result::Ok();
359         }
360         default:
361             SkASSERT(false);
362             return Result::Fatal("Error: Should not be reached.");
363     }
364 }
365 
size() const366 SkISize BRDSrc::size() const {
367     auto brd = create_brd(fPath);
368     if (brd) {
369         return {std::max(1, brd->width() / (int)fSampleSize),
370                 std::max(1, brd->height() / (int)fSampleSize)};
371     }
372     return {0, 0};
373 }
374 
name() const375 Name BRDSrc::name() const {
376     // We will replicate the names used by CodecSrc so that images can
377     // be compared in Gold.
378     if (1 == fSampleSize) {
379         return SkOSPath::Basename(fPath.c_str());
380     }
381     return get_scaled_name(fPath, 1.0f / (float) fSampleSize);
382 }
383 
384 #endif // SK_ENABLE_ANDROID_UTILS
385 
386 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
387 
serial_from_path_name(const SkString & path)388 static bool serial_from_path_name(const SkString& path) {
389     if (!FLAGS_RAW_threading) {
390         static const char* const exts[] = {
391             "arw", "cr2", "dng", "nef", "nrw", "orf", "raf", "rw2", "pef", "srw",
392             "ARW", "CR2", "DNG", "NEF", "NRW", "ORF", "RAF", "RW2", "PEF", "SRW",
393         };
394         const char* actualExt = strrchr(path.c_str(), '.');
395         if (actualExt) {
396             actualExt++;
397             for (auto* ext : exts) {
398                 if (0 == strcmp(ext, actualExt)) {
399                     return true;
400                 }
401             }
402         }
403     }
404     return false;
405 }
406 
CodecSrc(Path path,Mode mode,DstColorType dstColorType,SkAlphaType dstAlphaType,float scale)407 CodecSrc::CodecSrc(Path path, Mode mode, DstColorType dstColorType, SkAlphaType dstAlphaType,
408                    float scale)
409     : fPath(path)
410     , fMode(mode)
411     , fDstColorType(dstColorType)
412     , fDstAlphaType(dstAlphaType)
413     , fScale(scale)
414     , fRunSerially(serial_from_path_name(path))
415 {}
416 
veto(SinkFlags flags) const417 bool CodecSrc::veto(SinkFlags flags) const {
418     // Test to direct raster backends (8888 and 565).
419     return flags.type != SinkFlags::kRaster || flags.approach != SinkFlags::kDirect;
420 }
421 
422 // Allows us to test decodes to non-native 8888.
swap_rb_if_necessary(SkBitmap & bitmap,CodecSrc::DstColorType dstColorType)423 static void swap_rb_if_necessary(SkBitmap& bitmap, CodecSrc::DstColorType dstColorType) {
424     if (CodecSrc::kNonNative8888_Always_DstColorType != dstColorType) {
425         return;
426     }
427 
428     for (int y = 0; y < bitmap.height(); y++) {
429         uint32_t* row = (uint32_t*) bitmap.getAddr(0, y);
430         SkOpts::RGBA_to_BGRA(row, row, bitmap.width());
431     }
432 }
433 
get_decode_info(SkImageInfo * decodeInfo,SkColorType canvasColorType,CodecSrc::DstColorType dstColorType,SkAlphaType dstAlphaType)434 static bool get_decode_info(SkImageInfo* decodeInfo, SkColorType canvasColorType,
435                             CodecSrc::DstColorType dstColorType, SkAlphaType dstAlphaType) {
436     switch (dstColorType) {
437         case CodecSrc::kGrayscale_Always_DstColorType:
438             if (kRGB_565_SkColorType == canvasColorType) {
439                 return false;
440             }
441             *decodeInfo = decodeInfo->makeColorType(kGray_8_SkColorType);
442             break;
443         case CodecSrc::kNonNative8888_Always_DstColorType:
444             if (kRGB_565_SkColorType == canvasColorType
445                     || kRGBA_F16_SkColorType == canvasColorType) {
446                 return false;
447             }
448 #ifdef SK_PMCOLOR_IS_RGBA
449             *decodeInfo = decodeInfo->makeColorType(kBGRA_8888_SkColorType);
450 #else
451             *decodeInfo = decodeInfo->makeColorType(kRGBA_8888_SkColorType);
452 #endif
453             break;
454         default:
455             if (kRGB_565_SkColorType == canvasColorType &&
456                     kOpaque_SkAlphaType != decodeInfo->alphaType()) {
457                 return false;
458             }
459 
460             *decodeInfo = decodeInfo->makeColorType(canvasColorType);
461             break;
462     }
463 
464     *decodeInfo = decodeInfo->makeAlphaType(dstAlphaType);
465     return true;
466 }
467 
draw_to_canvas(SkCanvas * canvas,const SkImageInfo & info,void * pixels,size_t rowBytes,CodecSrc::DstColorType dstColorType,SkScalar left=0,SkScalar top=0)468 static void draw_to_canvas(SkCanvas* canvas, const SkImageInfo& info, void* pixels, size_t rowBytes,
469                            CodecSrc::DstColorType dstColorType,
470                            SkScalar left = 0, SkScalar top = 0) {
471     SkBitmap bitmap;
472     bitmap.installPixels(info, pixels, rowBytes);
473     swap_rb_if_necessary(bitmap, dstColorType);
474     canvas->drawImage(bitmap.asImage(), left, top);
475 }
476 
477 // For codec srcs, we want the "draw" step to be a memcpy.  Any interesting color space or
478 // color format conversions should be performed by the codec.  Sometimes the output of the
479 // decode will be in an interesting color space.  On our srgb and f16 backends, we need to
480 // "pretend" that the color space is standard sRGB to avoid triggering color conversion
481 // at draw time.
set_bitmap_color_space(SkImageInfo * info)482 static void set_bitmap_color_space(SkImageInfo* info) {
483     *info = info->makeColorSpace(SkColorSpace::MakeSRGB());
484 }
485 
draw(SkCanvas * canvas,GraphiteTestContext *) const486 Result CodecSrc::draw(SkCanvas* canvas, GraphiteTestContext*) const {
487     sk_sp<SkData> encoded(SkData::MakeFromFileName(fPath.c_str()));
488     if (!encoded) {
489         return Result::Fatal("Couldn't read %s.", fPath.c_str());
490     }
491 
492     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(encoded));
493     if (nullptr == codec) {
494         return Result::Fatal("Couldn't create codec for %s.", fPath.c_str());
495     }
496 
497     SkImageInfo decodeInfo = codec->getInfo();
498     if (!get_decode_info(&decodeInfo, canvas->imageInfo().colorType(), fDstColorType,
499                          fDstAlphaType)) {
500         return Result::Skip("Skipping uninteresting test.");
501     }
502 
503     // Try to scale the image if it is desired
504     SkISize size = codec->getScaledDimensions(fScale);
505 
506     std::unique_ptr<SkAndroidCodec> androidCodec;
507     if (1.0f != fScale && fMode == kAnimated_Mode) {
508         androidCodec = SkAndroidCodec::MakeFromData(encoded);
509         size = androidCodec->getSampledDimensions(1 / fScale);
510     }
511 
512     if (size == decodeInfo.dimensions() && 1.0f != fScale) {
513         return Result::Skip("Test without scaling is uninteresting.");
514     }
515 
516     // Visually inspecting very small output images is not necessary.  We will
517     // cover these cases in unit testing.
518     if ((size.width() <= 10 || size.height() <= 10) && 1.0f != fScale) {
519         return Result::Skip("Scaling very small images is uninteresting.");
520     }
521     decodeInfo = decodeInfo.makeDimensions(size);
522 
523     const int bpp = decodeInfo.bytesPerPixel();
524     const size_t rowBytes = size.width() * bpp;
525     const size_t safeSize = decodeInfo.computeByteSize(rowBytes);
526     SkAutoMalloc pixels(safeSize);
527 
528     SkCodec::Options options;
529     if (kCodecZeroInit_Mode == fMode) {
530         memset(pixels.get(), 0, size.height() * rowBytes);
531         options.fZeroInitialized = SkCodec::kYes_ZeroInitialized;
532     }
533 
534     SkImageInfo bitmapInfo = decodeInfo;
535     set_bitmap_color_space(&bitmapInfo);
536     if (kRGBA_8888_SkColorType == decodeInfo.colorType() ||
537             kBGRA_8888_SkColorType == decodeInfo.colorType()) {
538         bitmapInfo = bitmapInfo.makeColorType(kN32_SkColorType);
539     }
540 
541     switch (fMode) {
542         case kAnimated_Mode: {
543             SkAndroidCodec::AndroidOptions androidOptions;
544             if (fScale != 1.0f) {
545                 SkASSERT(androidCodec);
546                 androidOptions.fSampleSize = 1 / fScale;
547                 auto dims = androidCodec->getSampledDimensions(androidOptions.fSampleSize);
548                 decodeInfo = decodeInfo.makeDimensions(dims);
549             }
550 
551             std::vector<SkCodec::FrameInfo> frameInfos = androidCodec
552                     ? androidCodec->codec()->getFrameInfo() : codec->getFrameInfo();
553             if (frameInfos.size() <= 1) {
554                 return Result::Fatal("%s is not an animated image.", fPath.c_str());
555             }
556 
557             // As in CodecSrc::size(), compute a roughly square grid to draw the frames
558             // into. "factor" is the number of frames to draw on one row. There will be
559             // up to "factor" rows as well.
560             const float root = sqrt((float) frameInfos.size());
561             const int factor = sk_float_ceil2int(root);
562 
563             // Used to cache a frame that future frames will depend on.
564             SkAutoMalloc priorFramePixels;
565             int cachedFrame = SkCodec::kNoFrame;
566             for (int i = 0; static_cast<size_t>(i) < frameInfos.size(); i++) {
567                 androidOptions.fFrameIndex = i;
568                 // Check for a prior frame
569                 const int reqFrame = frameInfos[i].fRequiredFrame;
570                 if (reqFrame != SkCodec::kNoFrame && reqFrame == cachedFrame
571                         && priorFramePixels.get()) {
572                     // Copy into pixels
573                     memcpy(pixels.get(), priorFramePixels.get(), safeSize);
574                     androidOptions.fPriorFrame = reqFrame;
575                 } else {
576                     androidOptions.fPriorFrame = SkCodec::kNoFrame;
577                 }
578                 SkCodec::Result result = androidCodec
579                         ? androidCodec->getAndroidPixels(decodeInfo, pixels.get(), rowBytes,
580                                                          &androidOptions)
581                         : codec->getPixels(decodeInfo, pixels.get(), rowBytes, &androidOptions);
582                 if (SkCodec::kInvalidInput == result && i > 0) {
583                     // Some of our test images have truncated later frames. Treat that
584                     // the same as incomplete.
585                     result = SkCodec::kIncompleteInput;
586                 }
587                 switch (result) {
588                     case SkCodec::kSuccess:
589                     case SkCodec::kErrorInInput:
590                     case SkCodec::kIncompleteInput: {
591                         // If the next frame depends on this one, store it in priorFrame.
592                         // It is possible that we may discard a frame that future frames depend on,
593                         // but the codec will simply redecode the discarded frame.
594                         // Do this before calling draw_to_canvas, which premultiplies in place. If
595                         // we're decoding to unpremul, we want to pass the unmodified frame to the
596                         // codec for decoding the next frame.
597                         if (static_cast<size_t>(i+1) < frameInfos.size()
598                                 && frameInfos[i+1].fRequiredFrame == i) {
599                             memcpy(priorFramePixels.reset(safeSize), pixels.get(), safeSize);
600                             cachedFrame = i;
601                         }
602 
603                         SkAutoCanvasRestore acr(canvas, true);
604                         const int xTranslate = (i % factor) * decodeInfo.width();
605                         const int yTranslate = (i / factor) * decodeInfo.height();
606                         canvas->translate(SkIntToScalar(xTranslate), SkIntToScalar(yTranslate));
607                         draw_to_canvas(canvas, bitmapInfo, pixels.get(), rowBytes, fDstColorType);
608                         if (result != SkCodec::kSuccess) {
609                             return Result::Ok();
610                         }
611                         break;
612                     }
613                     case SkCodec::kInvalidConversion:
614                         if (i > 0 && (decodeInfo.colorType() == kRGB_565_SkColorType)) {
615                             return Result::Skip(
616                                 "Cannot decode frame %i to 565 (%s).", i, fPath.c_str());
617                         }
618                         [[fallthrough]];
619                     default:
620                         return Result::Fatal(
621                             "Couldn't getPixels for frame %i in %s.", i, fPath.c_str());
622                 }
623             }
624             break;
625         }
626         case kCodecZeroInit_Mode:
627         case kCodec_Mode: {
628             switch (codec->getPixels(decodeInfo, pixels.get(), rowBytes, &options)) {
629                 case SkCodec::kSuccess:
630                     // We consider these to be valid, since we should still decode what is
631                     // available.
632                 case SkCodec::kErrorInInput:
633                 case SkCodec::kIncompleteInput:
634                     break;
635                 default:
636                     // Everything else is considered a failure.
637                     return Result::Fatal("Couldn't getPixels %s.", fPath.c_str());
638             }
639 
640             draw_to_canvas(canvas, bitmapInfo, pixels.get(), rowBytes, fDstColorType);
641             break;
642         }
643         case kScanline_Mode: {
644             void* dst = pixels.get();
645             uint32_t height = decodeInfo.height();
646             const bool useIncremental = [this]() {
647                 auto exts = { "png", "PNG", "gif", "GIF" };
648                 for (auto ext : exts) {
649                     if (fPath.endsWith(ext)) {
650                         return true;
651                     }
652                 }
653                 return false;
654             }();
655             // ico may use the old scanline method or the new one, depending on whether it
656             // internally holds a bmp or a png.
657             const bool ico = fPath.endsWith("ico");
658             bool useOldScanlineMethod = !useIncremental && !ico;
659             if (useIncremental || ico) {
660                 if (SkCodec::kSuccess == codec->startIncrementalDecode(decodeInfo, dst,
661                         rowBytes, &options)) {
662                     int rowsDecoded;
663                     auto result = codec->incrementalDecode(&rowsDecoded);
664                     if (SkCodec::kIncompleteInput == result || SkCodec::kErrorInInput == result) {
665                         codec->fillIncompleteImage(decodeInfo, dst, rowBytes,
666                                                    SkCodec::kNo_ZeroInitialized, height,
667                                                    rowsDecoded);
668                     }
669                 } else {
670                     if (useIncremental) {
671                         // Error: These should support incremental decode.
672                         return Result::Fatal("Could not start incremental decode");
673                     }
674                     // Otherwise, this is an ICO. Since incremental failed, it must contain a BMP,
675                     // which should work via startScanlineDecode
676                     useOldScanlineMethod = true;
677                 }
678             }
679 
680             if (useOldScanlineMethod) {
681                 if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo)) {
682                     return Result::Fatal("Could not start scanline decoder");
683                 }
684 
685                 // We do not need to check the return value.  On an incomplete
686                 // image, memory will be filled with a default value.
687                 codec->getScanlines(dst, height, rowBytes);
688             }
689 
690             draw_to_canvas(canvas, bitmapInfo, dst, rowBytes, fDstColorType);
691             break;
692         }
693         case kStripe_Mode: {
694             const int height = decodeInfo.height();
695             // This value is chosen arbitrarily.  We exercise more cases by choosing a value that
696             // does not align with image blocks.
697             const int stripeHeight = 37;
698             const int numStripes = (height + stripeHeight - 1) / stripeHeight;
699             void* dst = pixels.get();
700 
701             // Decode odd stripes
702             if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo, &options)) {
703                 return Result::Fatal("Could not start scanline decoder");
704             }
705 
706             // This mode was designed to test the new skip scanlines API in libjpeg-turbo.
707             // Jpegs have kTopDown_SkScanlineOrder, and at this time, it is not interesting
708             // to run this test for image types that do not have this scanline ordering.
709             // We only run this on Jpeg, which is always kTopDown.
710             SkASSERT(SkCodec::kTopDown_SkScanlineOrder == codec->getScanlineOrder());
711 
712             for (int i = 0; i < numStripes; i += 2) {
713                 // Skip a stripe
714                 const int linesToSkip = std::min(stripeHeight, height - i * stripeHeight);
715                 codec->skipScanlines(linesToSkip);
716 
717                 // Read a stripe
718                 const int startY = (i + 1) * stripeHeight;
719                 const int linesToRead = std::min(stripeHeight, height - startY);
720                 if (linesToRead > 0) {
721                     codec->getScanlines(SkTAddOffset<void>(dst, rowBytes * startY), linesToRead,
722                                         rowBytes);
723                 }
724             }
725 
726             // Decode even stripes
727             const SkCodec::Result startResult = codec->startScanlineDecode(decodeInfo);
728             if (SkCodec::kSuccess != startResult) {
729                 return Result::Fatal("Failed to restart scanline decoder with same parameters.");
730             }
731             for (int i = 0; i < numStripes; i += 2) {
732                 // Read a stripe
733                 const int startY = i * stripeHeight;
734                 const int linesToRead = std::min(stripeHeight, height - startY);
735                 codec->getScanlines(SkTAddOffset<void>(dst, rowBytes * startY), linesToRead,
736                                     rowBytes);
737 
738                 // Skip a stripe
739                 const int linesToSkip = std::min(stripeHeight, height - (i + 1) * stripeHeight);
740                 if (linesToSkip > 0) {
741                     codec->skipScanlines(linesToSkip);
742                 }
743             }
744 
745             draw_to_canvas(canvas, bitmapInfo, dst, rowBytes, fDstColorType);
746             break;
747         }
748         case kCroppedScanline_Mode: {
749             const int width = decodeInfo.width();
750             const int height = decodeInfo.height();
751             // This value is chosen because, as we move across the image, it will sometimes
752             // align with the jpeg block sizes and it will sometimes not.  This allows us
753             // to test interestingly different code paths in the implementation.
754             const int tileSize = 36;
755             SkIRect subset;
756             for (int x = 0; x < width; x += tileSize) {
757                 subset = SkIRect::MakeXYWH(x, 0, std::min(tileSize, width - x), height);
758                 options.fSubset = &subset;
759                 if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo, &options)) {
760                     return Result::Fatal("Could not start scanline decoder.");
761                 }
762 
763                 codec->getScanlines(SkTAddOffset<void>(pixels.get(), x * bpp), height, rowBytes);
764             }
765 
766             draw_to_canvas(canvas, bitmapInfo, pixels.get(), rowBytes, fDstColorType);
767             break;
768         }
769         case kSubset_Mode: {
770             // Arbitrarily choose a divisor.
771             int divisor = 2;
772             // Total width/height of the image.
773             const int W = codec->getInfo().width();
774             const int H = codec->getInfo().height();
775             if (divisor > W || divisor > H) {
776                 return Result::Skip("Cannot codec subset: divisor %d is too big "
777                                     "for %s with dimensions (%d x %d)", divisor,
778                                     fPath.c_str(), W, H);
779             }
780             // subset dimensions
781             // SkWebpCodec, the only one that supports subsets, requires even top/left boundaries.
782             const int w = SkAlign2(W / divisor);
783             const int h = SkAlign2(H / divisor);
784             SkIRect subset;
785             options.fSubset = &subset;
786             SkBitmap subsetBm;
787             // We will reuse pixel memory from bitmap.
788             void* dst = pixels.get();
789             // Keep track of left and top (for drawing subsetBm into canvas). We could use
790             // fScale * x and fScale * y, but we want integers such that the next subset will start
791             // where the last one ended. So we'll add decodeInfo.width() and height().
792             int left = 0;
793             for (int x = 0; x < W; x += w) {
794                 int top = 0;
795                 for (int y = 0; y < H; y+= h) {
796                     // Do not make the subset go off the edge of the image.
797                     const int preScaleW = std::min(w, W - x);
798                     const int preScaleH = std::min(h, H - y);
799                     subset.setXYWH(x, y, preScaleW, preScaleH);
800                     // And scale
801                     // FIXME: Should we have a version of getScaledDimensions that takes a subset
802                     // into account?
803                     const int scaledW = std::max(1, SkScalarRoundToInt(preScaleW * fScale));
804                     const int scaledH = std::max(1, SkScalarRoundToInt(preScaleH * fScale));
805                     decodeInfo = decodeInfo.makeWH(scaledW, scaledH);
806                     SkImageInfo subsetBitmapInfo = bitmapInfo.makeWH(scaledW, scaledH);
807                     size_t subsetRowBytes = subsetBitmapInfo.minRowBytes();
808                     const SkCodec::Result result = codec->getPixels(decodeInfo, dst, subsetRowBytes,
809                             &options);
810                     switch (result) {
811                         case SkCodec::kSuccess:
812                         case SkCodec::kErrorInInput:
813                         case SkCodec::kIncompleteInput:
814                             break;
815                         default:
816                             return Result::Fatal("subset codec failed to decode (%d, %d, %d, %d) "
817                                                  "from %s with dimensions (%d x %d)\t error %d",
818                                                  x, y, decodeInfo.width(), decodeInfo.height(),
819                                                  fPath.c_str(), W, H, result);
820                     }
821                     draw_to_canvas(canvas, subsetBitmapInfo, dst, subsetRowBytes, fDstColorType,
822                                    SkIntToScalar(left), SkIntToScalar(top));
823 
824                     // translate by the scaled height.
825                     top += decodeInfo.height();
826                 }
827                 // translate by the scaled width.
828                 left += decodeInfo.width();
829             }
830             return Result::Ok();
831         }
832         default:
833             SkASSERT(false);
834             return Result::Fatal("Invalid fMode");
835     }
836     return Result::Ok();
837 }
838 
size() const839 SkISize CodecSrc::size() const {
840     sk_sp<SkData> encoded(SkData::MakeFromFileName(fPath.c_str()));
841     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(encoded));
842     if (nullptr == codec) {
843         return {0, 0};
844     }
845 
846     if (fMode != kAnimated_Mode) {
847         return codec->getScaledDimensions(fScale);
848     }
849 
850     // We'll draw one of each frame, so make it big enough to hold them all
851     // in a grid. The grid will be roughly square, with "factor" frames per
852     // row and up to "factor" rows.
853     const size_t count = codec->getFrameInfo().size();
854     const float root = sqrt((float) count);
855     const int factor = sk_float_ceil2int(root);
856 
857     auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec));
858     auto imageSize = androidCodec->getSampledDimensions(1 / fScale);
859     imageSize.fWidth  = imageSize.fWidth  * factor;
860     imageSize.fHeight = imageSize.fHeight * sk_float_ceil2int((float) count / (float) factor);
861     return imageSize;
862 }
863 
name() const864 Name CodecSrc::name() const {
865     Name name = SkOSPath::Basename(fPath.c_str());
866     if (fMode == kAnimated_Mode) {
867         name.append("_animated");
868     }
869     if (1.0f == fScale) {
870         return name;
871     }
872     return get_scaled_name(name.c_str(), fScale);
873 }
874 
875 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
876 
AndroidCodecSrc(Path path,CodecSrc::DstColorType dstColorType,SkAlphaType dstAlphaType,int sampleSize)877 AndroidCodecSrc::AndroidCodecSrc(Path path, CodecSrc::DstColorType dstColorType,
878         SkAlphaType dstAlphaType, int sampleSize)
879     : fPath(path)
880     , fDstColorType(dstColorType)
881     , fDstAlphaType(dstAlphaType)
882     , fSampleSize(sampleSize)
883     , fRunSerially(serial_from_path_name(path))
884 {}
885 
veto(SinkFlags flags) const886 bool AndroidCodecSrc::veto(SinkFlags flags) const {
887     // No need to test decoding to non-raster or indirect backend.
888     return flags.type != SinkFlags::kRaster
889         || flags.approach != SinkFlags::kDirect;
890 }
891 
draw(SkCanvas * canvas,GraphiteTestContext *) const892 Result AndroidCodecSrc::draw(SkCanvas* canvas, GraphiteTestContext*) const {
893     sk_sp<SkData> encoded(SkData::MakeFromFileName(fPath.c_str()));
894     if (!encoded) {
895         return Result::Fatal("Couldn't read %s.", fPath.c_str());
896     }
897     std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::MakeFromData(encoded));
898     if (nullptr == codec) {
899         return Result::Fatal("Couldn't create android codec for %s.", fPath.c_str());
900     }
901 
902     SkImageInfo decodeInfo = codec->getInfo();
903     if (!get_decode_info(&decodeInfo, canvas->imageInfo().colorType(), fDstColorType,
904                          fDstAlphaType)) {
905         return Result::Skip("Skipping uninteresting test.");
906     }
907 
908     // Scale the image if it is desired.
909     SkISize size = codec->getSampledDimensions(fSampleSize);
910 
911     // Visually inspecting very small output images is not necessary.  We will
912     // cover these cases in unit testing.
913     if ((size.width() <= 10 || size.height() <= 10) && 1 != fSampleSize) {
914         return Result::Skip("Scaling very small images is uninteresting.");
915     }
916     decodeInfo = decodeInfo.makeDimensions(size);
917 
918     int bpp = decodeInfo.bytesPerPixel();
919     size_t rowBytes = size.width() * bpp;
920     SkAutoMalloc pixels(size.height() * rowBytes);
921 
922     SkBitmap bitmap;
923     SkImageInfo bitmapInfo = decodeInfo;
924     set_bitmap_color_space(&bitmapInfo);
925     if (kRGBA_8888_SkColorType == decodeInfo.colorType() ||
926             kBGRA_8888_SkColorType == decodeInfo.colorType()) {
927         bitmapInfo = bitmapInfo.makeColorType(kN32_SkColorType);
928     }
929 
930     // Create options for the codec.
931     SkAndroidCodec::AndroidOptions options;
932     options.fSampleSize = fSampleSize;
933 
934     switch (codec->getAndroidPixels(decodeInfo, pixels.get(), rowBytes, &options)) {
935         case SkCodec::kSuccess:
936         case SkCodec::kErrorInInput:
937         case SkCodec::kIncompleteInput:
938             break;
939         default:
940             return Result::Fatal("Couldn't getPixels %s.", fPath.c_str());
941     }
942     draw_to_canvas(canvas, bitmapInfo, pixels.get(), rowBytes, fDstColorType);
943     return Result::Ok();
944 }
945 
size() const946 SkISize AndroidCodecSrc::size() const {
947     sk_sp<SkData> encoded(SkData::MakeFromFileName(fPath.c_str()));
948     std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::MakeFromData(encoded));
949     if (nullptr == codec) {
950         return {0, 0};
951     }
952     return codec->getSampledDimensions(fSampleSize);
953 }
954 
name() const955 Name AndroidCodecSrc::name() const {
956     // We will replicate the names used by CodecSrc so that images can
957     // be compared in Gold.
958     if (1 == fSampleSize) {
959         return SkOSPath::Basename(fPath.c_str());
960     }
961     return get_scaled_name(fPath, 1.0f / (float) fSampleSize);
962 }
963 
964 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
965 
ImageGenSrc(Path path,Mode mode,SkAlphaType alphaType,bool isGpu)966 ImageGenSrc::ImageGenSrc(Path path, Mode mode, SkAlphaType alphaType, bool isGpu)
967     : fPath(path)
968     , fMode(mode)
969     , fDstAlphaType(alphaType)
970     , fIsGpu(isGpu)
971     , fRunSerially(serial_from_path_name(path))
972 {}
973 
veto(SinkFlags flags) const974 bool ImageGenSrc::veto(SinkFlags flags) const {
975     if (fIsGpu) {
976         // MSAA runs tend to run out of memory and tests the same code paths as regular gpu configs.
977         return flags.type != SinkFlags::kGPU || flags.approach != SinkFlags::kDirect ||
978                flags.multisampled == SinkFlags::kMultisampled;
979     }
980 
981     return flags.type != SinkFlags::kRaster || flags.approach != SinkFlags::kDirect;
982 }
983 
draw(SkCanvas * canvas,GraphiteTestContext *) const984 Result ImageGenSrc::draw(SkCanvas* canvas, GraphiteTestContext*) const {
985     if (kRGB_565_SkColorType == canvas->imageInfo().colorType()) {
986         return Result::Skip("Uninteresting to test image generator to 565.");
987     }
988 
989     sk_sp<SkData> encoded(SkData::MakeFromFileName(fPath.c_str()));
990     if (!encoded) {
991         return Result::Fatal("Couldn't read %s.", fPath.c_str());
992     }
993 
994 #if defined(SK_BUILD_FOR_WIN)
995     // Initialize COM in order to test with WIC.
996     SkAutoCoInitialize com;
997     if (!com.succeeded()) {
998         return Result::Fatal("Could not initialize COM.");
999     }
1000 #endif
1001 
1002     std::unique_ptr<SkImageGenerator> gen(nullptr);
1003     switch (fMode) {
1004         case kCodec_Mode:
1005             gen = SkCodecImageGenerator::MakeFromEncodedCodec(encoded);
1006             if (!gen) {
1007                 return Result::Fatal("Could not create codec image generator.");
1008             }
1009             break;
1010         case kPlatform_Mode: {
1011 #if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
1012             gen = SkImageGeneratorCG::MakeFromEncodedCG(encoded);
1013 #elif defined(SK_BUILD_FOR_WIN)
1014             gen = SkImageGeneratorWIC::MakeFromEncodedWIC(encoded);
1015 #elif defined(SK_ENABLE_NDK_IMAGES)
1016             gen = SkImageGeneratorNDK::MakeFromEncodedNDK(encoded);
1017 #endif
1018             if (!gen) {
1019                 return Result::Fatal("Could not create platform image generator.");
1020             }
1021             break;
1022         }
1023         default:
1024             SkASSERT(false);
1025             return Result::Fatal("Invalid image generator mode");
1026     }
1027 
1028     // Test deferred decoding path on GPU
1029     if (fIsGpu) {
1030         sk_sp<SkImage> image(SkImages::DeferredFromGenerator(std::move(gen)));
1031         if (!image) {
1032             return Result::Fatal("Could not create image from codec image generator.");
1033         }
1034         canvas->drawImage(image, 0, 0);
1035         return Result::Ok();
1036     }
1037 
1038     // Test various color and alpha types on CPU
1039     SkImageInfo decodeInfo = gen->getInfo().makeAlphaType(fDstAlphaType);
1040 
1041     int bpp = decodeInfo.bytesPerPixel();
1042     size_t rowBytes = decodeInfo.width() * bpp;
1043     SkAutoMalloc pixels(decodeInfo.height() * rowBytes);
1044     if (!gen->getPixels(decodeInfo, pixels.get(), rowBytes)) {
1045         Result::Status status = Result::Status::Fatal;
1046 #if defined(SK_BUILD_FOR_WIN)
1047         if (kPlatform_Mode == fMode) {
1048             // Do not issue a fatal error for WIC flakiness.
1049             status = Result::Status::Skip;
1050         }
1051 #endif
1052         return Result(
1053                 status,
1054                 SkStringPrintf("Image generator could not getPixels() for %s\n", fPath.c_str()));
1055     }
1056 
1057     set_bitmap_color_space(&decodeInfo);
1058     draw_to_canvas(canvas, decodeInfo, pixels.get(), rowBytes,
1059                    CodecSrc::kGetFromCanvas_DstColorType);
1060     return Result::Ok();
1061 }
1062 
size() const1063 SkISize ImageGenSrc::size() const {
1064     sk_sp<SkData> encoded(SkData::MakeFromFileName(fPath.c_str()));
1065     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(encoded));
1066     if (nullptr == codec) {
1067         return {0, 0};
1068     }
1069     return codec->getInfo().dimensions();
1070 }
1071 
name() const1072 Name ImageGenSrc::name() const {
1073     return SkOSPath::Basename(fPath.c_str());
1074 }
1075 
1076 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1077 
ColorCodecSrc(Path path,bool decode_to_dst)1078 ColorCodecSrc::ColorCodecSrc(Path path, bool decode_to_dst) : fPath(path)
1079                                                             , fDecodeToDst(decode_to_dst) {}
1080 
veto(SinkFlags flags) const1081 bool ColorCodecSrc::veto(SinkFlags flags) const {
1082     // Test to direct raster backends (8888 and 565).
1083     return flags.type != SinkFlags::kRaster || flags.approach != SinkFlags::kDirect;
1084 }
1085 
draw(SkCanvas * canvas,GraphiteTestContext *) const1086 Result ColorCodecSrc::draw(SkCanvas* canvas, GraphiteTestContext*) const {
1087     sk_sp<SkData> encoded(SkData::MakeFromFileName(fPath.c_str()));
1088     if (!encoded) {
1089         return Result::Fatal("Couldn't read %s.", fPath.c_str());
1090     }
1091 
1092     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(encoded));
1093     if (nullptr == codec) {
1094         return Result::Fatal("Couldn't create codec for %s.", fPath.c_str());
1095     }
1096 
1097     SkImageInfo info = codec->getInfo();
1098     if (SkEncodedOriginSwapsWidthHeight(codec->getOrigin())) {
1099         info = SkPixmapUtils::SwapWidthHeight(info);
1100     }
1101     if (fDecodeToDst) {
1102         SkImageInfo canvasInfo = canvas->imageInfo();
1103         if (!canvasInfo.colorSpace()) {
1104             // This will skip color conversion, and the resulting images will
1105             // look different from images they are compared against in Gold, but
1106             // that doesn't mean they are wrong. We have a test verifying that
1107             // passing a null SkColorSpace skips conversion, so skip this
1108             // misleading test.
1109             return Result::Skip("Skipping decoding without color transform.");
1110         }
1111         info = canvasInfo.makeDimensions(info.dimensions());
1112     }
1113 
1114     auto [image, result] = codec->getImage(info);
1115     switch (result) {
1116         case SkCodec::kSuccess:
1117         case SkCodec::kErrorInInput:
1118         case SkCodec::kIncompleteInput:
1119             canvas->drawImage(image, 0,0);
1120             return Result::Ok();
1121         case SkCodec::kInvalidConversion:
1122             // TODO(mtklein): why are there formats we can't decode to?
1123             return Result::Skip("SkCodec can't decode to this format.");
1124         default:
1125             return Result::Fatal("Couldn't getPixels %s. Error code %d", fPath.c_str(), result);
1126     }
1127 }
1128 
size() const1129 SkISize ColorCodecSrc::size() const {
1130     sk_sp<SkData> encoded(SkData::MakeFromFileName(fPath.c_str()));
1131     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(encoded));
1132     if (nullptr == codec) {
1133         return {0, 0};
1134     }
1135     return {codec->getInfo().width(), codec->getInfo().height()};
1136 }
1137 
name() const1138 Name ColorCodecSrc::name() const {
1139     return SkOSPath::Basename(fPath.c_str());
1140 }
1141 
1142 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1143 
1144 static DEFINE_int(skpViewportSize, 1000,
1145                   "Width & height of the viewport used to crop skp rendering.");
1146 
SKPSrc(Path path)1147 SKPSrc::SKPSrc(Path path) : fPath(path) { }
1148 
draw(SkCanvas * canvas,GraphiteTestContext *) const1149 Result SKPSrc::draw(SkCanvas* canvas, GraphiteTestContext*) const {
1150     struct DeserializationContext {
1151         GrDirectContext*           fDirectContext = nullptr;
1152 #if defined(SK_GRAPHITE)
1153         skgpu::graphite::Recorder* fRecorder = nullptr;
1154 #endif
1155     } ctx {
1156         GrAsDirectContext(canvas->recordingContext()),
1157 #if defined(SK_GRAPHITE)
1158         canvas->recorder()
1159 #endif
1160     };
1161 
1162     SkDeserialProcs procs;
1163     procs.fImageProc = [](const void* data, size_t size, void* ctx) -> sk_sp<SkImage> {
1164         sk_sp<SkData> tmpData = SkData::MakeWithoutCopy(data, size);
1165         sk_sp<SkImage> image = SkImages::DeferredFromEncodedData(std::move(tmpData));
1166         image = image->makeRasterImage(); // force decoding
1167 
1168         if (image) {
1169             DeserializationContext* context = reinterpret_cast<DeserializationContext*>(ctx);
1170 
1171             if (context->fDirectContext) {
1172                 return SkImages::TextureFromImage(context->fDirectContext, image);
1173             }
1174         }
1175         return image;
1176     };
1177     procs.fImageCtx = &ctx;
1178 
1179     // SKPs may have typefaces encoded in them (e.g. with FreeType). We can try falling back
1180     // to the Test FontMgr (possibly a native one) if we have do not have FreeType built-in.
1181     procs.fTypefaceProc = [](const void* data, size_t size, void*) -> sk_sp<SkTypeface> {
1182         SkStream** stream = reinterpret_cast<SkStream**>(const_cast<void*>(data));
1183         return SkTypeface::MakeDeserialize(*stream, ToolUtils::TestFontMgr());
1184     };
1185 
1186 
1187     std::unique_ptr<SkStream> stream = SkStream::MakeFromFile(fPath.c_str());
1188     if (!stream) {
1189         return Result::Fatal("Couldn't read %s.", fPath.c_str());
1190     }
1191     sk_sp<SkPicture> pic(SkPicture::MakeFromStream(stream.get(), &procs));
1192     if (!pic) {
1193         return Result::Fatal("Couldn't parse file %s.", fPath.c_str());
1194     }
1195     stream = nullptr;  // Might as well drop this when we're done with it.
1196     canvas->clipRect(SkRect::MakeWH(FLAGS_skpViewportSize, FLAGS_skpViewportSize));
1197     canvas->drawPicture(pic);
1198     return Result::Ok();
1199 }
1200 
get_cull_rect_for_skp(const char * path)1201 static SkRect get_cull_rect_for_skp(const char* path) {
1202     std::unique_ptr<SkStream> stream = SkStream::MakeFromFile(path);
1203     if (!stream) {
1204         return SkRect::MakeEmpty();
1205     }
1206     SkPictInfo info;
1207     if (!SkPicture_StreamIsSKP(stream.get(), &info)) {
1208         return SkRect::MakeEmpty();
1209     }
1210 
1211     return info.fCullRect;
1212 }
1213 
size() const1214 SkISize SKPSrc::size() const {
1215     SkRect viewport = get_cull_rect_for_skp(fPath.c_str());
1216     if (!viewport.intersect((SkRect::MakeWH(FLAGS_skpViewportSize, FLAGS_skpViewportSize)))) {
1217         return {0, 0};
1218     }
1219     return viewport.roundOut().size();
1220 }
1221 
name() const1222 Name SKPSrc::name() const { return SkOSPath::Basename(fPath.c_str()); }
1223 
1224 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1225 
BisectSrc(Path path,const char * trail)1226 BisectSrc::BisectSrc(Path path, const char* trail) : INHERITED(path), fTrail(trail) {}
1227 
draw(SkCanvas * canvas,GraphiteTestContext * testContext) const1228 Result BisectSrc::draw(SkCanvas* canvas, GraphiteTestContext* testContext) const {
1229     struct FoundPath {
1230         SkPath fPath;
1231         SkPaint fPaint;
1232         SkMatrix fViewMatrix;
1233     };
1234 
1235     // This subclass of SkCanvas just extracts all the SkPaths (drawn via drawPath) from an SKP.
1236     class PathFindingCanvas : public SkCanvas {
1237     public:
1238         PathFindingCanvas(int width, int height) : SkCanvas(width, height, nullptr) {}
1239         const TArray<FoundPath>& foundPaths() const { return fFoundPaths; }
1240 
1241     private:
1242         void onDrawPath(const SkPath& path, const SkPaint& paint) override {
1243             fFoundPaths.push_back() = {path, paint, this->getTotalMatrix()};
1244         }
1245 
1246         TArray<FoundPath> fFoundPaths;
1247     };
1248 
1249     PathFindingCanvas pathFinder(canvas->getBaseLayerSize().width(),
1250                                  canvas->getBaseLayerSize().height());
1251     Result result = this->INHERITED::draw(&pathFinder, testContext);
1252     if (!result.isOk()) {
1253         return result;
1254     }
1255 
1256     int start = 0, end = pathFinder.foundPaths().size();
1257     for (const char* ch = fTrail.c_str(); *ch; ++ch) {
1258         int midpt = (start + end) / 2;
1259         if ('l' == *ch) {
1260             start = midpt;
1261         } else if ('r' == *ch) {
1262             end = midpt;
1263         }
1264     }
1265 
1266     for (int i = start; i < end; ++i) {
1267         const FoundPath& path = pathFinder.foundPaths()[i];
1268         SkAutoCanvasRestore acr(canvas, true);
1269         canvas->concat(path.fViewMatrix);
1270         canvas->drawPath(path.fPath, path.fPaint);
1271     }
1272 
1273     return Result::Ok();
1274 }
1275 
1276 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1277 
1278 #if defined(SK_ENABLE_SKOTTIE)
1279 static DEFINE_bool(useLottieGlyphPaths, false,
1280                    "Prioritize embedded glyph paths over native fonts.");
1281 
SkottieSrc(Path path)1282 SkottieSrc::SkottieSrc(Path path) : fPath(std::move(path)) {}
1283 
draw(SkCanvas * canvas,GraphiteTestContext *) const1284 Result SkottieSrc::draw(SkCanvas* canvas, GraphiteTestContext*) const {
1285     auto predecode = skresources::ImageDecodeStrategy::kPreDecode;
1286     // DM should have already registered the codecs necessary for DataURIResourceProviderProxy
1287     // to decode images.
1288     auto resource_provider = skresources::DataURIResourceProviderProxy::Make(
1289             skresources::FileResourceProvider::Make(SkOSPath::Dirname(fPath.c_str()), predecode),
1290             predecode,
1291             ToolUtils::TestFontMgr());
1292 
1293     static constexpr char kInterceptPrefix[] = "__";
1294     auto precomp_interceptor =
1295             sk_make_sp<skottie_utils::ExternalAnimationPrecompInterceptor>(resource_provider,
1296                                                                            kInterceptPrefix);
1297     uint32_t flags = 0;
1298     if (FLAGS_useLottieGlyphPaths) {
1299         flags |= skottie::Animation::Builder::kPreferEmbeddedFonts;
1300     }
1301 
1302     auto animation = skottie::Animation::Builder(flags)
1303         .setFontManager(ToolUtils::TestFontMgr())
1304         .setResourceProvider(std::move(resource_provider))
1305         .setPrecompInterceptor(std::move(precomp_interceptor))
1306         .setTextShapingFactory(SkShapers::BestAvailable())
1307         .makeFromFile(fPath.c_str());
1308     if (!animation) {
1309         return Result::Fatal("Unable to parse file: %s", fPath.c_str());
1310     }
1311 
1312     canvas->drawColor(SK_ColorWHITE);
1313 
1314     const auto t_rate = 1.0f / (kTileCount * kTileCount - 1);
1315 
1316     // Draw the frames in a shuffled order to exercise non-linear
1317     // frame progression. The film strip will still be in order left-to-right,
1318     // top-down, just not drawn in that order.
1319     static constexpr int frameOrder[] = { 4, 0, 3, 1, 2 };
1320     static_assert(std::size(frameOrder) == kTileCount, "");
1321 
1322     for (int i = 0; i < kTileCount; ++i) {
1323         const SkScalar y = frameOrder[i] * kTileSize;
1324 
1325         for (int j = 0; j < kTileCount; ++j) {
1326             const SkScalar x = frameOrder[j] * kTileSize;
1327             SkRect dest = SkRect::MakeXYWH(x, y, kTileSize, kTileSize);
1328 
1329             const auto t = t_rate * (frameOrder[i] * kTileCount + frameOrder[j]);
1330             {
1331                 SkAutoCanvasRestore acr(canvas, true);
1332                 canvas->clipRect(dest, true);
1333                 canvas->concat(SkMatrix::RectToRect(SkRect::MakeSize(animation->size()), dest,
1334                                                     SkMatrix::kCenter_ScaleToFit));
1335                 animation->seek(t);
1336                 animation->render(canvas);
1337             }
1338         }
1339     }
1340 
1341     return Result::Ok();
1342 }
1343 
size() const1344 SkISize SkottieSrc::size() const {
1345     return SkISize::Make(kTargetSize, kTargetSize);
1346 }
1347 
name() const1348 Name SkottieSrc::name() const { return SkOSPath::Basename(fPath.c_str()); }
1349 
veto(SinkFlags flags) const1350 bool SkottieSrc::veto(SinkFlags flags) const {
1351     // No need to test to non-(raster||gpu||vector) or indirect backends.
1352     bool type_ok = flags.type == SinkFlags::kRaster
1353                 || flags.type == SinkFlags::kGPU
1354                 || flags.type == SinkFlags::kVector;
1355 
1356     return !type_ok || flags.approach != SinkFlags::kDirect;
1357 }
1358 #endif
1359 
1360 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1361 #if defined(SK_ENABLE_SVG)
1362 // Used when the image doesn't have an intrinsic size.
1363 static const SkSize kDefaultSVGSize = {1000, 1000};
1364 
1365 // Used to force-scale tiny fixed-size images.
1366 static const SkSize kMinimumSVGSize = {128, 128};
1367 
SVGSrc(Path path)1368 SVGSrc::SVGSrc(Path path)
1369     : fName(SkOSPath::Basename(path.c_str()))
1370     , fScale(1) {
1371 
1372     auto stream = SkStream::MakeFromFile(path.c_str());
1373     if (!stream) {
1374         return;
1375     }
1376 
1377     // DM should have already registered the codecs necessary for DataURIResourceProviderProxy
1378     // to decode images.
1379     auto predecode = skresources::ImageDecodeStrategy::kPreDecode;
1380     auto rp = skresources::DataURIResourceProviderProxy::Make(
1381             skresources::FileResourceProvider::Make(SkOSPath::Dirname(path.c_str()), predecode),
1382             predecode,
1383             ToolUtils::TestFontMgr());
1384 
1385     fDom = SkSVGDOM::Builder()
1386                    .setResourceProvider(std::move(rp))
1387                    .setFontManager(ToolUtils::TestFontMgr())
1388                    .setTextShapingFactory(SkShapers::BestAvailable())
1389                    .make(*stream);
1390     if (!fDom) {
1391         return;
1392     }
1393 
1394     const SkSize& sz = fDom->containerSize();
1395     if (sz.isEmpty()) {
1396         // no intrinsic size
1397         fDom->setContainerSize(kDefaultSVGSize);
1398     } else {
1399         fScale = std::max(1.f, std::max(kMinimumSVGSize.width()  / sz.width(),
1400                                         kMinimumSVGSize.height() / sz.height()));
1401     }
1402 }
1403 
draw(SkCanvas * canvas,GraphiteTestContext *) const1404 Result SVGSrc::draw(SkCanvas* canvas, GraphiteTestContext*) const {
1405     if (!fDom) {
1406         return Result::Fatal("Unable to parse file: %s", fName.c_str());
1407     }
1408 
1409     SkAutoCanvasRestore acr(canvas, true);
1410     canvas->scale(fScale, fScale);
1411     canvas->drawColor(SK_ColorWHITE);
1412     fDom->render(canvas);
1413 
1414     return Result::Ok();
1415 }
1416 
size() const1417 SkISize SVGSrc::size() const {
1418     if (!fDom) {
1419         return {0, 0};
1420     }
1421 
1422     return SkSize{fDom->containerSize().width() * fScale, fDom->containerSize().height() * fScale}
1423             .toRound();
1424 }
1425 
name() const1426 Name SVGSrc::name() const { return fName; }
1427 
veto(SinkFlags flags) const1428 bool SVGSrc::veto(SinkFlags flags) const {
1429     // No need to test to non-(raster||gpu||vector) or indirect backends.
1430     bool type_ok = flags.type == SinkFlags::kRaster
1431                 || flags.type == SinkFlags::kGPU
1432                 || flags.type == SinkFlags::kVector;
1433 
1434     return !type_ok || flags.approach != SinkFlags::kDirect;
1435 }
1436 
1437 #endif // defined(SK_ENABLE_SVG)
1438 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1439 
MSKPSrc(Path path)1440 MSKPSrc::MSKPSrc(Path path) : fPath(path) {
1441     std::unique_ptr<SkStreamAsset> stream = SkStream::MakeFromFile(fPath.c_str());
1442     int count = SkMultiPictureDocument::ReadPageCount(stream.get());
1443     if (count > 0) {
1444         fPages.reset(count);
1445         SkASSERT_RELEASE(SkMultiPictureDocument::ReadPageSizes(stream.get(), &fPages[0],
1446                                                                fPages.size()));
1447     }
1448 }
1449 
pageCount() const1450 int MSKPSrc::pageCount() const { return fPages.size(); }
1451 
size() const1452 SkISize MSKPSrc::size() const { return this->size(FLAGS_mskpFrame); }
size(int i) const1453 SkISize MSKPSrc::size(int i) const {
1454     return i >= 0 && i < fPages.size() ? fPages[i].fSize.toCeil() : SkISize{0, 0};
1455 }
1456 
draw(SkCanvas * c,GraphiteTestContext * testContext) const1457 Result MSKPSrc::draw(SkCanvas* c, GraphiteTestContext* testContext) const {
1458     return this->draw(FLAGS_mskpFrame, c, testContext);
1459 }
draw(int i,SkCanvas * canvas,GraphiteTestContext *) const1460 Result MSKPSrc::draw(int i, SkCanvas* canvas, GraphiteTestContext*) const {
1461     if (this->pageCount() == 0) {
1462         return Result::Fatal("Unable to parse MultiPictureDocument file: %s", fPath.c_str());
1463     }
1464     if (i >= fPages.size() || i < 0) {
1465         return Result::Fatal("MultiPictureDocument page number out of range: %d", i);
1466     }
1467     SkPicture* page = fPages[i].fPicture.get();
1468     if (!page) {
1469         std::unique_ptr<SkStreamAsset> stream = SkStream::MakeFromFile(fPath.c_str());
1470         if (!stream) {
1471             return Result::Fatal("Unable to open file: %s", fPath.c_str());
1472         }
1473         if (!SkMultiPictureDocument::Read(stream.get(), &fPages[0], fPages.size())) {
1474             return Result::Fatal("SkMultiPictureDocument reader failed on page %d: %s", i,
1475                                  fPath.c_str());
1476         }
1477         page = fPages[i].fPicture.get();
1478     }
1479     canvas->drawPicture(page);
1480     return Result::Ok();
1481 }
1482 
name() const1483 Name MSKPSrc::name() const { return SkOSPath::Basename(fPath.c_str()); }
1484 
1485 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1486 
draw(const Src & src,SkBitmap *,SkWStream *,SkString *) const1487 Result NullSink::draw(const Src& src, SkBitmap*, SkWStream*, SkString*) const {
1488     return src.draw(SkMakeNullCanvas().get(), /*GraphiteTestContext=*/nullptr);
1489 }
1490 
1491 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1492 
compare_bitmaps(const SkBitmap & reference,const SkBitmap & bitmap)1493 static Result compare_bitmaps(const SkBitmap& reference, const SkBitmap& bitmap) {
1494     // The dimensions are a property of the Src only, and so should be identical.
1495     SkASSERT(reference.computeByteSize() == bitmap.computeByteSize());
1496     if (reference.computeByteSize() != bitmap.computeByteSize()) {
1497         return Result::Fatal("Dimensions don't match reference");
1498     }
1499     // All SkBitmaps in DM are tight, so this comparison is easy.
1500     if (0 != memcmp(reference.getPixels(), bitmap.getPixels(), reference.computeByteSize())) {
1501         SkString encoded;
1502         SkString errString("Pixels don't match reference");
1503         if (ToolUtils::BitmapToBase64DataURI(reference, &encoded)) {
1504             errString.append("\nExpected: ");
1505             errString.append(encoded);
1506         } else {
1507             errString.append("\nExpected image failed to encode: ");
1508             errString.append(encoded);
1509         }
1510         if (ToolUtils::BitmapToBase64DataURI(bitmap, &encoded)) {
1511             errString.append("\nActual: ");
1512             errString.append(encoded);
1513         } else {
1514             errString.append("\nActual image failed to encode: ");
1515             errString.append(encoded);
1516         }
1517         return Result(Result::Status::Fatal, errString);
1518     }
1519     return Result::Ok();
1520 }
1521 
1522 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1523 
1524 static DEFINE_bool(gpuStats, false, "Append GPU stats to the log for each GPU task?");
1525 static DEFINE_bool(preAbandonGpuContext, false,
1526                    "Test abandoning the GrContext before running the test.");
1527 static DEFINE_bool(abandonGpuContext, false,
1528                    "Test abandoning the GrContext after running each test.");
1529 static DEFINE_bool(releaseAndAbandonGpuContext, false,
1530                    "Test releasing all gpu resources and abandoning the GrContext "
1531                    "after running each test");
1532 static DEFINE_bool(drawOpClip, false, "Clip each GrDrawOp to its device bounds for testing.");
1533 static DEFINE_bool(programBinaryCache, true, "Use in-memory program binary cache");
1534 
GPUSink(const SkCommandLineConfigGpu * config,const GrContextOptions & grCtxOptions)1535 GPUSink::GPUSink(const SkCommandLineConfigGpu* config,
1536                  const GrContextOptions& grCtxOptions)
1537         : fContextType(config->getContextType())
1538         , fContextOverrides(config->getContextOverrides())
1539         , fSurfType(config->getSurfType())
1540         , fSampleCount(config->getSamples())
1541         , fSurfaceFlags(config->getSurfaceFlags())
1542         , fColorType(config->getColorType())
1543         , fAlphaType(config->getAlphaType())
1544         , fBaseContextOptions(grCtxOptions) {
1545     if (FLAGS_programBinaryCache) {
1546         fBaseContextOptions.fPersistentCache = &fMemoryCache;
1547     }
1548 }
1549 
draw(const Src & src,SkBitmap * dst,SkWStream * dstStream,SkString * log) const1550 Result GPUSink::draw(const Src& src, SkBitmap* dst, SkWStream* dstStream, SkString* log) const {
1551     return this->onDraw(src, dst, dstStream, log, fBaseContextOptions);
1552 }
1553 
createDstSurface(GrDirectContext * context,SkISize size) const1554 sk_sp<SkSurface> GPUSink::createDstSurface(GrDirectContext* context, SkISize size) const {
1555     sk_sp<SkSurface> surface;
1556 
1557     SkImageInfo info = SkImageInfo::Make(size, this->colorInfo());
1558     SkSurfaceProps props(fSurfaceFlags, kRGB_H_SkPixelGeometry);
1559 
1560     switch (fSurfType) {
1561         case SkCommandLineConfigGpu::SurfType::kDefault:
1562             surface = SkSurfaces::RenderTarget(
1563                     context, skgpu::Budgeted::kNo, info, fSampleCount, &props);
1564             break;
1565         case SkCommandLineConfigGpu::SurfType::kBackendTexture:
1566             surface = sk_gpu_test::MakeBackendTextureSurface(context,
1567                                                              info,
1568                                                              kTopLeft_GrSurfaceOrigin,
1569                                                              fSampleCount,
1570                                                              skgpu::Mipmapped::kNo,
1571                                                              GrProtected::kNo,
1572                                                              &props);
1573             break;
1574         case SkCommandLineConfigGpu::SurfType::kBackendRenderTarget:
1575             surface = sk_gpu_test::MakeBackendRenderTargetSurface(context,
1576                                                                   info,
1577                                                                   kBottomLeft_GrSurfaceOrigin,
1578                                                                   fSampleCount,
1579                                                                   GrProtected::kNo,
1580                                                                   &props);
1581             break;
1582     }
1583 
1584     return surface;
1585 }
1586 
readBack(SkSurface * surface,SkBitmap * dst) const1587 bool GPUSink::readBack(SkSurface* surface, SkBitmap* dst) const {
1588     SkCanvas* canvas = surface->getCanvas();
1589     SkISize size = surface->imageInfo().dimensions();
1590 
1591     SkImageInfo info = SkImageInfo::Make(size, this->colorInfo());
1592     dst->allocPixels(info);
1593     return canvas->readPixels(*dst, 0, 0);
1594 }
1595 
onDraw(const Src & src,SkBitmap * dst,SkWStream *,SkString * log,const GrContextOptions & baseOptions,std::function<void (GrDirectContext *)> initContext,std::function<SkCanvas * (SkCanvas *)> wrapCanvas) const1596 Result GPUSink::onDraw(const Src& src, SkBitmap* dst, SkWStream*, SkString* log,
1597                        const GrContextOptions& baseOptions,
1598                        std::function<void(GrDirectContext*)> initContext,
1599                        std::function<SkCanvas*(SkCanvas*)> wrapCanvas) const {
1600     GrContextOptions grOptions = baseOptions;
1601 
1602     // We don't expect the src to mess with the persistent cache or the executor.
1603     SkDEBUGCODE(auto cache = grOptions.fPersistentCache);
1604     SkDEBUGCODE(auto exec = grOptions.fExecutor);
1605     src.modifyGrContextOptions(&grOptions);
1606     SkASSERT(cache == grOptions.fPersistentCache);
1607     SkASSERT(exec == grOptions.fExecutor);
1608 
1609     GrContextFactory factory(grOptions);
1610     auto direct = factory.getContextInfo(fContextType, fContextOverrides).directContext();
1611     if (initContext) {
1612         initContext(direct);
1613     }
1614 
1615     const int maxDimension = direct->priv().caps()->maxTextureSize();
1616     if (maxDimension < std::max(src.size().width(), src.size().height())) {
1617         return Result::Skip("Src too large to create a texture.\n");
1618     }
1619 
1620     sk_sp<SkSurface> surface = this->createDstSurface(direct, src.size());
1621     if (!surface) {
1622         return Result::Fatal("Could not create a surface.");
1623     }
1624     if (FLAGS_preAbandonGpuContext) {
1625         factory.abandonContexts();
1626     }
1627 
1628     auto canvas = surface->getCanvas();
1629     if (wrapCanvas != nullptr) {
1630         canvas = wrapCanvas(canvas);
1631     }
1632 
1633     Result result = src.draw(canvas, /*GraphiteTestContext=*/nullptr);
1634     if (!result.isOk()) {
1635         return result;
1636     }
1637     direct->flushAndSubmit(surface.get(), GrSyncCpu::kNo);
1638     if (FLAGS_gpuStats) {
1639         direct->priv().dumpCacheStats(log);
1640         direct->priv().dumpGpuStats(log);
1641         direct->priv().dumpContextStats(log);
1642     }
1643 
1644     this->readBack(surface.get(), dst);
1645 
1646     if (FLAGS_abandonGpuContext) {
1647         factory.abandonContexts();
1648     } else if (FLAGS_releaseAndAbandonGpuContext) {
1649         factory.releaseResourcesAndAbandonContexts();
1650     }
1651 
1652     if (grOptions.fPersistentCache) {
1653         direct->storeVkPipelineCacheData();
1654     }
1655     return Result::Ok();
1656 }
1657 
1658 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GPUSlugSink(const SkCommandLineConfigGpu * config,const GrContextOptions & options)1659 GPUSlugSink::GPUSlugSink(const SkCommandLineConfigGpu* config, const GrContextOptions& options)
1660         : GPUSink(config, options) {}
1661 
draw(const Src & src,SkBitmap * dst,SkWStream * write,SkString * log) const1662 Result GPUSlugSink::draw(const Src& src, SkBitmap* dst, SkWStream* write, SkString* log) const {
1663     GrContextOptions grOptions = this->baseContextOptions();
1664     // Force padded atlas entries for slug drawing.
1665     grOptions.fSupportBilerpFromGlyphAtlas |= true;
1666 
1667     SkTLazy<skiatest::TestCanvas<skiatest::SkSlugTestKey>> testCanvas;
1668 
1669     return onDraw(src, dst, write, log, grOptions, nullptr,
1670         [&](SkCanvas* canvas){
1671             testCanvas.init(canvas);
1672             return testCanvas.get();
1673         });
1674 }
1675 
1676 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GPUSerializeSlugSink(const SkCommandLineConfigGpu * config,const GrContextOptions & options)1677 GPUSerializeSlugSink::GPUSerializeSlugSink(
1678         const SkCommandLineConfigGpu* config, const GrContextOptions& options)
1679     : GPUSink(config, options) {}
1680 
draw(const Src & src,SkBitmap * dst,SkWStream * write,SkString * log) const1681 Result GPUSerializeSlugSink::draw(
1682         const Src& src, SkBitmap* dst, SkWStream* write, SkString* log) const {
1683     GrContextOptions grOptions = this->baseContextOptions();
1684     // Force padded atlas entries for slug drawing.
1685     grOptions.fSupportBilerpFromGlyphAtlas |= true;
1686 
1687     SkTLazy<skiatest::TestCanvas<skiatest::SkSerializeSlugTestKey>> testCanvas;
1688 
1689     return onDraw(src, dst, write, log, grOptions, nullptr,
1690                   [&](SkCanvas* canvas){
1691                       testCanvas.init(canvas);
1692                       return testCanvas.get();
1693                   });
1694 }
1695 
1696 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GPURemoteSlugSink(const SkCommandLineConfigGpu * config,const GrContextOptions & options)1697 GPURemoteSlugSink::GPURemoteSlugSink(
1698         const SkCommandLineConfigGpu* config, const GrContextOptions& options)
1699         : GPUSink(config, options) {}
1700 
draw(const Src & src,SkBitmap * dst,SkWStream * write,SkString * log) const1701 Result GPURemoteSlugSink::draw(
1702         const Src& src, SkBitmap* dst, SkWStream* write, SkString* log) const {
1703     GrContextOptions grOptions = this->baseContextOptions();
1704     // Force padded atlas entries for slug drawing.
1705     grOptions.fSupportBilerpFromGlyphAtlas |= true;
1706 
1707     SkTLazy<skiatest::TestCanvas<skiatest::SkRemoteSlugTestKey>> testCanvas;
1708 
1709     return onDraw(src, dst, write, log, grOptions, nullptr,
1710                   [&](SkCanvas* canvas) {
1711                       testCanvas.init(canvas);
1712                       return testCanvas.get();
1713                   });
1714 }
1715 
1716 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GPUPersistentCacheTestingSink(const SkCommandLineConfigGpu * config,const GrContextOptions & grCtxOptions)1717 GPUPersistentCacheTestingSink::GPUPersistentCacheTestingSink(const SkCommandLineConfigGpu* config,
1718                                                              const GrContextOptions& grCtxOptions)
1719     : INHERITED(config, grCtxOptions)
1720     , fCacheType(config->getTestPersistentCache()) {}
1721 
draw(const Src & src,SkBitmap * dst,SkWStream * wStream,SkString * log) const1722 Result GPUPersistentCacheTestingSink::draw(const Src& src, SkBitmap* dst, SkWStream* wStream,
1723                                            SkString* log) const {
1724     // Draw twice, once with a cold cache, and again with a warm cache. Verify that we get the same
1725     // result.
1726     sk_gpu_test::MemoryCache memoryCache;
1727     GrContextOptions contextOptions = this->baseContextOptions();
1728     contextOptions.fPersistentCache = &memoryCache;
1729     if (fCacheType == 2) {
1730         contextOptions.fShaderCacheStrategy = GrContextOptions::ShaderCacheStrategy::kBackendSource;
1731     }
1732 
1733     Result result = this->onDraw(src, dst, wStream, log, contextOptions);
1734     if (!result.isOk() || !dst) {
1735         return result;
1736     }
1737 
1738     SkBitmap reference;
1739     SkString refLog;
1740     SkDynamicMemoryWStream refStream;
1741     memoryCache.resetCacheStats();
1742     Result refResult = this->onDraw(src, &reference, &refStream, &refLog, contextOptions);
1743     if (!refResult.isOk()) {
1744         return refResult;
1745     }
1746     SkASSERT(!memoryCache.numCacheMisses());
1747     SkASSERT(!memoryCache.numCacheStores());
1748 
1749     return compare_bitmaps(reference, *dst);
1750 }
1751 
1752 
1753 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1754 
GaneshPrecompileTestingSink(const SkCommandLineConfigGpu * config,const GrContextOptions & grCtxOptions)1755 GaneshPrecompileTestingSink::GaneshPrecompileTestingSink(const SkCommandLineConfigGpu* config,
1756                                                          const GrContextOptions& grCtxOptions)
1757     : INHERITED(config, grCtxOptions) {}
1758 
draw(const Src & src,SkBitmap * dst,SkWStream * wStream,SkString * log) const1759 Result GaneshPrecompileTestingSink::draw(const Src& src, SkBitmap* dst, SkWStream* wStream,
1760                                          SkString* log) const {
1761     // Three step process:
1762     // 1) Draw once with an SkSL cache, and store off the shader blobs.
1763     // 2) For the second context, pre-compile the shaders to warm the cache.
1764     // 3) Draw with the second context, ensuring that we get the same result, and no cache misses.
1765     sk_gpu_test::MemoryCache memoryCache;
1766     GrContextOptions contextOptions = this->baseContextOptions();
1767     contextOptions.fPersistentCache = &memoryCache;
1768     contextOptions.fShaderCacheStrategy = GrContextOptions::ShaderCacheStrategy::kSkSL;
1769 
1770     Result result = this->onDraw(src, dst, wStream, log, contextOptions);
1771     if (!result.isOk() || !dst) {
1772         return result;
1773     }
1774 
1775     auto precompileShaders = [&memoryCache](GrDirectContext* dContext) {
1776         memoryCache.foreach([dContext](sk_sp<const SkData> key,
1777                                        sk_sp<SkData> data,
1778                                        const SkString& /*description*/,
1779                                        int /*count*/) {
1780             SkAssertResult(dContext->precompileShader(*key, *data));
1781         });
1782     };
1783 
1784     sk_gpu_test::MemoryCache replayCache;
1785     GrContextOptions replayOptions = this->baseContextOptions();
1786     // Ensure that the runtime cache is large enough to hold all of the shaders we pre-compile
1787     replayOptions.fRuntimeProgramCacheSize = memoryCache.numCacheMisses();
1788     replayOptions.fPersistentCache = &replayCache;
1789 
1790     SkBitmap reference;
1791     SkString refLog;
1792     SkDynamicMemoryWStream refStream;
1793     Result refResult = this->onDraw(src, &reference, &refStream, &refLog, replayOptions,
1794                                     precompileShaders);
1795     if (!refResult.isOk()) {
1796         return refResult;
1797     }
1798     SkASSERT(!replayCache.numCacheMisses());
1799 
1800     return compare_bitmaps(reference, *dst);
1801 }
1802 
1803 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GPUDDLSink(const SkCommandLineConfigGpu * config,const GrContextOptions & ctxOptions)1804 GPUDDLSink::GPUDDLSink(const SkCommandLineConfigGpu* config, const GrContextOptions& ctxOptions)
1805         : INHERITED(config, ctxOptions)
1806         , fRecordingExecutor(SkExecutor::MakeLIFOThreadPool(1))
1807         , fGPUExecutor(SkExecutor::MakeFIFOThreadPool(1, false)) {
1808 }
1809 
ddlDraw(const Src & src,sk_sp<SkSurface> dstSurface,SkTaskGroup * recordingTaskGroup,SkTaskGroup * gpuTaskGroup,sk_gpu_test::TestContext * gpuTestCtx,GrDirectContext * dContext) const1810 Result GPUDDLSink::ddlDraw(const Src& src,
1811                            sk_sp<SkSurface> dstSurface,
1812                            SkTaskGroup* recordingTaskGroup,
1813                            SkTaskGroup* gpuTaskGroup,
1814                            sk_gpu_test::TestContext* gpuTestCtx,
1815                            GrDirectContext* dContext) const {
1816 
1817     // We have to do this here bc characterization can hit the SkGpuDevice's thread guard (i.e.,
1818     // leaving it until the DDLTileHelper ctor will result in multiple threads trying to use the
1819     // same context (this thread and the gpuThread - which will be uploading textures)).
1820     GrSurfaceCharacterization dstCharacterization;
1821     SkAssertResult(dstSurface->characterize(&dstCharacterization));
1822 
1823     auto size = src.size();
1824     SkPictureRecorder recorder;
1825     Result result = src.draw(recorder.beginRecording(SkIntToScalar(size.width()),
1826                                                      SkIntToScalar(size.height())),
1827                                                      /*GraphiteTestContext=*/nullptr);
1828     if (!result.isOk()) {
1829         return result;
1830     }
1831     sk_sp<SkPicture> inputPicture(recorder.finishRecordingAsPicture());
1832 
1833     // this is our ultimate final drawing area/rect
1834     SkIRect viewport = SkIRect::MakeWH(size.fWidth, size.fHeight);
1835 
1836     auto supportedYUVADataTypes = skgpu::ganesh::SupportedTextureFormats(*dContext);
1837     DDLPromiseImageHelper promiseImageHelper(supportedYUVADataTypes);
1838     sk_sp<SkPicture> newSKP = promiseImageHelper.recreateSKP(dContext, inputPicture.get());
1839     if (!newSKP) {
1840         return Result::Fatal("GPUDDLSink: Couldn't recreate the SKP");
1841     }
1842 
1843     // 'gpuTestCtx/gpuThreadCtx' is being shifted to the gpuThread. Leave the main (this)
1844     // thread w/o a context.
1845     gpuTestCtx->makeNotCurrent();
1846 
1847     // Job one for the GPU thread is to make 'gpuTestCtx' current!
1848     gpuTaskGroup->add([gpuTestCtx] { gpuTestCtx->makeCurrent(); });
1849 
1850     // TODO: move the image upload to the utility thread
1851     promiseImageHelper.uploadAllToGPU(gpuTaskGroup, dContext);
1852 
1853     // Care must be taken when using 'gpuThreadCtx' bc it moves between the gpu-thread and this
1854     // one. About all it can be consistently used for is GrCaps access and 'defaultBackendFormat'
1855     // calls.
1856     constexpr int kNumDivisions = 3;
1857     DDLTileHelper tiles(dContext, dstCharacterization, viewport,
1858                         kNumDivisions, kNumDivisions,
1859                         /* addRandomPaddingToDst */ false);
1860 
1861     tiles.createBackendTextures(gpuTaskGroup, dContext);
1862 
1863     tiles.kickOffThreadedWork(recordingTaskGroup, gpuTaskGroup, dContext, newSKP.get());
1864 
1865     // We have to wait for the recording threads to schedule all their work on the gpu thread
1866     // before we can schedule the composition draw and the flush. Note that the gpu thread
1867     // is not blocked at this point and this thread is borrowing recording work.
1868     recordingTaskGroup->wait();
1869 
1870     // Note: at this point the recording thread(s) are stalled out w/ nothing to do.
1871 
1872     if (FLAGS_preAbandonGpuContext) {
1873         dContext->abandonContext();
1874     }
1875 
1876     // The recording threads have already scheduled the drawing of each tile's DDL on the gpu
1877     // thread. The composition DDL must be scheduled last bc it relies on the result of all
1878     // the tiles' rendering. Additionally, bc we're aliasing the tiles' backend textures,
1879     // there is nothing in the DAG to automatically force the required order.
1880     gpuTaskGroup->add([dstSurface, ddl = tiles.composeDDL()]() {
1881                           skgpu::ganesh::DrawDDL(dstSurface, ddl);
1882                       });
1883 
1884     // This should be the only explicit flush for the entire DDL draw.
1885     gpuTaskGroup->add([dContext]() {
1886                                            // We need to ensure all the GPU work is finished so
1887                                            // the following 'deleteAllFromGPU' call will work
1888                                            // on Vulkan.
1889                                            // TODO: switch over to using the promiseImage callbacks
1890                                            // to free the backendTextures. This is complicated a
1891                                            // bit by which thread possesses the direct context.
1892                                            dContext->flush();
1893                                            dContext->submit(GrSyncCpu::kYes);
1894                                        });
1895 
1896     // The backend textures are created on the gpuThread by the 'uploadAllToGPU' call.
1897     // It is simpler to also delete them at this point on the gpuThread.
1898     promiseImageHelper.deleteAllFromGPU(gpuTaskGroup, dContext);
1899 
1900     tiles.deleteBackendTextures(gpuTaskGroup, dContext);
1901 
1902     // A flush has already been scheduled on the gpu thread along with the clean up of the backend
1903     // textures so it is safe to schedule making 'gpuTestCtx' not current on the gpuThread.
1904     gpuTaskGroup->add([gpuTestCtx] { gpuTestCtx->makeNotCurrent(); });
1905 
1906     // All the work is scheduled on the gpu thread, we just need to wait
1907     gpuTaskGroup->wait();
1908 
1909     return Result::Ok();
1910 }
1911 
draw(const Src & src,SkBitmap * dst,SkWStream *,SkString * log) const1912 Result GPUDDLSink::draw(const Src& src, SkBitmap* dst, SkWStream*, SkString* log) const {
1913     GrContextOptions contextOptions = this->baseContextOptions();
1914     src.modifyGrContextOptions(&contextOptions);
1915     contextOptions.fPersistentCache = nullptr;
1916     contextOptions.fExecutor = nullptr;
1917 
1918     GrContextFactory factory(contextOptions);
1919 
1920     // This captures the context destined to be the main gpu context
1921     ContextInfo mainCtxInfo = factory.getContextInfo(this->contextType(), this->contextOverrides());
1922     sk_gpu_test::TestContext* mainTestCtx = mainCtxInfo.testContext();
1923     auto mainCtx = mainCtxInfo.directContext();
1924     if (!mainCtx) {
1925         return Result::Fatal("Could not create context.");
1926     }
1927 
1928     SkASSERT(mainCtx->priv().getGpu());
1929 
1930     // TODO: make use of 'otherCtx' for uploads & compilation
1931 #if 0
1932     // This captures the context destined to be the utility context. It is in a share group
1933     // with the main context
1934     ContextInfo otherCtxInfo = factory.getSharedContextInfo(mainCtx);
1935     sk_gpu_test::TestContext* otherTestCtx = otherCtxInfo.testContext();
1936     auto otherCtx = otherCtxInfo.directContext();
1937     if (!otherCtx) {
1938         return Result::Fatal("Cound not create shared context.");
1939     }
1940 
1941     SkASSERT(otherCtx->priv().getGpu());
1942 #endif
1943 
1944     SkTaskGroup recordingTaskGroup(*fRecordingExecutor);
1945     SkTaskGroup gpuTaskGroup(*fGPUExecutor);
1946 
1947     // Make sure 'mainCtx' is current
1948     mainTestCtx->makeCurrent();
1949 
1950     sk_sp<SkSurface> surface = this->createDstSurface(mainCtx, src.size());
1951     if (!surface) {
1952         return Result::Fatal("Could not create a surface.");
1953     }
1954 
1955     Result result = this->ddlDraw(src, surface, &recordingTaskGroup, &gpuTaskGroup,
1956                                   mainTestCtx, mainCtx);
1957     if (!result.isOk()) {
1958         return result;
1959     }
1960 
1961     // 'ddlDraw' will have made 'mainCtx' not current on the gpuThread
1962     mainTestCtx->makeCurrent();
1963 
1964     if (FLAGS_gpuStats) {
1965         mainCtx->priv().dumpCacheStats(log);
1966         mainCtx->priv().dumpGpuStats(log);
1967         mainCtx->priv().dumpContextStats(log);
1968 
1969 #if 0
1970         otherCtx->priv().dumpCacheStats(log);
1971         otherCtx->priv().dumpGpuStats(log);
1972         otherCtx->priv().dumpContextStats(log);
1973 #endif
1974     }
1975 
1976     if (!this->readBack(surface.get(), dst)) {
1977         return Result::Fatal("Could not readback from surface.");
1978     }
1979 
1980     return Result::Ok();
1981 }
1982 
1983 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
draw_skdocument(const Src & src,SkDocument * doc,SkWStream * dst)1984 static Result draw_skdocument(const Src& src, SkDocument* doc, SkWStream* dst) {
1985     if (src.size().isEmpty()) {
1986         return Result::Fatal("Source has empty dimensions");
1987     }
1988     SkASSERT(doc);
1989     int pageCount = src.pageCount();
1990     for (int i = 0; i < pageCount; ++i) {
1991         int width = src.size(i).width(), height = src.size(i).height();
1992         SkCanvas* canvas =
1993                 doc->beginPage(SkIntToScalar(width), SkIntToScalar(height));
1994         if (!canvas) {
1995             return Result::Fatal("SkDocument::beginPage(w,h) returned nullptr");
1996         }
1997         Result result = src.draw(i, canvas, /*GraphiteTestContext=*/nullptr);
1998         if (!result.isOk()) {
1999             return result;
2000         }
2001         doc->endPage();
2002     }
2003     doc->close();
2004     dst->flush();
2005     return Result::Ok();
2006 }
2007 
draw(const Src & src,SkBitmap *,SkWStream * dst,SkString *) const2008 Result PDFSink::draw(const Src& src, SkBitmap*, SkWStream* dst, SkString*) const {
2009     SkPDF::Metadata metadata;
2010     metadata.fTitle = src.name();
2011     metadata.fSubject = "rendering correctness test";
2012     metadata.fCreator = "Skia/DM";
2013     metadata.fProducer = "Skia/PDF HEAD"; // Set producer to avoid SK_MILESTONE churn.
2014     metadata.fRasterDPI = fRasterDpi;
2015     metadata.fPDFA = fPDFA;
2016     metadata.jpegDecoder = SkPDF::JPEG::Decode;
2017     metadata.jpegEncoder = SkPDF::JPEG::Encode;
2018 #if SK_PDF_TEST_EXECUTOR
2019     std::unique_ptr<SkExecutor> executor = SkExecutor::MakeFIFOThreadPool();
2020     metadata.fExecutor = executor.get();
2021 #endif
2022     auto doc = SkPDF::MakeDocument(dst, metadata);
2023     if (!doc) {
2024         return Result::Fatal("SkPDF::MakeDocument() returned nullptr");
2025     }
2026     return draw_skdocument(src, doc.get(), dst);
2027 }
2028 
2029 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
2030 
XPSSink()2031 XPSSink::XPSSink() {}
2032 
2033 #if defined(SK_SUPPORT_XPS)
make_xps_factory()2034 static SkTScopedComPtr<IXpsOMObjectFactory> make_xps_factory() {
2035     IXpsOMObjectFactory* factory;
2036     HRN(CoCreateInstance(CLSID_XpsOMObjectFactory,
2037                          nullptr,
2038                          CLSCTX_INPROC_SERVER,
2039                          IID_PPV_ARGS(&factory)));
2040     return SkTScopedComPtr<IXpsOMObjectFactory>(factory);
2041 }
2042 
draw(const Src & src,SkBitmap *,SkWStream * dst,SkString *) const2043 Result XPSSink::draw(const Src& src, SkBitmap*, SkWStream* dst, SkString*) const {
2044     SkAutoCoInitialize com;
2045     if (!com.succeeded()) {
2046         return Result::Fatal("Could not initialize COM.");
2047     }
2048     SkTScopedComPtr<IXpsOMObjectFactory> factory = make_xps_factory();
2049     if (!factory) {
2050         return Result::Fatal("Failed to create XPS Factory.");
2051     }
2052     auto doc = SkXPS::MakeDocument(dst, factory.get());
2053     if (!doc) {
2054         return Result::Fatal("SkXPS::MakeDocument() returned nullptr");
2055     }
2056     return draw_skdocument(src, doc.get(), dst);
2057 }
2058 #else
draw(const Src & src,SkBitmap *,SkWStream * dst,SkString *) const2059 Result XPSSink::draw(const Src& src, SkBitmap*, SkWStream* dst, SkString*) const {
2060     return Result::Fatal("XPS not supported on this platform.");
2061 }
2062 #endif
2063 
serial_procs_using_png()2064 static SkSerialProcs serial_procs_using_png() {
2065     static SkSerialProcs procs;
2066     procs.fImageProc = [](SkImage* img, void*) -> sk_sp<SkData> {
2067         return SkPngEncoder::Encode(as_IB(img)->directContext(), img, SkPngEncoder::Options{});
2068     };
2069     return procs;
2070 }
2071 
2072 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
2073 
SKPSink()2074 SKPSink::SKPSink() {}
2075 
draw(const Src & src,SkBitmap *,SkWStream * dst,SkString *) const2076 Result SKPSink::draw(const Src& src, SkBitmap*, SkWStream* dst, SkString*) const {
2077     auto size = SkSize::Make(src.size());
2078     SkPictureRecorder recorder;
2079     Result result = src.draw(recorder.beginRecording(size.width(), size.height()),
2080                              /*GraphiteTestContext=*/nullptr);
2081     if (!result.isOk()) {
2082         return result;
2083     }
2084     SkSerialProcs procs = serial_procs_using_png();
2085     recorder.finishRecordingAsPicture()->serialize(dst, &procs);
2086     return Result::Ok();
2087 }
2088 
2089 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
2090 
draw(const Src & src,SkBitmap *,SkWStream * dst,SkString *) const2091 Result DebugSink::draw(const Src& src, SkBitmap*, SkWStream* dst, SkString*) const {
2092     DebugCanvas debugCanvas(src.size().width(), src.size().height());
2093     Result result = src.draw(&debugCanvas, /*GraphiteTestContext=*/nullptr);
2094     if (!result.isOk()) {
2095         return result;
2096     }
2097     std::unique_ptr<SkCanvas> nullCanvas = SkMakeNullCanvas();
2098     UrlDataManager dataManager(SkString("data"));
2099     SkJSONWriter writer(dst, SkJSONWriter::Mode::kPretty);
2100     writer.beginObject(); // root
2101     debugCanvas.toJSON(writer, dataManager, nullCanvas.get());
2102     writer.endObject(); // root
2103     writer.flush();
2104     return Result::Ok();
2105 }
2106 
2107 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
2108 
SVGSink(int pageIndex)2109 SVGSink::SVGSink(int pageIndex) : fPageIndex(pageIndex) {}
2110 
draw(const Src & src,SkBitmap *,SkWStream * dst,SkString *) const2111 Result SVGSink::draw(const Src& src, SkBitmap*, SkWStream* dst, SkString*) const {
2112 #if defined(SK_ENABLE_SVG)
2113     if (src.pageCount() > 1) {
2114         int pageCount = src.pageCount();
2115         if (fPageIndex > pageCount - 1) {
2116             return Result::Fatal("Page index %d too high for document with only %d pages.",
2117                                  fPageIndex, pageCount);
2118         }
2119     }
2120     return src.draw(fPageIndex,
2121                     SkSVGCanvas::Make(SkRect::MakeWH(SkIntToScalar(src.size().width()),
2122                                                      SkIntToScalar(src.size().height())),
2123                                       dst)
2124                             .get(),
2125                     /*GraphiteTestContext=*/nullptr);
2126 #else
2127     (void)fPageIndex;
2128     return Result::Fatal("SVG sink is disabled.");
2129 #endif // SK_ENABLE_SVG
2130 }
2131 
2132 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
2133 
RasterSink(SkColorType colorType)2134 RasterSink::RasterSink(SkColorType colorType)
2135     : fColorType(colorType) {}
2136 
draw(const Src & src,SkBitmap * dst,SkWStream *,SkString *) const2137 Result RasterSink::draw(const Src& src, SkBitmap* dst, SkWStream*, SkString*) const {
2138     const SkISize size = src.size();
2139     if (size.isEmpty()) {
2140         return Result(Result::Status::Skip,
2141                       SkStringPrintf("Skipping empty source: %s", src.name().c_str()));
2142     }
2143 
2144     dst->allocPixelsFlags(SkImageInfo::Make(size, this->colorInfo()),
2145                           SkBitmap::kZeroPixels_AllocFlag);
2146 
2147     SkSurfaceProps props(/*flags=*/0, kRGB_H_SkPixelGeometry);
2148     auto surface = SkSurfaces::WrapPixels(dst->pixmap(), &props);
2149     return src.draw(surface->getCanvas(), /*GraphiteTestContext=*/nullptr);
2150 }
2151 
2152 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
2153 
2154 #if defined(SK_GRAPHITE)
2155 
GraphiteSink(const SkCommandLineConfigGraphite * config,const skiatest::graphite::TestOptions & options)2156 GraphiteSink::GraphiteSink(const SkCommandLineConfigGraphite* config,
2157                            const skiatest::graphite::TestOptions& options)
2158         : fOptions(options)
2159         , fContextType(config->getContextType())
2160         , fColorType(config->getColorType())
2161         , fAlphaType(config->getAlphaType()) {}
2162 
draw(const Src & src,SkBitmap * dst,SkWStream * dstStream,SkString * log) const2163 Result GraphiteSink::draw(const Src& src,
2164                           SkBitmap* dst,
2165                           SkWStream* dstStream,
2166                           SkString* log) const {
2167     skiatest::graphite::TestOptions options = fOptions;
2168     // If we've copied context options from an external source we can't trust that the
2169     // priv pointer is still in scope, so assume it should be NULL and set our own up.
2170     SkASSERT(!options.fContextOptions.fOptionsPriv);
2171     skgpu::graphite::ContextOptionsPriv optionsPriv;
2172     options.fContextOptions.fOptionsPriv = &optionsPriv;
2173 
2174     src.modifyGraphiteContextOptions(&options.fContextOptions);
2175 
2176     skiatest::graphite::ContextFactory factory(options);
2177     skiatest::graphite::ContextInfo ctxInfo = factory.getContextInfo(fContextType);
2178     skgpu::graphite::Context* context = ctxInfo.fContext;
2179     if (!context) {
2180         return Result::Fatal("Could not create a context.");
2181     }
2182 
2183     std::unique_ptr<skgpu::graphite::Recorder> recorder =
2184                                 context->makeRecorder(ToolUtils::CreateTestingRecorderOptions());
2185     if (!recorder) {
2186         return Result::Fatal("Could not create a recorder.");
2187     }
2188 
2189     {
2190         sk_sp<SkSurface> surface = this->makeSurface(recorder.get(), src.size());
2191         if (!surface) {
2192             return Result::Fatal("Could not create a surface.");
2193         }
2194         dst->allocPixels(surface->imageInfo());
2195         Result result = src.draw(surface->getCanvas(), ctxInfo.fTestContext);
2196         if (!result.isOk()) {
2197             return result;
2198         }
2199 
2200         SkPixmap pm;
2201         if (!dst->peekPixels(&pm) ||
2202             !surface->readPixels(pm, 0, 0)) {
2203             return Result::Fatal("Could not readback from surface.");
2204         }
2205     }
2206 
2207     std::unique_ptr<skgpu::graphite::Recording> recording = recorder->snap();
2208     if (!recording) {
2209         return Result::Fatal("Could not create a recording.");
2210     }
2211 
2212     skgpu::graphite::InsertRecordingInfo info;
2213     info.fRecording = recording.get();
2214     if (!context->insertRecording(info)) {
2215         return Result::Fatal("Context::insertRecording failed.");
2216     }
2217     ctxInfo.fTestContext->syncedSubmit(context);
2218 
2219     return Result::Ok();
2220 }
2221 
makeSurface(skgpu::graphite::Recorder * recorder,SkISize dimensions) const2222 sk_sp<SkSurface> GraphiteSink::makeSurface(skgpu::graphite::Recorder* recorder,
2223                                            SkISize dimensions) const {
2224     SkSurfaceProps props(0, kRGB_H_SkPixelGeometry);
2225     auto ii = SkImageInfo::Make(dimensions, this->colorInfo());
2226 
2227 #if defined(SK_DAWN)
2228     if (fOptions.fUseWGPUTextureView) {
2229         return sk_gpu_test::MakeBackendTextureViewSurface(recorder,
2230                                                           ii,
2231                                                           skgpu::Mipmapped::kNo,
2232                                                           skgpu::Protected::kNo,
2233                                                           &props);
2234     }
2235 #endif // SK_DAWN
2236 
2237     return SkSurfaces::RenderTarget(recorder, ii, skgpu::Mipmapped::kNo, &props);
2238 }
2239 
2240 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
2241 
2242 #if defined(SK_ENABLE_PRECOMPILE)
2243 
GraphitePrecompileTestingSink(const SkCommandLineConfigGraphite * config,const skiatest::graphite::TestOptions & options)2244 GraphitePrecompileTestingSink::GraphitePrecompileTestingSink(
2245         const SkCommandLineConfigGraphite* config,
2246         const skiatest::graphite::TestOptions& options) : GraphiteSink(config, options) {}
2247 
~GraphitePrecompileTestingSink()2248 GraphitePrecompileTestingSink::~GraphitePrecompileTestingSink() {}
2249 
drawSrc(const Src & src,skgpu::graphite::Context * context,skiatest::graphite::GraphiteTestContext * testContext,skgpu::graphite::Recorder * recorder) const2250 Result GraphitePrecompileTestingSink::drawSrc(
2251         const Src& src,
2252         skgpu::graphite::Context* context,
2253         skiatest::graphite::GraphiteTestContext* testContext,
2254         skgpu::graphite::Recorder* recorder) const {
2255 
2256     sk_sp<SkSurface> surface = this->makeSurface(recorder, src.size());
2257     if (!surface) {
2258         return Result::Fatal("Could not create a surface.");
2259     }
2260     Result result = src.draw(surface->getCanvas(), testContext);
2261     if (!result.isOk()) {
2262         return result;
2263     }
2264 
2265     std::unique_ptr<skgpu::graphite::Recording> recording = recorder->snap();
2266     if (!recording) {
2267         return Result::Fatal("Could not create a recording.");
2268     }
2269 
2270     skgpu::graphite::InsertRecordingInfo info;
2271     info.fRecording = recording.get();
2272     if (!context->insertRecording(info)) {
2273         return Result::Fatal("Context::insertRecording failed.");
2274     }
2275     if (!context->submit(skgpu::graphite::SyncToCpu::kYes)) {
2276         return Result::Fatal("Context::submit failed.");
2277     }
2278 
2279     return Result::Ok();
2280 }
2281 
resetAndRecreatePipelines(skiatools::graphite::PipelineCallBackHandler * handler,skgpu::graphite::PrecompileContext * precompileContext) const2282 Result GraphitePrecompileTestingSink::resetAndRecreatePipelines(
2283         skiatools::graphite::PipelineCallBackHandler* handler,
2284         skgpu::graphite::PrecompileContext* precompileContext) const {
2285     using namespace skgpu::graphite;
2286 
2287     GlobalCache* globalCache = precompileContext->priv().globalCache();
2288 
2289     std::vector<skgpu::UniqueKey> origKeys;
2290 
2291     UniqueKeyUtils::FetchUniqueKeys(precompileContext, &origKeys);
2292 
2293     std::vector<sk_sp<SkData>> androidStyleKeys;
2294 
2295     handler->retrieve(&androidStyleKeys);
2296     handler->reset();
2297 
2298     SkASSERTF_RELEASE(origKeys.size() == androidStyleKeys.size(),
2299                       "orig %zu != new %zu", origKeys.size(), androidStyleKeys.size());
2300 
2301     int numBeforeReset = globalCache->numGraphicsPipelines();
2302     SkASSERT_RELEASE(numBeforeReset == (int) origKeys.size());
2303 
2304     globalCache->resetGraphicsPipelines();
2305 
2306     SkASSERT_RELEASE(globalCache->numGraphicsPipelines() == 0);
2307 
2308     for (sk_sp<SkData>& d : androidStyleKeys) {
2309         bool result = precompileContext->precompile(d);
2310         SkAssertResult(result);
2311     }
2312 
2313     int postRecreate = globalCache->numGraphicsPipelines();
2314 
2315     SkASSERT_RELEASE(numBeforeReset == postRecreate);
2316 
2317     {
2318         std::vector<skgpu::UniqueKey> recreatedKeys;
2319 
2320         UniqueKeyUtils::FetchUniqueKeys(precompileContext, &recreatedKeys);
2321 
2322         CompareKeys(precompileContext,
2323                     origKeys, "original",
2324                     recreatedKeys, "recreated");
2325     }
2326 
2327     return Result::Ok();
2328 }
2329 
2330 
2331 #ifdef SK_DEBUG
LogMissingKey(skgpu::graphite::PrecompileContext * precompileContext,const skgpu::UniqueKey & missingKey,const char * missingKeyName,const std::vector<skgpu::UniqueKey> & pool,const char * poolName)2332 void GraphitePrecompileTestingSink::LogMissingKey(
2333         skgpu::graphite::PrecompileContext* precompileContext,
2334         const skgpu::UniqueKey& missingKey,
2335         const char* missingKeyName,
2336         const std::vector<skgpu::UniqueKey>& pool,
2337         const char* poolName) {
2338     using namespace skgpu::graphite;
2339 
2340     {
2341         GraphicsPipelineDesc originalPipelineDesc;
2342         RenderPassDesc originalRenderPassDesc;
2343         bool extracted = UniqueKeyUtils::ExtractKeyDescs(precompileContext, missingKey,
2344                                                          &originalPipelineDesc,
2345                                                          &originalRenderPassDesc);
2346 
2347         SkDebugf("------- Key missing from %s keys:\n", poolName);
2348         missingKey.dump(missingKeyName);
2349         if (extracted) {
2350             UniqueKeyUtils::DumpDescs(precompileContext,
2351                                       originalPipelineDesc,
2352                                       originalRenderPassDesc);
2353         }
2354     }
2355 
2356     SkDebugf("Have %d %s keys -----------------\n", (int) pool.size(), poolName);
2357     int count = 0;
2358     for (const skgpu::UniqueKey& b : pool) {
2359 
2360         GraphicsPipelineDesc recreatedPipelineDesc;
2361         RenderPassDesc recreatedRenderPassDesc;
2362         bool extracted = UniqueKeyUtils::ExtractKeyDescs(precompileContext, b,
2363                                                          &recreatedPipelineDesc,
2364                                                          &recreatedRenderPassDesc);
2365 
2366         SkDebugf("%d: ----\n", count++);
2367         b.dump("recreated key:");
2368         if (extracted) {
2369             UniqueKeyUtils::DumpDescs(precompileContext,
2370                                       recreatedPipelineDesc,
2371                                       recreatedRenderPassDesc);
2372         }
2373     }
2374 }
2375 #endif
2376 
CompareKeys(skgpu::graphite::PrecompileContext * precompileContext,const std::vector<skgpu::UniqueKey> & vA,const char * aName,const std::vector<skgpu::UniqueKey> & vB,const char * bName)2377 void GraphitePrecompileTestingSink::CompareKeys(
2378         skgpu::graphite::PrecompileContext* precompileContext,
2379         const std::vector<skgpu::UniqueKey>& vA,
2380         const char* aName,
2381         const std::vector<skgpu::UniqueKey>& vB,
2382         const char* bName) {
2383 
2384     for (const skgpu::UniqueKey& a : vA) {
2385         if (std::find(vB.begin(), vB.end(), a) == vB.end()) {
2386 #ifdef SK_DEBUG
2387             LogMissingKey(precompileContext, a, aName, vB, bName);
2388 #endif
2389 
2390             SK_ABORT("missing");
2391         }
2392     }
2393 }
2394 
draw(const Src & src,SkBitmap * dst,SkWStream * dstStream,SkString * log) const2395 Result GraphitePrecompileTestingSink::draw(const Src& src,
2396                                            SkBitmap* dst,
2397                                            SkWStream* dstStream,
2398                                            SkString* log) const {
2399     using namespace skgpu::graphite;
2400     using namespace skiatest::graphite;
2401     using namespace skiatools::graphite;
2402 
2403     std::unique_ptr<PipelineCallBackHandler> pipelineHandler(new PipelineCallBackHandler);
2404 
2405     {
2406         TestOptions options = fOptions;
2407         // If we've copied context options from an external source we can't trust that the
2408         // priv pointer is still in scope, so assume it should be NULL and set our own up.
2409         SkASSERT(!options.fContextOptions.fOptionsPriv);
2410         ContextOptionsPriv optionsPriv;
2411         options.fContextOptions.fOptionsPriv = &optionsPriv;
2412 
2413         SkASSERT(!options.fContextOptions.fPipelineCallbackContext);
2414         SkASSERT(!options.fContextOptions.fPipelineCallback);
2415         options.fContextOptions.fPipelineCallbackContext = pipelineHandler.get();
2416         options.fContextOptions.fPipelineCallback = PipelineCallBackHandler::CallBack;
2417 
2418         src.modifyGraphiteContextOptions(&options.fContextOptions);
2419 
2420         ContextFactory factory(options);
2421         skiatest::graphite::ContextInfo ctxInfo = factory.getContextInfo(fContextType);
2422         Context* context = ctxInfo.fContext;
2423         if (!context) {
2424             return Result::Fatal("Could not create a context.");
2425         }
2426 
2427         std::unique_ptr<skgpu::graphite::Recorder> recorder =
2428                 context->makeRecorder(ToolUtils::CreateTestingRecorderOptions());
2429         if (!recorder) {
2430             return Result::Fatal("Could not create a recorder.");
2431         }
2432         std::unique_ptr<PrecompileContext> precompileContext = context->makePrecompileContext();
2433         ShaderCodeDictionary* shaderCodeDictionary = context->priv().shaderCodeDictionary();
2434 
2435         SkASSERT_RELEASE(!context->priv().globalCache()->numGraphicsPipelines());
2436         SkASSERT_RELEASE(!shaderCodeDictionary->numUserDefinedRuntimeEffects());
2437 
2438         // Draw the Src for the first time, populating the global pipeline cache.
2439         Result result = this->drawSrc(src, context, ctxInfo.fTestContext, recorder.get());
2440         if (!result.isOk()) {
2441             return result;
2442         }
2443 
2444         // Note: this is different than numUserDefinedKnownRuntimeEffects! Known user-defined
2445         // runtime effects are allowed while unknown ones are not.
2446         if (shaderCodeDictionary->numUserDefinedRuntimeEffects()) {
2447             return Result::Skip("User-defined runtime effects cannot be serialized");
2448         }
2449 
2450         // Call resetAndRecreatePipelines to clear out all the Pipelines in the global cache and
2451         // then regenerate them using the Precompilation system.
2452         result = this->resetAndRecreatePipelines(pipelineHandler.get(), precompileContext.get());
2453         if (!result.isOk()) {
2454             return result;
2455         }
2456 
2457         GlobalCache* globalCache = precompileContext->priv().globalCache();
2458         int numBeforeSecondDraw = globalCache->numGraphicsPipelines();
2459 
2460         // Draw the Src for the second time. This shouldn't create any new Pipelines since the ones
2461         // generated via Precompilation should be sufficient.
2462         result = this->drawSrc(src, context, ctxInfo.fTestContext, recorder.get());
2463         if (!result.isOk()) {
2464             return result;
2465         }
2466 
2467         SkASSERT_RELEASE(numBeforeSecondDraw == globalCache->numGraphicsPipelines());
2468     }
2469 
2470     return Result::Ok();
2471 }
2472 #endif // SK_ENABLE_PRECOMPILE
2473 
2474 #endif // SK_GRAPHITE
2475 
2476 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
2477 
2478 // Handy for front-patching a Src.  Do whatever up-front work you need, then call draw_to_canvas(),
2479 // passing the Sink draw() arguments, a size, and a function draws into an SkCanvas.
2480 // Several examples below.
2481 
2482 using DrawToCanvasFn = std::function<DM::Result(SkCanvas*, Src::GraphiteTestContext*)>;
2483 
draw_to_canvas(Sink * sink,SkBitmap * bitmap,SkWStream * stream,SkString * log,SkISize size,const DrawToCanvasFn & draw)2484 static Result draw_to_canvas(Sink* sink, SkBitmap* bitmap, SkWStream* stream,
2485                              SkString* log, SkISize size, const DrawToCanvasFn& draw) {
2486     class ProxySrc : public Src {
2487     public:
2488         ProxySrc(SkISize size, const DrawToCanvasFn& draw) : fSize(size), fDraw(draw) {}
2489         Result draw(SkCanvas* canvas, GraphiteTestContext* testContext) const override {
2490             return fDraw(canvas, testContext);
2491         }
2492         Name    name() const override { return "ProxySrc"; }
2493         SkISize size() const override { return fSize; }
2494     private:
2495         SkISize               fSize;
2496         const DrawToCanvasFn& fDraw;
2497     };
2498     return sink->draw(ProxySrc(size, draw), bitmap, stream, log);
2499 }
2500 
2501 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
2502 
2503 static DEFINE_bool(check, true, "If true, have most Via- modes fail if they affect the output.");
2504 
2505 // Is *bitmap identical to what you get drawing src into sink?
check_against_reference(const SkBitmap * bitmap,const Src & src,Sink * sink)2506 static Result check_against_reference(const SkBitmap* bitmap, const Src& src, Sink* sink) {
2507     // We can only check raster outputs.
2508     // (Non-raster outputs like .pdf, .skp, .svg may differ but still draw identically.)
2509     if (FLAGS_check && bitmap) {
2510         SkBitmap reference;
2511         SkString log;
2512         SkDynamicMemoryWStream wStream;
2513         Result result = sink->draw(src, &reference, &wStream, &log);
2514         // If we can draw into this Sink via some pipeline, we should be able to draw directly.
2515         SkASSERT(result.isOk());
2516         if (!result.isOk()) {
2517             return result;
2518         }
2519         return compare_bitmaps(reference, *bitmap);
2520     }
2521     return Result::Ok();
2522 }
2523 
2524 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
2525 
auto_compute_translate(SkMatrix * matrix,int srcW,int srcH)2526 static SkISize auto_compute_translate(SkMatrix* matrix, int srcW, int srcH) {
2527     SkRect bounds = SkRect::MakeIWH(srcW, srcH);
2528     matrix->mapRect(&bounds);
2529     matrix->postTranslate(-bounds.x(), -bounds.y());
2530     return {SkScalarRoundToInt(bounds.width()), SkScalarRoundToInt(bounds.height())};
2531 }
2532 
ViaMatrix(SkMatrix matrix,Sink * sink)2533 ViaMatrix::ViaMatrix(SkMatrix matrix, Sink* sink) : Via(sink), fMatrix(matrix) {}
2534 
draw(const Src & src,SkBitmap * bitmap,SkWStream * stream,SkString * log) const2535 Result ViaMatrix::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
2536     SkMatrix matrix = fMatrix;
2537     SkISize size = auto_compute_translate(&matrix, src.size().width(), src.size().height());
2538     return draw_to_canvas(fSink.get(), bitmap, stream, log, size,
2539                           [&](SkCanvas* canvas,
2540                               Src::GraphiteTestContext* testContext) {
2541                               canvas->concat(matrix);
2542                               return src.draw(canvas, testContext);
2543                           });
2544 }
2545 
2546 // Undoes any flip or 90 degree rotate without changing the scale of the bitmap.
2547 // This should be pixel-preserving.
ViaUpright(SkMatrix matrix,Sink * sink)2548 ViaUpright::ViaUpright(SkMatrix matrix, Sink* sink) : Via(sink), fMatrix(matrix) {}
2549 
draw(const Src & src,SkBitmap * bitmap,SkWStream * stream,SkString * log) const2550 Result ViaUpright::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
2551     Result result = fSink->draw(src, bitmap, stream, log);
2552     if (!result.isOk()) {
2553         return result;
2554     }
2555 
2556     SkMatrix inverse;
2557     if (!fMatrix.rectStaysRect() || !fMatrix.invert(&inverse)) {
2558         return Result::Fatal("Cannot upright --matrix.");
2559     }
2560     SkMatrix upright = SkMatrix::I();
2561     upright.setScaleX(SkScalarSignAsScalar(inverse.getScaleX()));
2562     upright.setScaleY(SkScalarSignAsScalar(inverse.getScaleY()));
2563     upright.setSkewX(SkScalarSignAsScalar(inverse.getSkewX()));
2564     upright.setSkewY(SkScalarSignAsScalar(inverse.getSkewY()));
2565 
2566     SkBitmap uprighted;
2567     SkISize size = auto_compute_translate(&upright, bitmap->width(), bitmap->height());
2568     uprighted.allocPixels(bitmap->info().makeDimensions(size));
2569 
2570     SkCanvas canvas(uprighted);
2571     canvas.concat(upright);
2572     SkPaint paint;
2573     paint.setBlendMode(SkBlendMode::kSrc);
2574     canvas.drawImage(bitmap->asImage(), 0, 0, SkSamplingOptions(), &paint);
2575 
2576     *bitmap = uprighted;
2577     return Result::Ok();
2578 }
2579 
2580 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
2581 
draw(const Src & src,SkBitmap * bitmap,SkWStream * stream,SkString * log) const2582 Result ViaSerialization::draw(
2583         const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
2584     // Record our Src into a picture.
2585     auto size = src.size();
2586     SkPictureRecorder recorder;
2587     Result result = src.draw(recorder.beginRecording(SkIntToScalar(size.width()),
2588                                                      SkIntToScalar(size.height())),
2589                                                      /*GraphiteTestContext=*/nullptr);
2590     if (!result.isOk()) {
2591         return result;
2592     }
2593     sk_sp<SkPicture> pic(recorder.finishRecordingAsPicture());
2594 
2595     SkSerialProcs procs = serial_procs_using_png();
2596     // Serialize it and then deserialize it.
2597     sk_sp<SkPicture> deserialized = SkPicture::MakeFromData(pic->serialize(&procs).get());
2598 
2599     result = draw_to_canvas(fSink.get(), bitmap, stream, log, size,
2600                             [&](SkCanvas* canvas, Src::GraphiteTestContext*) {
2601                                 canvas->drawPicture(deserialized);
2602                                 return Result::Ok();
2603                             });
2604     if (!result.isOk()) {
2605         return result;
2606     }
2607 
2608     return check_against_reference(bitmap, src, fSink.get());
2609 }
2610 
2611 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
2612 
draw(const Src & src,SkBitmap * bitmap,SkWStream * stream,SkString * log) const2613 Result ViaPicture::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
2614     auto size = src.size();
2615     Result result = draw_to_canvas(fSink.get(), bitmap, stream, log, size,
2616                                    [&](SkCanvas* canvas, Src::GraphiteTestContext* testContext) {
2617         SkPictureRecorder recorder;
2618         sk_sp<SkPicture> pic;
2619         Result result = src.draw(recorder.beginRecording(SkIntToScalar(size.width()),
2620                                                          SkIntToScalar(size.height())),
2621                                  testContext);
2622         if (!result.isOk()) {
2623             return result;
2624         }
2625         pic = recorder.finishRecordingAsPicture();
2626         canvas->drawPicture(pic);
2627         return result;
2628     });
2629     if (!result.isOk()) {
2630         return result;
2631     }
2632 
2633     return check_against_reference(bitmap, src, fSink.get());
2634 }
2635 
2636 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
2637 
draw(const Src & src,SkBitmap * bitmap,SkWStream * stream,SkString * log) const2638 Result ViaRuntimeBlend::draw(const Src& src,
2639                              SkBitmap* bitmap,
2640                              SkWStream* stream,
2641                              SkString* log) const {
2642     class RuntimeBlendFilterCanvas : public SkPaintFilterCanvas {
2643     public:
2644         RuntimeBlendFilterCanvas(SkCanvas* canvas) : INHERITED(canvas) { }
2645 
2646     protected:
2647         bool onFilter(SkPaint& paint) const override {
2648             if (std::optional<SkBlendMode> mode = paint.asBlendMode()) {
2649                 paint.setBlender(GetRuntimeBlendForBlendMode(*mode));
2650             }
2651             return true;
2652         }
2653 
2654     private:
2655         using INHERITED = SkPaintFilterCanvas;
2656     };
2657 
2658     return draw_to_canvas(fSink.get(), bitmap, stream, log, src.size(),
2659                           [&](SkCanvas* canvas, Src::GraphiteTestContext* testContext) {
2660         RuntimeBlendFilterCanvas runtimeBlendCanvas{canvas};
2661         return src.draw(&runtimeBlendCanvas, testContext);
2662     });
2663 }
2664 
2665 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
2666 
2667 #ifdef TEST_VIA_SVG
draw(const Src & src,SkBitmap * bitmap,SkWStream * stream,SkString * log) const2668 Result ViaSVG::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
2669     auto size = src.size();
2670     return draw_to_canvas(fSink.get(), bitmap, stream, log, size,
2671                           [&](SkCanvas* canvas, Src::GraphiteTestContext* testContext) -> Result {
2672         SkDynamicMemoryWStream wstream;
2673         SkXMLStreamWriter writer(&wstream);
2674         Result result = src.draw(SkSVGCanvas::Make(SkRect::Make(size), &writer).get(),
2675                                  testContext);
2676         if (!result.isOk()) {
2677             return result;
2678         }
2679 
2680         auto shapingFactory = SkShapers::BestAvailable();
2681         auto fontMgr = ToolUtils::TestFontMgr();
2682         // When rendering our SVGs we want to be sure we are using shaping.
2683         // If we fail to make a shaper, then it can mean something like skunicode is misconfigured.
2684         SkASSERT(shapingFactory->makeShaper(fontMgr));
2685 
2686         std::unique_ptr<SkStream> rstream(wstream.detachAsStream());
2687         sk_sp<SkSVGDOM> dom = SkSVGDOM::Builder()
2688                                       .setFontManager(std::move(fontMgr))
2689                                       .setTextShapingFactory(std::move(shapingFactory))
2690                                       .make(*rstream);
2691         if (dom) {
2692             dom->setContainerSize(SkSize::Make(size));
2693             dom->render(canvas);
2694         }
2695         return Result::Ok();
2696     });
2697 }
2698 #endif
2699 
2700 }  // namespace DM
2701