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/core/SkColorSpace.h"
12 #include "include/core/SkData.h"
13 #include "include/core/SkDeferredDisplayListRecorder.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/SkMultiPictureDraw.h"
19 #include "include/core/SkPictureRecorder.h"
20 #include "include/core/SkStream.h"
21 #include "include/core/SkSurface.h"
22 #include "include/core/SkSurfaceCharacterization.h"
23 #include "include/docs/SkPDFDocument.h"
24 #include "include/gpu/GrBackendSurface.h"
25 #include "include/ports/SkImageGeneratorCG.h"
26 #include "include/ports/SkImageGeneratorWIC.h"
27 #include "include/private/SkImageInfoPriv.h"
28 #include "include/private/SkTLogic.h"
29 #include "include/third_party/skcms/skcms.h"
30 #include "include/utils/SkNullCanvas.h"
31 #include "include/utils/SkRandom.h"
32 #include "src/codec/SkCodecImageGenerator.h"
33 #include "src/codec/SkSwizzler.h"
34 #include "src/core/SkAutoMalloc.h"
35 #include "src/core/SkAutoPixmapStorage.h"
36 #include "src/core/SkMakeUnique.h"
37 #include "src/core/SkOSFile.h"
38 #include "src/core/SkOpts.h"
39 #include "src/core/SkPictureCommon.h"
40 #include "src/core/SkPictureData.h"
41 #include "src/core/SkRecordDraw.h"
42 #include "src/core/SkRecorder.h"
43 #include "src/core/SkTaskGroup.h"
44 #include "src/gpu/GrContextPriv.h"
45 #include "src/gpu/GrGpu.h"
46 #include "src/utils/SkMultiPictureDocumentPriv.h"
47 #include "src/utils/SkOSPath.h"
48 #include "tools/DDLPromiseImageHelper.h"
49 #include "tools/DDLTileHelper.h"
50 #include "tools/Resources.h"
51 #include "tools/debugger/DebugCanvas.h"
52 #include "tools/gpu/MemoryCache.h"
53 #if defined(SK_BUILD_FOR_WIN)
54 #include "include/docs/SkXPSDocument.h"
55 #include "src/utils/win/SkAutoCoInitialize.h"
56 #include "src/utils/win/SkHRESULT.h"
57 #include "src/utils/win/SkTScopedComPtr.h"
58 #include <XpsObjectModel.h>
59 #endif
60
61 #if defined(SK_ENABLE_SKOTTIE)
62 #include "modules/skottie/include/Skottie.h"
63 #include "modules/skottie/utils/SkottieUtils.h"
64 #endif
65
66 #if defined(SK_XML)
67 #include "experimental/svg/model/SkSVGDOM.h"
68 #include "include/svg/SkSVGCanvas.h"
69 #include "src/xml/SkXMLWriter.h"
70 #endif
71 #include "tests/TestUtils.h"
72
73 #include <cmath>
74 #include <functional>
75
76 static DEFINE_bool(multiPage, false,
77 "For document-type backends, render the source into multiple pages");
78 static DEFINE_bool(RAW_threading, true, "Allow RAW decodes to run on multiple threads?");
79
80 DECLARE_int(gpuThreads);
81
82 using sk_gpu_test::GrContextFactory;
83
84 namespace DM {
85
GMSrc(skiagm::GMFactory factory)86 GMSrc::GMSrc(skiagm::GMFactory factory) : fFactory(factory) {}
87
draw(SkCanvas * canvas) const88 Error GMSrc::draw(SkCanvas* canvas) const {
89 std::unique_ptr<skiagm::GM> gm(fFactory());
90 SkString errorMsg;
91 skiagm::DrawResult drawResult = gm->draw(canvas, &errorMsg);
92 if (skiagm::DrawResult::kSkip == drawResult) {
93 return Error::Nonfatal(std::move(errorMsg)); // Cause this test to be skipped.
94 }
95 return errorMsg;
96 }
97
size() const98 SkISize GMSrc::size() const {
99 std::unique_ptr<skiagm::GM> gm(fFactory());
100 return gm->getISize();
101 }
102
name() const103 Name GMSrc::name() const {
104 std::unique_ptr<skiagm::GM> gm(fFactory());
105 return gm->getName();
106 }
107
modifyGrContextOptions(GrContextOptions * options) const108 void GMSrc::modifyGrContextOptions(GrContextOptions* options) const {
109 std::unique_ptr<skiagm::GM> gm(fFactory());
110 gm->modifyGrContextOptions(options);
111 }
112
113 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
114
BRDSrc(Path path,Mode mode,CodecSrc::DstColorType dstColorType,uint32_t sampleSize)115 BRDSrc::BRDSrc(Path path, Mode mode, CodecSrc::DstColorType dstColorType, uint32_t sampleSize)
116 : fPath(path)
117 , fMode(mode)
118 , fDstColorType(dstColorType)
119 , fSampleSize(sampleSize)
120 {}
121
veto(SinkFlags flags) const122 bool BRDSrc::veto(SinkFlags flags) const {
123 // No need to test to non-raster or indirect backends.
124 return flags.type != SinkFlags::kRaster
125 || flags.approach != SinkFlags::kDirect;
126 }
127
create_brd(Path path)128 static SkBitmapRegionDecoder* create_brd(Path path) {
129 sk_sp<SkData> encoded(SkData::MakeFromFileName(path.c_str()));
130 if (!encoded) {
131 return nullptr;
132 }
133 return SkBitmapRegionDecoder::Create(encoded, SkBitmapRegionDecoder::kAndroidCodec_Strategy);
134 }
135
alpha8_to_gray8(SkBitmap * bitmap)136 static inline void alpha8_to_gray8(SkBitmap* bitmap) {
137 // Android requires kGray8 bitmaps to be tagged as kAlpha8. Here we convert
138 // them back to kGray8 so our test framework can draw them correctly.
139 if (kAlpha_8_SkColorType == bitmap->info().colorType()) {
140 SkImageInfo newInfo = bitmap->info().makeColorType(kGray_8_SkColorType)
141 .makeAlphaType(kOpaque_SkAlphaType);
142 *const_cast<SkImageInfo*>(&bitmap->info()) = newInfo;
143 }
144 }
145
draw(SkCanvas * canvas) const146 Error BRDSrc::draw(SkCanvas* canvas) const {
147 SkColorType colorType = canvas->imageInfo().colorType();
148 if (kRGB_565_SkColorType == colorType &&
149 CodecSrc::kGetFromCanvas_DstColorType != fDstColorType) {
150 return Error::Nonfatal("Testing non-565 to 565 is uninteresting.");
151 }
152 switch (fDstColorType) {
153 case CodecSrc::kGetFromCanvas_DstColorType:
154 break;
155 case CodecSrc::kGrayscale_Always_DstColorType:
156 colorType = kGray_8_SkColorType;
157 break;
158 default:
159 SkASSERT(false);
160 break;
161 }
162
163 std::unique_ptr<SkBitmapRegionDecoder> brd(create_brd(fPath));
164 if (nullptr == brd.get()) {
165 return Error::Nonfatal(SkStringPrintf("Could not create brd for %s.", fPath.c_str()));
166 }
167
168 auto recommendedCT = brd->computeOutputColorType(colorType);
169 if (kRGB_565_SkColorType == colorType && recommendedCT != colorType) {
170 return Error::Nonfatal("Skip decoding non-opaque to 565.");
171 }
172 colorType = recommendedCT;
173
174 auto colorSpace = brd->computeOutputColorSpace(colorType, nullptr);
175
176 const uint32_t width = brd->width();
177 const uint32_t height = brd->height();
178 // Visually inspecting very small output images is not necessary.
179 if ((width / fSampleSize <= 10 || height / fSampleSize <= 10) && 1 != fSampleSize) {
180 return Error::Nonfatal("Scaling very small images is uninteresting.");
181 }
182 switch (fMode) {
183 case kFullImage_Mode: {
184 SkBitmap bitmap;
185 if (!brd->decodeRegion(&bitmap, nullptr, SkIRect::MakeXYWH(0, 0, width, height),
186 fSampleSize, colorType, false, colorSpace)) {
187 return "Cannot decode (full) region.";
188 }
189 alpha8_to_gray8(&bitmap);
190
191 canvas->drawBitmap(bitmap, 0, 0);
192 return "";
193 }
194 case kDivisor_Mode: {
195 const uint32_t divisor = 2;
196 if (width < divisor || height < divisor) {
197 return Error::Nonfatal("Divisor is larger than image dimension.");
198 }
199
200 // Use a border to test subsets that extend outside the image.
201 // We will not allow the border to be larger than the image dimensions. Allowing
202 // these large borders causes off by one errors that indicate a problem with the
203 // test suite, not a problem with the implementation.
204 const uint32_t maxBorder = SkTMin(width, height) / (fSampleSize * divisor);
205 const uint32_t scaledBorder = SkTMin(5u, maxBorder);
206 const uint32_t unscaledBorder = scaledBorder * fSampleSize;
207
208 // We may need to clear the canvas to avoid uninitialized memory.
209 // Assume we are scaling a 780x780 image with sampleSize = 8.
210 // The output image should be 97x97.
211 // Each subset will be 390x390.
212 // Each scaled subset be 48x48.
213 // Four scaled subsets will only fill a 96x96 image.
214 // The bottom row and last column will not be touched.
215 // This is an unfortunate result of our rounding rules when scaling.
216 // Maybe we need to consider testing scaled subsets without trying to
217 // combine them to match the full scaled image? Or maybe this is the
218 // best we can do?
219 canvas->clear(0);
220
221 for (uint32_t x = 0; x < divisor; x++) {
222 for (uint32_t y = 0; y < divisor; y++) {
223 // Calculate the subset dimensions
224 uint32_t subsetWidth = width / divisor;
225 uint32_t subsetHeight = height / divisor;
226 const int left = x * subsetWidth;
227 const int top = y * subsetHeight;
228
229 // Increase the size of the last subset in each row or column, when the
230 // divisor does not divide evenly into the image dimensions
231 subsetWidth += (x + 1 == divisor) ? (width % divisor) : 0;
232 subsetHeight += (y + 1 == divisor) ? (height % divisor) : 0;
233
234 // Increase the size of the subset in order to have a border on each side
235 const int decodeLeft = left - unscaledBorder;
236 const int decodeTop = top - unscaledBorder;
237 const uint32_t decodeWidth = subsetWidth + unscaledBorder * 2;
238 const uint32_t decodeHeight = subsetHeight + unscaledBorder * 2;
239 SkBitmap bitmap;
240 if (!brd->decodeRegion(&bitmap, nullptr, SkIRect::MakeXYWH(decodeLeft,
241 decodeTop, decodeWidth, decodeHeight), fSampleSize, colorType, false,
242 colorSpace)) {
243 return "Cannot decode region.";
244 }
245
246 alpha8_to_gray8(&bitmap);
247 canvas->drawBitmapRect(bitmap,
248 SkRect::MakeXYWH((SkScalar) scaledBorder, (SkScalar) scaledBorder,
249 (SkScalar) (subsetWidth / fSampleSize),
250 (SkScalar) (subsetHeight / fSampleSize)),
251 SkRect::MakeXYWH((SkScalar) (left / fSampleSize),
252 (SkScalar) (top / fSampleSize),
253 (SkScalar) (subsetWidth / fSampleSize),
254 (SkScalar) (subsetHeight / fSampleSize)),
255 nullptr);
256 }
257 }
258 return "";
259 }
260 default:
261 SkASSERT(false);
262 return "Error: Should not be reached.";
263 }
264 }
265
size() const266 SkISize BRDSrc::size() const {
267 std::unique_ptr<SkBitmapRegionDecoder> brd(create_brd(fPath));
268 if (brd) {
269 return {SkTMax(1, brd->width() / (int)fSampleSize),
270 SkTMax(1, brd->height() / (int)fSampleSize)};
271 }
272 return {0, 0};
273 }
274
get_scaled_name(const Path & path,float scale)275 static SkString get_scaled_name(const Path& path, float scale) {
276 return SkStringPrintf("%s_%.3f", SkOSPath::Basename(path.c_str()).c_str(), scale);
277 }
278
name() const279 Name BRDSrc::name() const {
280 // We will replicate the names used by CodecSrc so that images can
281 // be compared in Gold.
282 if (1 == fSampleSize) {
283 return SkOSPath::Basename(fPath.c_str());
284 }
285 return get_scaled_name(fPath, 1.0f / (float) fSampleSize);
286 }
287
288 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
289
serial_from_path_name(const SkString & path)290 static bool serial_from_path_name(const SkString& path) {
291 if (!FLAGS_RAW_threading) {
292 static const char* const exts[] = {
293 "arw", "cr2", "dng", "nef", "nrw", "orf", "raf", "rw2", "pef", "srw",
294 "ARW", "CR2", "DNG", "NEF", "NRW", "ORF", "RAF", "RW2", "PEF", "SRW",
295 };
296 const char* actualExt = strrchr(path.c_str(), '.');
297 if (actualExt) {
298 actualExt++;
299 for (auto* ext : exts) {
300 if (0 == strcmp(ext, actualExt)) {
301 return true;
302 }
303 }
304 }
305 }
306 return false;
307 }
308
CodecSrc(Path path,Mode mode,DstColorType dstColorType,SkAlphaType dstAlphaType,float scale)309 CodecSrc::CodecSrc(Path path, Mode mode, DstColorType dstColorType, SkAlphaType dstAlphaType,
310 float scale)
311 : fPath(path)
312 , fMode(mode)
313 , fDstColorType(dstColorType)
314 , fDstAlphaType(dstAlphaType)
315 , fScale(scale)
316 , fRunSerially(serial_from_path_name(path))
317 {}
318
veto(SinkFlags flags) const319 bool CodecSrc::veto(SinkFlags flags) const {
320 // Test to direct raster backends (8888 and 565).
321 return flags.type != SinkFlags::kRaster || flags.approach != SinkFlags::kDirect;
322 }
323
324 // Allows us to test decodes to non-native 8888.
swap_rb_if_necessary(SkBitmap & bitmap,CodecSrc::DstColorType dstColorType)325 static void swap_rb_if_necessary(SkBitmap& bitmap, CodecSrc::DstColorType dstColorType) {
326 if (CodecSrc::kNonNative8888_Always_DstColorType != dstColorType) {
327 return;
328 }
329
330 for (int y = 0; y < bitmap.height(); y++) {
331 uint32_t* row = (uint32_t*) bitmap.getAddr(0, y);
332 SkOpts::RGBA_to_BGRA(row, row, bitmap.width());
333 }
334 }
335
get_decode_info(SkImageInfo * decodeInfo,SkColorType canvasColorType,CodecSrc::DstColorType dstColorType,SkAlphaType dstAlphaType)336 static bool get_decode_info(SkImageInfo* decodeInfo, SkColorType canvasColorType,
337 CodecSrc::DstColorType dstColorType, SkAlphaType dstAlphaType) {
338 switch (dstColorType) {
339 case CodecSrc::kGrayscale_Always_DstColorType:
340 if (kRGB_565_SkColorType == canvasColorType) {
341 return false;
342 }
343 *decodeInfo = decodeInfo->makeColorType(kGray_8_SkColorType);
344 break;
345 case CodecSrc::kNonNative8888_Always_DstColorType:
346 if (kRGB_565_SkColorType == canvasColorType
347 || kRGBA_F16_SkColorType == canvasColorType) {
348 return false;
349 }
350 #ifdef SK_PMCOLOR_IS_RGBA
351 *decodeInfo = decodeInfo->makeColorType(kBGRA_8888_SkColorType);
352 #else
353 *decodeInfo = decodeInfo->makeColorType(kRGBA_8888_SkColorType);
354 #endif
355 break;
356 default:
357 if (kRGB_565_SkColorType == canvasColorType &&
358 kOpaque_SkAlphaType != decodeInfo->alphaType()) {
359 return false;
360 }
361
362 *decodeInfo = decodeInfo->makeColorType(canvasColorType);
363 break;
364 }
365
366 *decodeInfo = decodeInfo->makeAlphaType(dstAlphaType);
367 return true;
368 }
369
draw_to_canvas(SkCanvas * canvas,const SkImageInfo & info,void * pixels,size_t rowBytes,CodecSrc::DstColorType dstColorType,SkScalar left=0,SkScalar top=0)370 static void draw_to_canvas(SkCanvas* canvas, const SkImageInfo& info, void* pixels, size_t rowBytes,
371 CodecSrc::DstColorType dstColorType,
372 SkScalar left = 0, SkScalar top = 0) {
373 SkBitmap bitmap;
374 bitmap.installPixels(info, pixels, rowBytes);
375 swap_rb_if_necessary(bitmap, dstColorType);
376 canvas->drawBitmap(bitmap, left, top);
377 }
378
379 // For codec srcs, we want the "draw" step to be a memcpy. Any interesting color space or
380 // color format conversions should be performed by the codec. Sometimes the output of the
381 // decode will be in an interesting color space. On our srgb and f16 backends, we need to
382 // "pretend" that the color space is standard sRGB to avoid triggering color conversion
383 // at draw time.
set_bitmap_color_space(SkImageInfo * info)384 static void set_bitmap_color_space(SkImageInfo* info) {
385 *info = info->makeColorSpace(SkColorSpace::MakeSRGB());
386 }
387
draw(SkCanvas * canvas) const388 Error CodecSrc::draw(SkCanvas* canvas) const {
389 sk_sp<SkData> encoded(SkData::MakeFromFileName(fPath.c_str()));
390 if (!encoded) {
391 return SkStringPrintf("Couldn't read %s.", fPath.c_str());
392 }
393
394 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(encoded));
395 if (nullptr == codec.get()) {
396 return SkStringPrintf("Couldn't create codec for %s.", fPath.c_str());
397 }
398
399 SkImageInfo decodeInfo = codec->getInfo();
400 if (!get_decode_info(&decodeInfo, canvas->imageInfo().colorType(), fDstColorType,
401 fDstAlphaType)) {
402 return Error::Nonfatal("Skipping uninteresting test.");
403 }
404
405 // Try to scale the image if it is desired
406 SkISize size = codec->getScaledDimensions(fScale);
407 if (size == decodeInfo.dimensions() && 1.0f != fScale) {
408 return Error::Nonfatal("Test without scaling is uninteresting.");
409 }
410
411 // Visually inspecting very small output images is not necessary. We will
412 // cover these cases in unit testing.
413 if ((size.width() <= 10 || size.height() <= 10) && 1.0f != fScale) {
414 return Error::Nonfatal("Scaling very small images is uninteresting.");
415 }
416 decodeInfo = decodeInfo.makeWH(size.width(), size.height());
417
418 const int bpp = decodeInfo.bytesPerPixel();
419 const size_t rowBytes = size.width() * bpp;
420 const size_t safeSize = decodeInfo.computeByteSize(rowBytes);
421 SkAutoMalloc pixels(safeSize);
422
423 SkCodec::Options options;
424 if (kCodecZeroInit_Mode == fMode) {
425 memset(pixels.get(), 0, size.height() * rowBytes);
426 options.fZeroInitialized = SkCodec::kYes_ZeroInitialized;
427 }
428
429 SkImageInfo bitmapInfo = decodeInfo;
430 set_bitmap_color_space(&bitmapInfo);
431 if (kRGBA_8888_SkColorType == decodeInfo.colorType() ||
432 kBGRA_8888_SkColorType == decodeInfo.colorType()) {
433 bitmapInfo = bitmapInfo.makeColorType(kN32_SkColorType);
434 }
435
436 switch (fMode) {
437 case kAnimated_Mode: {
438 std::vector<SkCodec::FrameInfo> frameInfos = codec->getFrameInfo();
439 if (frameInfos.size() <= 1) {
440 return SkStringPrintf("%s is not an animated image.", fPath.c_str());
441 }
442
443 // As in CodecSrc::size(), compute a roughly square grid to draw the frames
444 // into. "factor" is the number of frames to draw on one row. There will be
445 // up to "factor" rows as well.
446 const float root = sqrt((float) frameInfos.size());
447 const int factor = sk_float_ceil2int(root);
448
449 // Used to cache a frame that future frames will depend on.
450 SkAutoMalloc priorFramePixels;
451 int cachedFrame = SkCodec::kNoFrame;
452 for (int i = 0; static_cast<size_t>(i) < frameInfos.size(); i++) {
453 options.fFrameIndex = i;
454 // Check for a prior frame
455 const int reqFrame = frameInfos[i].fRequiredFrame;
456 if (reqFrame != SkCodec::kNoFrame && reqFrame == cachedFrame
457 && priorFramePixels.get()) {
458 // Copy into pixels
459 memcpy(pixels.get(), priorFramePixels.get(), safeSize);
460 options.fPriorFrame = reqFrame;
461 } else {
462 options.fPriorFrame = SkCodec::kNoFrame;
463 }
464 SkCodec::Result result = codec->getPixels(decodeInfo, pixels.get(),
465 rowBytes, &options);
466 if (SkCodec::kInvalidInput == result && i > 0) {
467 // Some of our test images have truncated later frames. Treat that
468 // the same as incomplete.
469 result = SkCodec::kIncompleteInput;
470 }
471 switch (result) {
472 case SkCodec::kSuccess:
473 case SkCodec::kErrorInInput:
474 case SkCodec::kIncompleteInput: {
475 // If the next frame depends on this one, store it in priorFrame.
476 // It is possible that we may discard a frame that future frames depend on,
477 // but the codec will simply redecode the discarded frame.
478 // Do this before calling draw_to_canvas, which premultiplies in place. If
479 // we're decoding to unpremul, we want to pass the unmodified frame to the
480 // codec for decoding the next frame.
481 if (static_cast<size_t>(i+1) < frameInfos.size()
482 && frameInfos[i+1].fRequiredFrame == i) {
483 memcpy(priorFramePixels.reset(safeSize), pixels.get(), safeSize);
484 cachedFrame = i;
485 }
486
487 SkAutoCanvasRestore acr(canvas, true);
488 const int xTranslate = (i % factor) * decodeInfo.width();
489 const int yTranslate = (i / factor) * decodeInfo.height();
490 canvas->translate(SkIntToScalar(xTranslate), SkIntToScalar(yTranslate));
491 draw_to_canvas(canvas, bitmapInfo, pixels.get(), rowBytes, fDstColorType);
492 if (result != SkCodec::kSuccess) {
493 return "";
494 }
495 break;
496 }
497 case SkCodec::kInvalidConversion:
498 if (i > 0 && (decodeInfo.colorType() == kRGB_565_SkColorType)) {
499 return Error::Nonfatal(SkStringPrintf(
500 "Cannot decode frame %i to 565 (%s).", i, fPath.c_str()));
501 }
502 // Fall through.
503 default:
504 return SkStringPrintf("Couldn't getPixels for frame %i in %s.",
505 i, fPath.c_str());
506 }
507 }
508 break;
509 }
510 case kCodecZeroInit_Mode:
511 case kCodec_Mode: {
512 switch (codec->getPixels(decodeInfo, pixels.get(), rowBytes, &options)) {
513 case SkCodec::kSuccess:
514 // We consider these to be valid, since we should still decode what is
515 // available.
516 case SkCodec::kErrorInInput:
517 case SkCodec::kIncompleteInput:
518 break;
519 default:
520 // Everything else is considered a failure.
521 return SkStringPrintf("Couldn't getPixels %s.", fPath.c_str());
522 }
523
524 draw_to_canvas(canvas, bitmapInfo, pixels.get(), rowBytes, fDstColorType);
525 break;
526 }
527 case kScanline_Mode: {
528 void* dst = pixels.get();
529 uint32_t height = decodeInfo.height();
530 const bool useIncremental = [this]() {
531 auto exts = { "png", "PNG", "gif", "GIF" };
532 for (auto ext : exts) {
533 if (fPath.endsWith(ext)) {
534 return true;
535 }
536 }
537 return false;
538 }();
539 // ico may use the old scanline method or the new one, depending on whether it
540 // internally holds a bmp or a png.
541 const bool ico = fPath.endsWith("ico");
542 bool useOldScanlineMethod = !useIncremental && !ico;
543 if (useIncremental || ico) {
544 if (SkCodec::kSuccess == codec->startIncrementalDecode(decodeInfo, dst,
545 rowBytes, &options)) {
546 int rowsDecoded;
547 auto result = codec->incrementalDecode(&rowsDecoded);
548 if (SkCodec::kIncompleteInput == result || SkCodec::kErrorInInput == result) {
549 codec->fillIncompleteImage(decodeInfo, dst, rowBytes,
550 SkCodec::kNo_ZeroInitialized, height,
551 rowsDecoded);
552 }
553 } else {
554 if (useIncremental) {
555 // Error: These should support incremental decode.
556 return "Could not start incremental decode";
557 }
558 // Otherwise, this is an ICO. Since incremental failed, it must contain a BMP,
559 // which should work via startScanlineDecode
560 useOldScanlineMethod = true;
561 }
562 }
563
564 if (useOldScanlineMethod) {
565 if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo)) {
566 return "Could not start scanline decoder";
567 }
568
569 // We do not need to check the return value. On an incomplete
570 // image, memory will be filled with a default value.
571 codec->getScanlines(dst, height, rowBytes);
572 }
573
574 draw_to_canvas(canvas, bitmapInfo, dst, rowBytes, fDstColorType);
575 break;
576 }
577 case kStripe_Mode: {
578 const int height = decodeInfo.height();
579 // This value is chosen arbitrarily. We exercise more cases by choosing a value that
580 // does not align with image blocks.
581 const int stripeHeight = 37;
582 const int numStripes = (height + stripeHeight - 1) / stripeHeight;
583 void* dst = pixels.get();
584
585 // Decode odd stripes
586 if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo, &options)) {
587 return "Could not start scanline decoder";
588 }
589
590 // This mode was designed to test the new skip scanlines API in libjpeg-turbo.
591 // Jpegs have kTopDown_SkScanlineOrder, and at this time, it is not interesting
592 // to run this test for image types that do not have this scanline ordering.
593 // We only run this on Jpeg, which is always kTopDown.
594 SkASSERT(SkCodec::kTopDown_SkScanlineOrder == codec->getScanlineOrder());
595
596 for (int i = 0; i < numStripes; i += 2) {
597 // Skip a stripe
598 const int linesToSkip = SkTMin(stripeHeight, height - i * stripeHeight);
599 codec->skipScanlines(linesToSkip);
600
601 // Read a stripe
602 const int startY = (i + 1) * stripeHeight;
603 const int linesToRead = SkTMin(stripeHeight, height - startY);
604 if (linesToRead > 0) {
605 codec->getScanlines(SkTAddOffset<void>(dst, rowBytes * startY), linesToRead,
606 rowBytes);
607 }
608 }
609
610 // Decode even stripes
611 const SkCodec::Result startResult = codec->startScanlineDecode(decodeInfo);
612 if (SkCodec::kSuccess != startResult) {
613 return "Failed to restart scanline decoder with same parameters.";
614 }
615 for (int i = 0; i < numStripes; i += 2) {
616 // Read a stripe
617 const int startY = i * stripeHeight;
618 const int linesToRead = SkTMin(stripeHeight, height - startY);
619 codec->getScanlines(SkTAddOffset<void>(dst, rowBytes * startY), linesToRead,
620 rowBytes);
621
622 // Skip a stripe
623 const int linesToSkip = SkTMin(stripeHeight, height - (i + 1) * stripeHeight);
624 if (linesToSkip > 0) {
625 codec->skipScanlines(linesToSkip);
626 }
627 }
628
629 draw_to_canvas(canvas, bitmapInfo, dst, rowBytes, fDstColorType);
630 break;
631 }
632 case kCroppedScanline_Mode: {
633 const int width = decodeInfo.width();
634 const int height = decodeInfo.height();
635 // This value is chosen because, as we move across the image, it will sometimes
636 // align with the jpeg block sizes and it will sometimes not. This allows us
637 // to test interestingly different code paths in the implementation.
638 const int tileSize = 36;
639 SkIRect subset;
640 for (int x = 0; x < width; x += tileSize) {
641 subset = SkIRect::MakeXYWH(x, 0, SkTMin(tileSize, width - x), height);
642 options.fSubset = ⊂
643 if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo, &options)) {
644 return "Could not start scanline decoder.";
645 }
646
647 codec->getScanlines(SkTAddOffset<void>(pixels.get(), x * bpp), height, rowBytes);
648 }
649
650 draw_to_canvas(canvas, bitmapInfo, pixels.get(), rowBytes, fDstColorType);
651 break;
652 }
653 case kSubset_Mode: {
654 // Arbitrarily choose a divisor.
655 int divisor = 2;
656 // Total width/height of the image.
657 const int W = codec->getInfo().width();
658 const int H = codec->getInfo().height();
659 if (divisor > W || divisor > H) {
660 return Error::Nonfatal(SkStringPrintf("Cannot codec subset: divisor %d is too big "
661 "for %s with dimensions (%d x %d)", divisor,
662 fPath.c_str(), W, H));
663 }
664 // subset dimensions
665 // SkWebpCodec, the only one that supports subsets, requires even top/left boundaries.
666 const int w = SkAlign2(W / divisor);
667 const int h = SkAlign2(H / divisor);
668 SkIRect subset;
669 options.fSubset = ⊂
670 SkBitmap subsetBm;
671 // We will reuse pixel memory from bitmap.
672 void* dst = pixels.get();
673 // Keep track of left and top (for drawing subsetBm into canvas). We could use
674 // fScale * x and fScale * y, but we want integers such that the next subset will start
675 // where the last one ended. So we'll add decodeInfo.width() and height().
676 int left = 0;
677 for (int x = 0; x < W; x += w) {
678 int top = 0;
679 for (int y = 0; y < H; y+= h) {
680 // Do not make the subset go off the edge of the image.
681 const int preScaleW = SkTMin(w, W - x);
682 const int preScaleH = SkTMin(h, H - y);
683 subset.setXYWH(x, y, preScaleW, preScaleH);
684 // And scale
685 // FIXME: Should we have a version of getScaledDimensions that takes a subset
686 // into account?
687 const int scaledW = SkTMax(1, SkScalarRoundToInt(preScaleW * fScale));
688 const int scaledH = SkTMax(1, SkScalarRoundToInt(preScaleH * fScale));
689 decodeInfo = decodeInfo.makeWH(scaledW, scaledH);
690 SkImageInfo subsetBitmapInfo = bitmapInfo.makeWH(scaledW, scaledH);
691 size_t subsetRowBytes = subsetBitmapInfo.minRowBytes();
692 const SkCodec::Result result = codec->getPixels(decodeInfo, dst, subsetRowBytes,
693 &options);
694 switch (result) {
695 case SkCodec::kSuccess:
696 case SkCodec::kErrorInInput:
697 case SkCodec::kIncompleteInput:
698 break;
699 default:
700 return SkStringPrintf("subset codec failed to decode (%d, %d, %d, %d) "
701 "from %s with dimensions (%d x %d)\t error %d",
702 x, y, decodeInfo.width(), decodeInfo.height(),
703 fPath.c_str(), W, H, result);
704 }
705 draw_to_canvas(canvas, subsetBitmapInfo, dst, subsetRowBytes, fDstColorType,
706 SkIntToScalar(left), SkIntToScalar(top));
707
708 // translate by the scaled height.
709 top += decodeInfo.height();
710 }
711 // translate by the scaled width.
712 left += decodeInfo.width();
713 }
714 return "";
715 }
716 default:
717 SkASSERT(false);
718 return "Invalid fMode";
719 }
720 return "";
721 }
722
size() const723 SkISize CodecSrc::size() const {
724 sk_sp<SkData> encoded(SkData::MakeFromFileName(fPath.c_str()));
725 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(encoded));
726 if (nullptr == codec) {
727 return {0, 0};
728 }
729
730 auto imageSize = codec->getScaledDimensions(fScale);
731 if (fMode == kAnimated_Mode) {
732 // We'll draw one of each frame, so make it big enough to hold them all
733 // in a grid. The grid will be roughly square, with "factor" frames per
734 // row and up to "factor" rows.
735 const size_t count = codec->getFrameInfo().size();
736 const float root = sqrt((float) count);
737 const int factor = sk_float_ceil2int(root);
738 imageSize.fWidth = imageSize.fWidth * factor;
739 imageSize.fHeight = imageSize.fHeight * sk_float_ceil2int((float) count / (float) factor);
740 }
741 return imageSize;
742 }
743
name() const744 Name CodecSrc::name() const {
745 if (1.0f == fScale) {
746 Name name = SkOSPath::Basename(fPath.c_str());
747 if (fMode == kAnimated_Mode) {
748 name.append("_animated");
749 }
750 return name;
751 }
752 SkASSERT(fMode != kAnimated_Mode);
753 return get_scaled_name(fPath, fScale);
754 }
755
756 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
757
AndroidCodecSrc(Path path,CodecSrc::DstColorType dstColorType,SkAlphaType dstAlphaType,int sampleSize)758 AndroidCodecSrc::AndroidCodecSrc(Path path, CodecSrc::DstColorType dstColorType,
759 SkAlphaType dstAlphaType, int sampleSize)
760 : fPath(path)
761 , fDstColorType(dstColorType)
762 , fDstAlphaType(dstAlphaType)
763 , fSampleSize(sampleSize)
764 , fRunSerially(serial_from_path_name(path))
765 {}
766
veto(SinkFlags flags) const767 bool AndroidCodecSrc::veto(SinkFlags flags) const {
768 // No need to test decoding to non-raster or indirect backend.
769 return flags.type != SinkFlags::kRaster
770 || flags.approach != SinkFlags::kDirect;
771 }
772
draw(SkCanvas * canvas) const773 Error AndroidCodecSrc::draw(SkCanvas* canvas) const {
774 sk_sp<SkData> encoded(SkData::MakeFromFileName(fPath.c_str()));
775 if (!encoded) {
776 return SkStringPrintf("Couldn't read %s.", fPath.c_str());
777 }
778 std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::MakeFromData(encoded));
779 if (nullptr == codec) {
780 return SkStringPrintf("Couldn't create android codec for %s.", fPath.c_str());
781 }
782
783 SkImageInfo decodeInfo = codec->getInfo();
784 if (!get_decode_info(&decodeInfo, canvas->imageInfo().colorType(), fDstColorType,
785 fDstAlphaType)) {
786 return Error::Nonfatal("Skipping uninteresting test.");
787 }
788
789 // Scale the image if it is desired.
790 SkISize size = codec->getSampledDimensions(fSampleSize);
791
792 // Visually inspecting very small output images is not necessary. We will
793 // cover these cases in unit testing.
794 if ((size.width() <= 10 || size.height() <= 10) && 1 != fSampleSize) {
795 return Error::Nonfatal("Scaling very small images is uninteresting.");
796 }
797 decodeInfo = decodeInfo.makeWH(size.width(), size.height());
798
799 int bpp = decodeInfo.bytesPerPixel();
800 size_t rowBytes = size.width() * bpp;
801 SkAutoMalloc pixels(size.height() * rowBytes);
802
803 SkBitmap bitmap;
804 SkImageInfo bitmapInfo = decodeInfo;
805 set_bitmap_color_space(&bitmapInfo);
806 if (kRGBA_8888_SkColorType == decodeInfo.colorType() ||
807 kBGRA_8888_SkColorType == decodeInfo.colorType()) {
808 bitmapInfo = bitmapInfo.makeColorType(kN32_SkColorType);
809 }
810
811 // Create options for the codec.
812 SkAndroidCodec::AndroidOptions options;
813 options.fSampleSize = fSampleSize;
814
815 switch (codec->getAndroidPixels(decodeInfo, pixels.get(), rowBytes, &options)) {
816 case SkCodec::kSuccess:
817 case SkCodec::kErrorInInput:
818 case SkCodec::kIncompleteInput:
819 break;
820 default:
821 return SkStringPrintf("Couldn't getPixels %s.", fPath.c_str());
822 }
823 draw_to_canvas(canvas, bitmapInfo, pixels.get(), rowBytes, fDstColorType);
824 return "";
825 }
826
size() const827 SkISize AndroidCodecSrc::size() const {
828 sk_sp<SkData> encoded(SkData::MakeFromFileName(fPath.c_str()));
829 std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::MakeFromData(encoded));
830 if (nullptr == codec) {
831 return {0, 0};
832 }
833 return codec->getSampledDimensions(fSampleSize);
834 }
835
name() const836 Name AndroidCodecSrc::name() const {
837 // We will replicate the names used by CodecSrc so that images can
838 // be compared in Gold.
839 if (1 == fSampleSize) {
840 return SkOSPath::Basename(fPath.c_str());
841 }
842 return get_scaled_name(fPath, 1.0f / (float) fSampleSize);
843 }
844
845 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
846
ImageGenSrc(Path path,Mode mode,SkAlphaType alphaType,bool isGpu)847 ImageGenSrc::ImageGenSrc(Path path, Mode mode, SkAlphaType alphaType, bool isGpu)
848 : fPath(path)
849 , fMode(mode)
850 , fDstAlphaType(alphaType)
851 , fIsGpu(isGpu)
852 , fRunSerially(serial_from_path_name(path))
853 {}
854
veto(SinkFlags flags) const855 bool ImageGenSrc::veto(SinkFlags flags) const {
856 if (fIsGpu) {
857 // MSAA runs tend to run out of memory and tests the same code paths as regular gpu configs.
858 return flags.type != SinkFlags::kGPU || flags.approach != SinkFlags::kDirect ||
859 flags.multisampled == SinkFlags::kMultisampled;
860 }
861
862 return flags.type != SinkFlags::kRaster || flags.approach != SinkFlags::kDirect;
863 }
864
draw(SkCanvas * canvas) const865 Error ImageGenSrc::draw(SkCanvas* canvas) const {
866 if (kRGB_565_SkColorType == canvas->imageInfo().colorType()) {
867 return Error::Nonfatal("Uninteresting to test image generator to 565.");
868 }
869
870 sk_sp<SkData> encoded(SkData::MakeFromFileName(fPath.c_str()));
871 if (!encoded) {
872 return SkStringPrintf("Couldn't read %s.", fPath.c_str());
873 }
874
875 #if defined(SK_BUILD_FOR_WIN)
876 // Initialize COM in order to test with WIC.
877 SkAutoCoInitialize com;
878 if (!com.succeeded()) {
879 return "Could not initialize COM.";
880 }
881 #endif
882
883 std::unique_ptr<SkImageGenerator> gen(nullptr);
884 switch (fMode) {
885 case kCodec_Mode:
886 gen = SkCodecImageGenerator::MakeFromEncodedCodec(encoded);
887 if (!gen) {
888 return "Could not create codec image generator.";
889 }
890 break;
891 case kPlatform_Mode: {
892 #if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
893 gen = SkImageGeneratorCG::MakeFromEncodedCG(encoded);
894 #elif defined(SK_BUILD_FOR_WIN)
895 gen = SkImageGeneratorWIC::MakeFromEncodedWIC(encoded);
896 #endif
897 if (!gen) {
898 return "Could not create platform image generator.";
899 }
900 break;
901 }
902 default:
903 SkASSERT(false);
904 return "Invalid image generator mode";
905 }
906
907 // Test deferred decoding path on GPU
908 if (fIsGpu) {
909 sk_sp<SkImage> image(SkImage::MakeFromGenerator(std::move(gen), nullptr));
910 if (!image) {
911 return "Could not create image from codec image generator.";
912 }
913 canvas->drawImage(image, 0, 0);
914 return "";
915 }
916
917 // Test various color and alpha types on CPU
918 SkImageInfo decodeInfo = gen->getInfo().makeAlphaType(fDstAlphaType);
919
920 int bpp = decodeInfo.bytesPerPixel();
921 size_t rowBytes = decodeInfo.width() * bpp;
922 SkAutoMalloc pixels(decodeInfo.height() * rowBytes);
923 if (!gen->getPixels(decodeInfo, pixels.get(), rowBytes)) {
924 SkString err =
925 SkStringPrintf("Image generator could not getPixels() for %s\n", fPath.c_str());
926
927 #if defined(SK_BUILD_FOR_WIN)
928 if (kPlatform_Mode == fMode) {
929 // Do not issue a fatal error for WIC flakiness.
930 return Error::Nonfatal(err);
931 }
932 #endif
933
934 return err;
935 }
936
937 set_bitmap_color_space(&decodeInfo);
938 draw_to_canvas(canvas, decodeInfo, pixels.get(), rowBytes,
939 CodecSrc::kGetFromCanvas_DstColorType);
940 return "";
941 }
942
size() const943 SkISize ImageGenSrc::size() const {
944 sk_sp<SkData> encoded(SkData::MakeFromFileName(fPath.c_str()));
945 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(encoded));
946 if (nullptr == codec) {
947 return {0, 0};
948 }
949 return codec->getInfo().dimensions();
950 }
951
name() const952 Name ImageGenSrc::name() const {
953 return SkOSPath::Basename(fPath.c_str());
954 }
955
956 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
957
ColorCodecSrc(Path path,bool decode_to_dst)958 ColorCodecSrc::ColorCodecSrc(Path path, bool decode_to_dst) : fPath(path)
959 , fDecodeToDst(decode_to_dst) {}
960
veto(SinkFlags flags) const961 bool ColorCodecSrc::veto(SinkFlags flags) const {
962 // Test to direct raster backends (8888 and 565).
963 return flags.type != SinkFlags::kRaster || flags.approach != SinkFlags::kDirect;
964 }
965
draw(SkCanvas * canvas) const966 Error ColorCodecSrc::draw(SkCanvas* canvas) const {
967 sk_sp<SkData> encoded(SkData::MakeFromFileName(fPath.c_str()));
968 if (!encoded) {
969 return SkStringPrintf("Couldn't read %s.", fPath.c_str());
970 }
971
972 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(encoded));
973 if (nullptr == codec) {
974 return SkStringPrintf("Couldn't create codec for %s.", fPath.c_str());
975 }
976
977 SkImageInfo info = codec->getInfo();
978 if (fDecodeToDst) {
979 info = canvas->imageInfo().makeWH(info.width(),
980 info.height());
981 }
982
983 SkBitmap bitmap;
984 if (!bitmap.tryAllocPixels(info)) {
985 return SkStringPrintf("Image(%s) is too large (%d x %d)",
986 fPath.c_str(), info.width(), info.height());
987 }
988
989 switch (auto r = codec->getPixels(info, bitmap.getPixels(), bitmap.rowBytes())) {
990 case SkCodec::kSuccess:
991 case SkCodec::kErrorInInput:
992 case SkCodec::kIncompleteInput:
993 canvas->drawBitmap(bitmap, 0,0);
994 return "";
995 case SkCodec::kInvalidConversion:
996 // TODO(mtklein): why are there formats we can't decode to?
997 return Error::Nonfatal("SkCodec can't decode to this format.");
998 default:
999 return SkStringPrintf("Couldn't getPixels %s. Error code %d", fPath.c_str(), r);
1000 }
1001 }
1002
size() const1003 SkISize ColorCodecSrc::size() const {
1004 sk_sp<SkData> encoded(SkData::MakeFromFileName(fPath.c_str()));
1005 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(encoded));
1006 if (nullptr == codec) {
1007 return {0, 0};
1008 }
1009 return {codec->getInfo().width(), codec->getInfo().height()};
1010 }
1011
name() const1012 Name ColorCodecSrc::name() const {
1013 return SkOSPath::Basename(fPath.c_str());
1014 }
1015
1016 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1017
1018 static DEFINE_int(skpViewportSize, 1000,
1019 "Width & height of the viewport used to crop skp rendering.");
1020
SKPSrc(Path path)1021 SKPSrc::SKPSrc(Path path) : fPath(path) { }
1022
draw(SkCanvas * canvas) const1023 Error SKPSrc::draw(SkCanvas* canvas) const {
1024 std::unique_ptr<SkStream> stream = SkStream::MakeFromFile(fPath.c_str());
1025 if (!stream) {
1026 return SkStringPrintf("Couldn't read %s.", fPath.c_str());
1027 }
1028 sk_sp<SkPicture> pic(SkPicture::MakeFromStream(stream.get()));
1029 if (!pic) {
1030 return SkStringPrintf("Couldn't parse file %s.", fPath.c_str());
1031 }
1032 stream = nullptr; // Might as well drop this when we're done with it.
1033 canvas->clipRect(SkRect::MakeWH(FLAGS_skpViewportSize, FLAGS_skpViewportSize));
1034 canvas->drawPicture(pic);
1035 return "";
1036 }
1037
get_cull_rect_for_skp(const char * path)1038 static SkRect get_cull_rect_for_skp(const char* path) {
1039 std::unique_ptr<SkStream> stream = SkStream::MakeFromFile(path);
1040 if (!stream) {
1041 return SkRect::MakeEmpty();
1042 }
1043 SkPictInfo info;
1044 if (!SkPicture_StreamIsSKP(stream.get(), &info)) {
1045 return SkRect::MakeEmpty();
1046 }
1047
1048 return info.fCullRect;
1049 }
1050
size() const1051 SkISize SKPSrc::size() const {
1052 SkRect viewport = get_cull_rect_for_skp(fPath.c_str());
1053 if (!viewport.intersect((SkRect::MakeWH(FLAGS_skpViewportSize, FLAGS_skpViewportSize)))) {
1054 return {0, 0};
1055 }
1056 return viewport.roundOut().size();
1057 }
1058
name() const1059 Name SKPSrc::name() const { return SkOSPath::Basename(fPath.c_str()); }
1060
1061 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1062
BisectSrc(Path path,const char * trail)1063 BisectSrc::BisectSrc(Path path, const char* trail) : INHERITED(path), fTrail(trail) {}
1064
draw(SkCanvas * canvas) const1065 Error BisectSrc::draw(SkCanvas* canvas) const {
1066 struct FoundPath {
1067 SkPath fPath;
1068 SkPaint fPaint;
1069 SkMatrix fViewMatrix;
1070 };
1071
1072 // This subclass of SkCanvas just extracts all the SkPaths (drawn via drawPath) from an SKP.
1073 class PathFindingCanvas : public SkCanvas {
1074 public:
1075 PathFindingCanvas(int width, int height) : SkCanvas(width, height, nullptr) {}
1076 const SkTArray<FoundPath>& foundPaths() const { return fFoundPaths; }
1077
1078 private:
1079 void onDrawPath(const SkPath& path, const SkPaint& paint) override {
1080 fFoundPaths.push_back() = {path, paint, this->getTotalMatrix()};
1081 }
1082
1083 SkTArray<FoundPath> fFoundPaths;
1084 };
1085
1086 PathFindingCanvas pathFinder(canvas->getBaseLayerSize().width(),
1087 canvas->getBaseLayerSize().height());
1088 Error err = this->INHERITED::draw(&pathFinder);
1089 if (!err.isEmpty()) {
1090 return err;
1091 }
1092
1093 int start = 0, end = pathFinder.foundPaths().count();
1094 for (const char* ch = fTrail.c_str(); *ch; ++ch) {
1095 int midpt = (start + end) / 2;
1096 if ('l' == *ch) {
1097 start = midpt;
1098 } else if ('r' == *ch) {
1099 end = midpt;
1100 }
1101 }
1102
1103 for (int i = start; i < end; ++i) {
1104 const FoundPath& path = pathFinder.foundPaths()[i];
1105 SkAutoCanvasRestore acr(canvas, true);
1106 canvas->concat(path.fViewMatrix);
1107 canvas->drawPath(path.fPath, path.fPaint);
1108 }
1109
1110 return "";
1111 }
1112
1113 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1114
1115 #if defined(SK_ENABLE_SKOTTIE)
SkottieSrc(Path path)1116 SkottieSrc::SkottieSrc(Path path) : fPath(std::move(path)) {}
1117
draw(SkCanvas * canvas) const1118 Error SkottieSrc::draw(SkCanvas* canvas) const {
1119 auto animation = skottie::Animation::Builder()
1120 .setResourceProvider(
1121 skottie_utils::FileResourceProvider::Make(SkOSPath::Dirname(fPath.c_str())))
1122 .makeFromFile(fPath.c_str());
1123 if (!animation) {
1124 return SkStringPrintf("Unable to parse file: %s", fPath.c_str());
1125 }
1126
1127 canvas->drawColor(SK_ColorWHITE);
1128
1129 const auto t_rate = 1.0f / (kTileCount * kTileCount - 1);
1130
1131 // Draw the frames in a shuffled order to exercise non-linear
1132 // frame progression. The film strip will still be in order left-to-right,
1133 // top-down, just not drawn in that order.
1134 static constexpr int frameOrder[] = { 4, 0, 3, 1, 2 };
1135 static_assert(SK_ARRAY_COUNT(frameOrder) == kTileCount, "");
1136
1137 for (int i = 0; i < kTileCount; ++i) {
1138 const SkScalar y = frameOrder[i] * kTileSize;
1139
1140 for (int j = 0; j < kTileCount; ++j) {
1141 const SkScalar x = frameOrder[j] * kTileSize;
1142 SkRect dest = SkRect::MakeXYWH(x, y, kTileSize, kTileSize);
1143
1144 const auto t = t_rate * (frameOrder[i] * kTileCount + frameOrder[j]);
1145 {
1146 SkAutoCanvasRestore acr(canvas, true);
1147 canvas->clipRect(dest, true);
1148 canvas->concat(SkMatrix::MakeRectToRect(SkRect::MakeSize(animation->size()),
1149 dest,
1150 SkMatrix::kCenter_ScaleToFit));
1151 animation->seek(t);
1152 animation->render(canvas);
1153 }
1154 }
1155 }
1156
1157 return "";
1158 }
1159
size() const1160 SkISize SkottieSrc::size() const {
1161 return SkISize::Make(kTargetSize, kTargetSize);
1162 }
1163
name() const1164 Name SkottieSrc::name() const { return SkOSPath::Basename(fPath.c_str()); }
1165
veto(SinkFlags flags) const1166 bool SkottieSrc::veto(SinkFlags flags) const {
1167 // No need to test to non-(raster||gpu||vector) or indirect backends.
1168 bool type_ok = flags.type == SinkFlags::kRaster
1169 || flags.type == SinkFlags::kGPU
1170 || flags.type == SinkFlags::kVector;
1171
1172 return !type_ok || flags.approach != SinkFlags::kDirect;
1173 }
1174 #endif
1175
1176 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1177 #if defined(SK_XML)
1178 // Used when the image doesn't have an intrinsic size.
1179 static const SkSize kDefaultSVGSize = {1000, 1000};
1180
1181 // Used to force-scale tiny fixed-size images.
1182 static const SkSize kMinimumSVGSize = {128, 128};
1183
SVGSrc(Path path)1184 SVGSrc::SVGSrc(Path path)
1185 : fName(SkOSPath::Basename(path.c_str()))
1186 , fScale(1) {
1187
1188 sk_sp<SkData> data(SkData::MakeFromFileName(path.c_str()));
1189 if (!data) {
1190 return;
1191 }
1192
1193 SkMemoryStream stream(std::move(data));
1194 fDom = SkSVGDOM::MakeFromStream(stream);
1195 if (!fDom) {
1196 return;
1197 }
1198
1199 const SkSize& sz = fDom->containerSize();
1200 if (sz.isEmpty()) {
1201 // no intrinsic size
1202 fDom->setContainerSize(kDefaultSVGSize);
1203 } else {
1204 fScale = SkTMax(1.f, SkTMax(kMinimumSVGSize.width() / sz.width(),
1205 kMinimumSVGSize.height() / sz.height()));
1206 }
1207 }
1208
draw(SkCanvas * canvas) const1209 Error SVGSrc::draw(SkCanvas* canvas) const {
1210 if (!fDom) {
1211 return SkStringPrintf("Unable to parse file: %s", fName.c_str());
1212 }
1213
1214 SkAutoCanvasRestore acr(canvas, true);
1215 canvas->scale(fScale, fScale);
1216 fDom->render(canvas);
1217
1218 return "";
1219 }
1220
size() const1221 SkISize SVGSrc::size() const {
1222 if (!fDom) {
1223 return {0, 0};
1224 }
1225
1226 return SkSize{fDom->containerSize().width() * fScale, fDom->containerSize().height() * fScale}
1227 .toRound();
1228 }
1229
name() const1230 Name SVGSrc::name() const { return fName; }
1231
veto(SinkFlags flags) const1232 bool SVGSrc::veto(SinkFlags flags) const {
1233 // No need to test to non-(raster||gpu||vector) or indirect backends.
1234 bool type_ok = flags.type == SinkFlags::kRaster
1235 || flags.type == SinkFlags::kGPU
1236 || flags.type == SinkFlags::kVector;
1237
1238 return !type_ok || flags.approach != SinkFlags::kDirect;
1239 }
1240
1241 #endif // defined(SK_XML)
1242 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1243
MSKPSrc(Path path)1244 MSKPSrc::MSKPSrc(Path path) : fPath(path) {
1245 std::unique_ptr<SkStreamAsset> stream = SkStream::MakeFromFile(fPath.c_str());
1246 int count = SkMultiPictureDocumentReadPageCount(stream.get());
1247 if (count > 0) {
1248 fPages.reset(count);
1249 (void)SkMultiPictureDocumentReadPageSizes(stream.get(), &fPages[0], fPages.count());
1250 }
1251 }
1252
pageCount() const1253 int MSKPSrc::pageCount() const { return fPages.count(); }
1254
size() const1255 SkISize MSKPSrc::size() const { return this->size(0); }
size(int i) const1256 SkISize MSKPSrc::size(int i) const {
1257 return i >= 0 && i < fPages.count() ? fPages[i].fSize.toCeil() : SkISize{0, 0};
1258 }
1259
draw(SkCanvas * c) const1260 Error MSKPSrc::draw(SkCanvas* c) const { return this->draw(0, c); }
draw(int i,SkCanvas * canvas) const1261 Error MSKPSrc::draw(int i, SkCanvas* canvas) const {
1262 if (this->pageCount() == 0) {
1263 return SkStringPrintf("Unable to parse MultiPictureDocument file: %s", fPath.c_str());
1264 }
1265 if (i >= fPages.count() || i < 0) {
1266 return SkStringPrintf("MultiPictureDocument page number out of range: %d", i);
1267 }
1268 SkPicture* page = fPages[i].fPicture.get();
1269 if (!page) {
1270 std::unique_ptr<SkStreamAsset> stream = SkStream::MakeFromFile(fPath.c_str());
1271 if (!stream) {
1272 return SkStringPrintf("Unable to open file: %s", fPath.c_str());
1273 }
1274 if (!SkMultiPictureDocumentRead(stream.get(), &fPages[0], fPages.count())) {
1275 return SkStringPrintf("SkMultiPictureDocument reader failed on page %d: %s", i,
1276 fPath.c_str());
1277 }
1278 page = fPages[i].fPicture.get();
1279 }
1280 canvas->drawPicture(page);
1281 return "";
1282 }
1283
name() const1284 Name MSKPSrc::name() const { return SkOSPath::Basename(fPath.c_str()); }
1285
1286 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1287
draw(const Src & src,SkBitmap *,SkWStream *,SkString *) const1288 Error NullSink::draw(const Src& src, SkBitmap*, SkWStream*, SkString*) const {
1289 return src.draw(SkMakeNullCanvas().get());
1290 }
1291
1292 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1293
compare_bitmaps(const SkBitmap & reference,const SkBitmap & bitmap)1294 static Error compare_bitmaps(const SkBitmap& reference, const SkBitmap& bitmap) {
1295 // The dimensions are a property of the Src only, and so should be identical.
1296 SkASSERT(reference.computeByteSize() == bitmap.computeByteSize());
1297 if (reference.computeByteSize() != bitmap.computeByteSize()) {
1298 return "Dimensions don't match reference";
1299 }
1300 // All SkBitmaps in DM are tight, so this comparison is easy.
1301 if (0 != memcmp(reference.getPixels(), bitmap.getPixels(), reference.computeByteSize())) {
1302 SkString encoded;
1303 SkString errString("Pixels don't match reference");
1304 if (bitmap_to_base64_data_uri(reference, &encoded)) {
1305 errString.append("\nExpected: ");
1306 errString.append(encoded);
1307 } else {
1308 errString.append("\nExpected image failed to encode: ");
1309 errString.append(encoded);
1310 }
1311 if (bitmap_to_base64_data_uri(bitmap, &encoded)) {
1312 errString.append("\nActual: ");
1313 errString.append(encoded);
1314 } else {
1315 errString.append("\nActual image failed to encode: ");
1316 errString.append(encoded);
1317 }
1318 return errString;
1319 }
1320 return "";
1321 }
1322
1323 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1324
1325 static DEFINE_bool(gpuStats, false, "Append GPU stats to the log for each GPU task?");
1326 static DEFINE_bool(preAbandonGpuContext, false,
1327 "Test abandoning the GrContext before running the test.");
1328 static DEFINE_bool(abandonGpuContext, false,
1329 "Test abandoning the GrContext after running each test.");
1330 static DEFINE_bool(releaseAndAbandonGpuContext, false,
1331 "Test releasing all gpu resources and abandoning the GrContext "
1332 "after running each test");
1333 static DEFINE_bool(drawOpClip, false, "Clip each GrDrawOp to its device bounds for testing.");
1334 static DEFINE_bool(programBinaryCache, true, "Use in-memory program binary cache");
1335
GPUSink(GrContextFactory::ContextType ct,GrContextFactory::ContextOverrides overrides,SkCommandLineConfigGpu::SurfType surfType,int samples,bool diText,SkColorType colorType,SkAlphaType alphaType,sk_sp<SkColorSpace> colorSpace,bool threaded,const GrContextOptions & grCtxOptions)1336 GPUSink::GPUSink(GrContextFactory::ContextType ct,
1337 GrContextFactory::ContextOverrides overrides,
1338 SkCommandLineConfigGpu::SurfType surfType,
1339 int samples,
1340 bool diText,
1341 SkColorType colorType,
1342 SkAlphaType alphaType,
1343 sk_sp<SkColorSpace> colorSpace,
1344 bool threaded,
1345 const GrContextOptions& grCtxOptions)
1346 : fContextType(ct)
1347 , fContextOverrides(overrides)
1348 , fSurfType(surfType)
1349 , fSampleCount(samples)
1350 , fUseDIText(diText)
1351 , fColorType(colorType)
1352 , fAlphaType(alphaType)
1353 , fColorSpace(std::move(colorSpace))
1354 , fThreaded(threaded)
1355 , fBaseContextOptions(grCtxOptions) {
1356 if (FLAGS_programBinaryCache) {
1357 fBaseContextOptions.fPersistentCache = &fMemoryCache;
1358 }
1359 }
1360
draw(const Src & src,SkBitmap * dst,SkWStream * dstStream,SkString * log) const1361 Error GPUSink::draw(const Src& src, SkBitmap* dst, SkWStream* dstStream, SkString* log) const {
1362 return this->onDraw(src, dst, dstStream, log, fBaseContextOptions);
1363 }
1364
onDraw(const Src & src,SkBitmap * dst,SkWStream *,SkString * log,const GrContextOptions & baseOptions) const1365 Error GPUSink::onDraw(const Src& src, SkBitmap* dst, SkWStream*, SkString* log,
1366 const GrContextOptions& baseOptions) const {
1367 GrContextOptions grOptions = baseOptions;
1368
1369 // We don't expect the src to mess with the persistent cache or the executor.
1370 SkDEBUGCODE(auto cache = grOptions.fPersistentCache);
1371 SkDEBUGCODE(auto exec = grOptions.fExecutor);
1372 src.modifyGrContextOptions(&grOptions);
1373 SkASSERT(cache == grOptions.fPersistentCache);
1374 SkASSERT(exec == grOptions.fExecutor);
1375
1376 GrContextFactory factory(grOptions);
1377 const SkISize size = src.size();
1378 SkImageInfo info =
1379 SkImageInfo::Make(size.width(), size.height(), fColorType, fAlphaType, fColorSpace);
1380 sk_sp<SkSurface> surface;
1381 GrContext* context = factory.getContextInfo(fContextType, fContextOverrides).grContext();
1382 const int maxDimension = context->priv().caps()->maxTextureSize();
1383 if (maxDimension < SkTMax(size.width(), size.height())) {
1384 return Error::Nonfatal("Src too large to create a texture.\n");
1385 }
1386 uint32_t flags = fUseDIText ? SkSurfaceProps::kUseDeviceIndependentFonts_Flag : 0;
1387 SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType);
1388 GrBackendTexture backendTexture;
1389 GrBackendRenderTarget backendRT;
1390 switch (fSurfType) {
1391 case SkCommandLineConfigGpu::SurfType::kDefault:
1392 surface = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info, fSampleCount,
1393 &props);
1394 break;
1395 case SkCommandLineConfigGpu::SurfType::kBackendTexture:
1396 backendTexture = context->createBackendTexture(
1397 info.width(), info.height(), info.colorType(), SkColors::kTransparent,
1398 GrMipMapped::kNo, GrRenderable::kYes, GrProtected::kNo);
1399 surface = SkSurface::MakeFromBackendTexture(context, backendTexture,
1400 kTopLeft_GrSurfaceOrigin, fSampleCount,
1401 fColorType, info.refColorSpace(), &props);
1402 break;
1403 case SkCommandLineConfigGpu::SurfType::kBackendRenderTarget:
1404 if (1 == fSampleCount) {
1405 auto colorType = SkColorTypeToGrColorType(info.colorType());
1406 backendRT = context->priv().getGpu()->createTestingOnlyBackendRenderTarget(
1407 info.width(), info.height(), colorType);
1408 surface = SkSurface::MakeFromBackendRenderTarget(
1409 context, backendRT, kBottomLeft_GrSurfaceOrigin, info.colorType(),
1410 info.refColorSpace(), &props);
1411 }
1412 break;
1413 }
1414
1415 if (!surface) {
1416 return "Could not create a surface.";
1417 }
1418 if (FLAGS_preAbandonGpuContext) {
1419 factory.abandonContexts();
1420 }
1421 SkCanvas* canvas = surface->getCanvas();
1422 Error err = src.draw(canvas);
1423 if (!err.isEmpty()) {
1424 return err;
1425 }
1426 surface->flush();
1427 if (FLAGS_gpuStats) {
1428 canvas->getGrContext()->priv().dumpCacheStats(log);
1429 canvas->getGrContext()->priv().dumpGpuStats(log);
1430 }
1431 if (info.colorType() == kRGB_565_SkColorType || info.colorType() == kARGB_4444_SkColorType ||
1432 info.colorType() == kRGB_888x_SkColorType) {
1433 // We don't currently support readbacks into these formats on the GPU backend. Convert to
1434 // 32 bit.
1435 info = SkImageInfo::Make(size.width(), size.height(), kRGBA_8888_SkColorType,
1436 kPremul_SkAlphaType, fColorSpace);
1437 }
1438 dst->allocPixels(info);
1439 canvas->readPixels(*dst, 0, 0);
1440 if (FLAGS_abandonGpuContext) {
1441 factory.abandonContexts();
1442 } else if (FLAGS_releaseAndAbandonGpuContext) {
1443 factory.releaseResourcesAndAbandonContexts();
1444 }
1445 if (!context->abandoned()) {
1446 surface.reset();
1447 if (backendTexture.isValid()) {
1448 context->deleteBackendTexture(backendTexture);
1449 }
1450 if (backendRT.isValid()) {
1451 context->priv().getGpu()->deleteTestingOnlyBackendRenderTarget(backendRT);
1452 }
1453 }
1454 if (grOptions.fPersistentCache) {
1455 context->storeVkPipelineCacheData();
1456 }
1457 return "";
1458 }
1459
1460 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1461
GPUThreadTestingSink(GrContextFactory::ContextType ct,GrContextFactory::ContextOverrides overrides,SkCommandLineConfigGpu::SurfType surfType,int samples,bool diText,SkColorType colorType,SkAlphaType alphaType,sk_sp<SkColorSpace> colorSpace,bool threaded,const GrContextOptions & grCtxOptions)1462 GPUThreadTestingSink::GPUThreadTestingSink(GrContextFactory::ContextType ct,
1463 GrContextFactory::ContextOverrides overrides,
1464 SkCommandLineConfigGpu::SurfType surfType,
1465 int samples,
1466 bool diText,
1467 SkColorType colorType,
1468 SkAlphaType alphaType,
1469 sk_sp<SkColorSpace> colorSpace,
1470 bool threaded,
1471 const GrContextOptions& grCtxOptions)
1472 : INHERITED(ct, overrides, surfType, samples, diText, colorType, alphaType,
1473 std::move(colorSpace), threaded, grCtxOptions)
1474 , fExecutor(SkExecutor::MakeFIFOThreadPool(FLAGS_gpuThreads)) {
1475 SkASSERT(fExecutor);
1476 }
1477
draw(const Src & src,SkBitmap * dst,SkWStream * wStream,SkString * log) const1478 Error GPUThreadTestingSink::draw(const Src& src, SkBitmap* dst, SkWStream* wStream,
1479 SkString* log) const {
1480 // Draw twice, once with worker threads, and once without. Verify that we get the same result.
1481 // Also, force us to only use the software path renderer, so we really stress-test the threaded
1482 // version of that code.
1483 GrContextOptions contextOptions = this->baseContextOptions();
1484 contextOptions.fGpuPathRenderers = GpuPathRenderers::kNone;
1485 contextOptions.fExecutor = fExecutor.get();
1486
1487 Error err = this->onDraw(src, dst, wStream, log, contextOptions);
1488 if (!err.isEmpty() || !dst) {
1489 return err;
1490 }
1491
1492 SkBitmap reference;
1493 SkString refLog;
1494 SkDynamicMemoryWStream refStream;
1495 contextOptions.fExecutor = nullptr;
1496 Error refErr = this->onDraw(src, &reference, &refStream, &refLog, contextOptions);
1497 if (!refErr.isEmpty()) {
1498 return refErr;
1499 }
1500
1501 return compare_bitmaps(reference, *dst);
1502 }
1503
1504 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1505
GPUPersistentCacheTestingSink(GrContextFactory::ContextType ct,GrContextFactory::ContextOverrides overrides,SkCommandLineConfigGpu::SurfType surfType,int samples,bool diText,SkColorType colorType,SkAlphaType alphaType,sk_sp<SkColorSpace> colorSpace,bool threaded,const GrContextOptions & grCtxOptions,int cacheType)1506 GPUPersistentCacheTestingSink::GPUPersistentCacheTestingSink(
1507 GrContextFactory::ContextType ct,
1508 GrContextFactory::ContextOverrides overrides,
1509 SkCommandLineConfigGpu::SurfType surfType,
1510 int samples,
1511 bool diText,
1512 SkColorType colorType,
1513 SkAlphaType alphaType,
1514 sk_sp<SkColorSpace> colorSpace,
1515 bool threaded,
1516 const GrContextOptions& grCtxOptions,
1517 int cacheType)
1518 : INHERITED(ct, overrides, surfType, samples, diText, colorType, alphaType,
1519 std::move(colorSpace), threaded, grCtxOptions)
1520 , fCacheType(cacheType) {}
1521
draw(const Src & src,SkBitmap * dst,SkWStream * wStream,SkString * log) const1522 Error GPUPersistentCacheTestingSink::draw(const Src& src, SkBitmap* dst, SkWStream* wStream,
1523 SkString* log) const {
1524 // Draw twice, once with a cold cache, and again with a warm cache. Verify that we get the same
1525 // result.
1526 sk_gpu_test::MemoryCache memoryCache;
1527 GrContextOptions contextOptions = this->baseContextOptions();
1528 contextOptions.fPersistentCache = &memoryCache;
1529 contextOptions.fDisallowGLSLBinaryCaching = (fCacheType == 2);
1530 // anglebug.com/3619
1531 contextOptions.fGpuPathRenderers =
1532 contextOptions.fGpuPathRenderers & ~GpuPathRenderers::kStencilAndCover;
1533
1534 Error err = this->onDraw(src, dst, wStream, log, contextOptions);
1535 if (!err.isEmpty() || !dst) {
1536 return err;
1537 }
1538
1539 SkBitmap reference;
1540 SkString refLog;
1541 SkDynamicMemoryWStream refStream;
1542 memoryCache.resetNumCacheMisses();
1543 Error refErr = this->onDraw(src, &reference, &refStream, &refLog, contextOptions);
1544 if (!refErr.isEmpty()) {
1545 return refErr;
1546 }
1547 SkASSERT(!memoryCache.numCacheMisses());
1548
1549 return compare_bitmaps(reference, *dst);
1550 }
1551
1552 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
draw_skdocument(const Src & src,SkDocument * doc,SkWStream * dst)1553 static Error draw_skdocument(const Src& src, SkDocument* doc, SkWStream* dst) {
1554 if (src.size().isEmpty()) {
1555 return "Source has empty dimensions";
1556 }
1557 SkASSERT(doc);
1558 int pageCount = src.pageCount();
1559 for (int i = 0; i < pageCount; ++i) {
1560 int width = src.size(i).width(), height = src.size(i).height();
1561 SkCanvas* canvas =
1562 doc->beginPage(SkIntToScalar(width), SkIntToScalar(height));
1563 if (!canvas) {
1564 return "SkDocument::beginPage(w,h) returned nullptr";
1565 }
1566 Error err = src.draw(i, canvas);
1567 if (!err.isEmpty()) {
1568 return err;
1569 }
1570 doc->endPage();
1571 }
1572 doc->close();
1573 dst->flush();
1574 return "";
1575 }
1576
draw(const Src & src,SkBitmap *,SkWStream * dst,SkString *) const1577 Error PDFSink::draw(const Src& src, SkBitmap*, SkWStream* dst, SkString*) const {
1578 SkPDF::Metadata metadata;
1579 metadata.fTitle = src.name();
1580 metadata.fSubject = "rendering correctness test";
1581 metadata.fCreator = "Skia/DM";
1582 metadata.fRasterDPI = fRasterDpi;
1583 metadata.fPDFA = fPDFA;
1584 #if SK_PDF_TEST_EXECUTOR
1585 std::unique_ptr<SkExecutor> executor = SkExecutor::MakeFIFOThreadPool();
1586 metadata.fExecutor = executor.get();
1587 #endif
1588 auto doc = SkPDF::MakeDocument(dst, metadata);
1589 if (!doc) {
1590 return "SkPDF::MakeDocument() returned nullptr";
1591 }
1592 return draw_skdocument(src, doc.get(), dst);
1593 }
1594
1595 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1596
XPSSink()1597 XPSSink::XPSSink() {}
1598
1599 #ifdef SK_BUILD_FOR_WIN
make_xps_factory()1600 static SkTScopedComPtr<IXpsOMObjectFactory> make_xps_factory() {
1601 IXpsOMObjectFactory* factory;
1602 HRN(CoCreateInstance(CLSID_XpsOMObjectFactory,
1603 nullptr,
1604 CLSCTX_INPROC_SERVER,
1605 IID_PPV_ARGS(&factory)));
1606 return SkTScopedComPtr<IXpsOMObjectFactory>(factory);
1607 }
1608
draw(const Src & src,SkBitmap *,SkWStream * dst,SkString *) const1609 Error XPSSink::draw(const Src& src, SkBitmap*, SkWStream* dst, SkString*) const {
1610 SkAutoCoInitialize com;
1611 if (!com.succeeded()) {
1612 return "Could not initialize COM.";
1613 }
1614 SkTScopedComPtr<IXpsOMObjectFactory> factory = make_xps_factory();
1615 if (!factory) {
1616 return "Failed to create XPS Factory.";
1617 }
1618 auto doc = SkXPS::MakeDocument(dst, factory.get());
1619 if (!doc) {
1620 return "SkXPS::MakeDocument() returned nullptr";
1621 }
1622 return draw_skdocument(src, doc.get(), dst);
1623 }
1624 #else
draw(const Src & src,SkBitmap *,SkWStream * dst,SkString *) const1625 Error XPSSink::draw(const Src& src, SkBitmap*, SkWStream* dst, SkString*) const {
1626 return "XPS not supported on this platform.";
1627 }
1628 #endif
1629
1630 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1631
SKPSink()1632 SKPSink::SKPSink() {}
1633
draw(const Src & src,SkBitmap *,SkWStream * dst,SkString *) const1634 Error SKPSink::draw(const Src& src, SkBitmap*, SkWStream* dst, SkString*) const {
1635 SkSize size;
1636 size = src.size();
1637 SkPictureRecorder recorder;
1638 Error err = src.draw(recorder.beginRecording(size.width(), size.height()));
1639 if (!err.isEmpty()) {
1640 return err;
1641 }
1642 recorder.finishRecordingAsPicture()->serialize(dst);
1643 return "";
1644 }
1645
1646 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1647
draw(const Src & src,SkBitmap *,SkWStream * dst,SkString *) const1648 Error DebugSink::draw(const Src& src, SkBitmap*, SkWStream* dst, SkString*) const {
1649 DebugCanvas debugCanvas(src.size().width(), src.size().height());
1650 Error err = src.draw(&debugCanvas);
1651 if (!err.isEmpty()) {
1652 return err;
1653 }
1654 std::unique_ptr<SkCanvas> nullCanvas = SkMakeNullCanvas();
1655 UrlDataManager dataManager(SkString("data"));
1656 SkJSONWriter writer(dst, SkJSONWriter::Mode::kPretty);
1657 writer.beginObject(); // root
1658 debugCanvas.toJSON(writer, dataManager, debugCanvas.getSize(), nullCanvas.get());
1659 writer.endObject(); // root
1660 writer.flush();
1661 return "";
1662 }
1663
1664 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1665
SVGSink(int pageIndex)1666 SVGSink::SVGSink(int pageIndex) : fPageIndex(pageIndex) {}
1667
draw(const Src & src,SkBitmap *,SkWStream * dst,SkString *) const1668 Error SVGSink::draw(const Src& src, SkBitmap*, SkWStream* dst, SkString*) const {
1669 #if defined(SK_XML)
1670 if (src.pageCount() > 1) {
1671 int pageCount = src.pageCount();
1672 if (fPageIndex > pageCount - 1) {
1673 return Error(SkStringPrintf("Page index %d too high for document with only %d pages.",
1674 fPageIndex, pageCount));
1675 }
1676 }
1677 return src.draw(fPageIndex,
1678 SkSVGCanvas::Make(SkRect::MakeWH(SkIntToScalar(src.size().width()),
1679 SkIntToScalar(src.size().height())),
1680 dst)
1681 .get());
1682 #else
1683 (void)fPageIndex;
1684 return Error("SVG sink is disabled.");
1685 #endif // SK_XML
1686 }
1687
1688 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1689
RasterSink(SkColorType colorType,sk_sp<SkColorSpace> colorSpace)1690 RasterSink::RasterSink(SkColorType colorType, sk_sp<SkColorSpace> colorSpace)
1691 : fColorType(colorType)
1692 , fColorSpace(std::move(colorSpace)) {}
1693
draw(const Src & src,SkBitmap * dst,SkWStream *,SkString *) const1694 Error RasterSink::draw(const Src& src, SkBitmap* dst, SkWStream*, SkString*) const {
1695 const SkISize size = src.size();
1696 // If there's an appropriate alpha type for this color type, use it, otherwise use premul.
1697 SkAlphaType alphaType = kPremul_SkAlphaType;
1698 (void)SkColorTypeValidateAlphaType(fColorType, alphaType, &alphaType);
1699
1700 dst->allocPixelsFlags(SkImageInfo::Make(size.width(), size.height(),
1701 fColorType, alphaType, fColorSpace),
1702 SkBitmap::kZeroPixels_AllocFlag);
1703
1704 SkCanvas canvas(*dst);
1705 return src.draw(&canvas);
1706 }
1707
1708 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1709
1710 // Handy for front-patching a Src. Do whatever up-front work you need, then call draw_to_canvas(),
1711 // passing the Sink draw() arguments, a size, and a function draws into an SkCanvas.
1712 // Several examples below.
1713
1714 template <typename Fn>
draw_to_canvas(Sink * sink,SkBitmap * bitmap,SkWStream * stream,SkString * log,SkISize size,const Fn & draw)1715 static Error draw_to_canvas(Sink* sink, SkBitmap* bitmap, SkWStream* stream, SkString* log,
1716 SkISize size, const Fn& draw) {
1717 class ProxySrc : public Src {
1718 public:
1719 ProxySrc(SkISize size, const Fn& draw) : fSize(size), fDraw(draw) {}
1720 Error draw(SkCanvas* canvas) const override { return fDraw(canvas); }
1721 Name name() const override { return "ProxySrc"; }
1722 SkISize size() const override { return fSize; }
1723 private:
1724 SkISize fSize;
1725 const Fn& fDraw;
1726 };
1727 return sink->draw(ProxySrc(size, draw), bitmap, stream, log);
1728 }
1729
1730 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1731
1732 static DEFINE_bool(check, true, "If true, have most Via- modes fail if they affect the output.");
1733
1734 // Is *bitmap identical to what you get drawing src into sink?
check_against_reference(const SkBitmap * bitmap,const Src & src,Sink * sink)1735 static Error check_against_reference(const SkBitmap* bitmap, const Src& src, Sink* sink) {
1736 // We can only check raster outputs.
1737 // (Non-raster outputs like .pdf, .skp, .svg may differ but still draw identically.)
1738 if (FLAGS_check && bitmap) {
1739 SkBitmap reference;
1740 SkString log;
1741 SkDynamicMemoryWStream wStream;
1742 Error err = sink->draw(src, &reference, &wStream, &log);
1743 // If we can draw into this Sink via some pipeline, we should be able to draw directly.
1744 SkASSERT(err.isEmpty());
1745 if (!err.isEmpty()) {
1746 return err;
1747 }
1748 return compare_bitmaps(reference, *bitmap);
1749 }
1750 return "";
1751 }
1752
1753 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1754
auto_compute_translate(SkMatrix * matrix,int srcW,int srcH)1755 static SkISize auto_compute_translate(SkMatrix* matrix, int srcW, int srcH) {
1756 SkRect bounds = SkRect::MakeIWH(srcW, srcH);
1757 matrix->mapRect(&bounds);
1758 matrix->postTranslate(-bounds.x(), -bounds.y());
1759 return {SkScalarRoundToInt(bounds.width()), SkScalarRoundToInt(bounds.height())};
1760 }
1761
ViaMatrix(SkMatrix matrix,Sink * sink)1762 ViaMatrix::ViaMatrix(SkMatrix matrix, Sink* sink) : Via(sink), fMatrix(matrix) {}
1763
draw(const Src & src,SkBitmap * bitmap,SkWStream * stream,SkString * log) const1764 Error ViaMatrix::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
1765 SkMatrix matrix = fMatrix;
1766 SkISize size = auto_compute_translate(&matrix, src.size().width(), src.size().height());
1767 return draw_to_canvas(fSink.get(), bitmap, stream, log, size, [&](SkCanvas* canvas) {
1768 canvas->concat(matrix);
1769 return src.draw(canvas);
1770 });
1771 }
1772
1773 // Undoes any flip or 90 degree rotate without changing the scale of the bitmap.
1774 // This should be pixel-preserving.
ViaUpright(SkMatrix matrix,Sink * sink)1775 ViaUpright::ViaUpright(SkMatrix matrix, Sink* sink) : Via(sink), fMatrix(matrix) {}
1776
draw(const Src & src,SkBitmap * bitmap,SkWStream * stream,SkString * log) const1777 Error ViaUpright::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
1778 Error err = fSink->draw(src, bitmap, stream, log);
1779 if (!err.isEmpty()) {
1780 return err;
1781 }
1782
1783 SkMatrix inverse;
1784 if (!fMatrix.rectStaysRect() || !fMatrix.invert(&inverse)) {
1785 return "Cannot upright --matrix.";
1786 }
1787 SkMatrix upright = SkMatrix::I();
1788 upright.setScaleX(SkScalarSignAsScalar(inverse.getScaleX()));
1789 upright.setScaleY(SkScalarSignAsScalar(inverse.getScaleY()));
1790 upright.setSkewX(SkScalarSignAsScalar(inverse.getSkewX()));
1791 upright.setSkewY(SkScalarSignAsScalar(inverse.getSkewY()));
1792
1793 SkBitmap uprighted;
1794 SkISize size = auto_compute_translate(&upright, bitmap->width(), bitmap->height());
1795 uprighted.allocPixels(bitmap->info().makeWH(size.width(), size.height()));
1796
1797 SkCanvas canvas(uprighted);
1798 canvas.concat(upright);
1799 SkPaint paint;
1800 paint.setBlendMode(SkBlendMode::kSrc);
1801 canvas.drawBitmap(*bitmap, 0, 0, &paint);
1802
1803 *bitmap = uprighted;
1804 return "";
1805 }
1806
1807 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1808
draw(const Src & src,SkBitmap * bitmap,SkWStream * stream,SkString * log) const1809 Error ViaSerialization::draw(
1810 const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
1811 // Record our Src into a picture.
1812 auto size = src.size();
1813 SkPictureRecorder recorder;
1814 Error err = src.draw(recorder.beginRecording(SkIntToScalar(size.width()),
1815 SkIntToScalar(size.height())));
1816 if (!err.isEmpty()) {
1817 return err;
1818 }
1819 sk_sp<SkPicture> pic(recorder.finishRecordingAsPicture());
1820
1821 // Serialize it and then deserialize it.
1822 sk_sp<SkPicture> deserialized(SkPicture::MakeFromData(pic->serialize().get()));
1823
1824 err = draw_to_canvas(fSink.get(), bitmap, stream, log, size, [&](SkCanvas* canvas) {
1825 canvas->drawPicture(deserialized);
1826 return "";
1827 });
1828 if (!err.isEmpty()) {
1829 return err;
1830 }
1831
1832 return check_against_reference(bitmap, src, fSink.get());
1833 }
1834
1835 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1836
ViaTiles(int w,int h,SkBBHFactory * factory,Sink * sink)1837 ViaTiles::ViaTiles(int w, int h, SkBBHFactory* factory, Sink* sink)
1838 : Via(sink)
1839 , fW(w)
1840 , fH(h)
1841 , fFactory(factory) {}
1842
draw(const Src & src,SkBitmap * bitmap,SkWStream * stream,SkString * log) const1843 Error ViaTiles::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
1844 auto size = src.size();
1845 SkPictureRecorder recorder;
1846 Error err = src.draw(recorder.beginRecording(SkIntToScalar(size.width()),
1847 SkIntToScalar(size.height()),
1848 fFactory.get()));
1849 if (!err.isEmpty()) {
1850 return err;
1851 }
1852 sk_sp<SkPicture> pic(recorder.finishRecordingAsPicture());
1853
1854 return draw_to_canvas(fSink.get(), bitmap, stream, log, src.size(), [&](SkCanvas* canvas) {
1855 const int xTiles = (size.width() + fW - 1) / fW,
1856 yTiles = (size.height() + fH - 1) / fH;
1857 SkMultiPictureDraw mpd(xTiles*yTiles);
1858 SkTArray<sk_sp<SkSurface>> surfaces;
1859 // surfaces.setReserve(xTiles*yTiles);
1860
1861 SkImageInfo info = canvas->imageInfo().makeWH(fW, fH);
1862 for (int j = 0; j < yTiles; j++) {
1863 for (int i = 0; i < xTiles; i++) {
1864 // This lets our ultimate Sink determine the best kind of surface.
1865 // E.g., if it's a GpuSink, the surfaces and images are textures.
1866 auto s = canvas->makeSurface(info);
1867 if (!s) {
1868 s = SkSurface::MakeRaster(info); // Some canvases can't create surfaces.
1869 }
1870 surfaces.push_back(s);
1871 SkCanvas* c = s->getCanvas();
1872 c->translate(SkIntToScalar(-i * fW),
1873 SkIntToScalar(-j * fH)); // Line up the canvas with this tile.
1874 mpd.add(c, pic.get());
1875 }
1876 }
1877 mpd.draw();
1878 for (int j = 0; j < yTiles; j++) {
1879 for (int i = 0; i < xTiles; i++) {
1880 sk_sp<SkImage> image(surfaces[i+xTiles*j]->makeImageSnapshot());
1881 canvas->drawImage(image, SkIntToScalar(i*fW), SkIntToScalar(j*fH));
1882 }
1883 }
1884 return "";
1885 });
1886 }
1887
1888 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1889
ViaDDL(int numReplays,int numDivisions,Sink * sink)1890 ViaDDL::ViaDDL(int numReplays, int numDivisions, Sink* sink)
1891 : Via(sink), fNumReplays(numReplays), fNumDivisions(numDivisions) {}
1892
draw(const Src & src,SkBitmap * bitmap,SkWStream * stream,SkString * log) const1893 Error ViaDDL::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
1894 auto size = src.size();
1895 SkPictureRecorder recorder;
1896 Error err = src.draw(recorder.beginRecording(SkIntToScalar(size.width()),
1897 SkIntToScalar(size.height())));
1898 if (!err.isEmpty()) {
1899 return err;
1900 }
1901 sk_sp<SkPicture> inputPicture(recorder.finishRecordingAsPicture());
1902
1903 // this is our ultimate final drawing area/rect
1904 SkIRect viewport = SkIRect::MakeWH(size.fWidth, size.fHeight);
1905
1906 DDLPromiseImageHelper promiseImageHelper;
1907 sk_sp<SkData> compressedPictureData = promiseImageHelper.deflateSKP(inputPicture.get());
1908 if (!compressedPictureData) {
1909 return SkStringPrintf("ViaDDL: Couldn't deflate SkPicture");
1910 }
1911 auto draw = [&](SkCanvas* canvas) -> Error {
1912 GrContext* context = canvas->getGrContext();
1913 if (!context || !context->priv().getGpu()) {
1914 return SkStringPrintf("DDLs are GPU only");
1915 }
1916
1917 // This is here bc this is the first point where we have access to the context
1918 promiseImageHelper.uploadAllToGPU(context);
1919 // We draw N times, with a clear between.
1920 for (int replay = 0; replay < fNumReplays; ++replay) {
1921 if (replay > 0) {
1922 // Clear the drawing of the previous replay
1923 canvas->clear(SK_ColorTRANSPARENT);
1924 }
1925 // First, create all the tiles (including their individual dest surfaces)
1926 DDLTileHelper tiles(canvas, viewport, fNumDivisions);
1927
1928 // Second, reinflate the compressed picture individually for each thread
1929 // This recreates the promise SkImages on each replay iteration. We are currently
1930 // relying on this to test using a SkPromiseImageTexture to fulfill different
1931 // SkImages. On each replay the promise SkImages are recreated in createSKPPerTile.
1932 tiles.createSKPPerTile(compressedPictureData.get(), promiseImageHelper);
1933
1934 // Third, create the DDLs in parallel
1935 tiles.createDDLsInParallel();
1936
1937 if (replay == fNumReplays - 1) {
1938 // This drops the promiseImageHelper's refs on all the promise images if we're in
1939 // the last run.
1940 promiseImageHelper.reset();
1941 }
1942
1943 // Fourth, synchronously render the display lists into the dest tiles
1944 // TODO: it would be cool to not wait until all the tiles are drawn to begin
1945 // drawing to the GPU and composing to the final surface
1946 tiles.drawAllTilesAndFlush(context, false);
1947
1948 // Finally, compose the drawn tiles into the result
1949 // Note: the separation between the tiles and the final composition better
1950 // matches Chrome but costs us a copy
1951 tiles.composeAllTiles(canvas);
1952 context->flush();
1953 }
1954 return "";
1955 };
1956 return draw_to_canvas(fSink.get(), bitmap, stream, log, size, draw);
1957 }
1958
1959 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1960
draw(const Src & src,SkBitmap * bitmap,SkWStream * stream,SkString * log) const1961 Error ViaPicture::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
1962 auto size = src.size();
1963 Error err = draw_to_canvas(fSink.get(), bitmap, stream, log, size, [&](SkCanvas* canvas) {
1964 SkPictureRecorder recorder;
1965 sk_sp<SkPicture> pic;
1966 Error err = src.draw(recorder.beginRecording(SkIntToScalar(size.width()),
1967 SkIntToScalar(size.height())));
1968 if (!err.isEmpty()) {
1969 return err;
1970 }
1971 pic = recorder.finishRecordingAsPicture();
1972 canvas->drawPicture(pic);
1973 return err;
1974 });
1975 if (!err.isEmpty()) {
1976 return err;
1977 }
1978
1979 return check_against_reference(bitmap, src, fSink.get());
1980 }
1981
1982 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1983
1984 #ifdef TEST_VIA_SVG
1985 #include "experimental/svg/model/SkSVGDOM.h"
1986 #include "include/svg/SkSVGCanvas.h"
1987 #include "src/xml/SkXMLWriter.h"
1988
draw(const Src & src,SkBitmap * bitmap,SkWStream * stream,SkString * log) const1989 Error ViaSVG::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
1990 auto size = src.size();
1991 return draw_to_canvas(fSink.get(), bitmap, stream, log, size, [&](SkCanvas* canvas) -> Error {
1992 SkDynamicMemoryWStream wstream;
1993 SkXMLStreamWriter writer(&wstream);
1994 Error err = src.draw(SkSVGCanvas::Make(SkRect::Make(size), &writer).get());
1995 if (!err.isEmpty()) {
1996 return err;
1997 }
1998 std::unique_ptr<SkStream> rstream(wstream.detachAsStream());
1999 auto dom = SkSVGDOM::MakeFromStream(*rstream);
2000 if (dom) {
2001 dom->setContainerSize(SkSize::Make(size));
2002 dom->render(canvas);
2003 }
2004 return "";
2005 });
2006 }
2007 #endif
2008
2009 } // namespace DM
2010