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