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