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