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