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