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