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