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