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