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