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 82 GMSrc::GMSrc(skiagm::GMRegistry::Factory factory) : fFactory(factory) {} 83 84 Error GMSrc::draw(SkCanvas* canvas) const { 85 std::unique_ptr<skiagm::GM> gm(fFactory(nullptr)); 86 gm->draw(canvas); 87 return ""; 88 } 89 90 SkISize GMSrc::size() const { 91 std::unique_ptr<skiagm::GM> gm(fFactory(nullptr)); 92 return gm->getISize(); 93 } 94 95 Name GMSrc::name() const { 96 std::unique_ptr<skiagm::GM> gm(fFactory(nullptr)); 97 return gm->getName(); 98 } 99 100 void GMSrc::modifyGrContextOptions(GrContextOptions* options) const { 101 std::unique_ptr<skiagm::GM> gm(fFactory(nullptr)); 102 gm->modifyGrContextOptions(options); 103 } 104 105 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 106 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 114 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 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 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 138 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 266 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 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 279 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 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 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 319 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. 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. 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 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 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. 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 431 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 773 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 794 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 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 817 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 823 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 885 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 894 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 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 913 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 923 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 1006 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 1015 Name ImageGenSrc::name() const { 1016 return SkOSPath::Basename(fPath.c_str()); 1017 } 1018 1019 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 1020 1021 ColorCodecSrc::ColorCodecSrc(Path path, Mode mode, SkColorType colorType) 1022 : fPath(path) 1023 , fMode(mode) 1024 , fColorType(colorType) 1025 {} 1026 1027 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 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 1053 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 1136 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 1145 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 1153 SKPSrc::SKPSrc(Path path) : fPath(path) { } 1154 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 1169 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 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 1193 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 1201 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 1212 DDLSKPSrc::DDLSKPSrc(Path path) : fPath(path) { } 1213 1214 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 1310 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 1318 Name DDLSKPSrc::name() const { return SkOSPath::Basename(fPath.c_str()); } 1319 1320 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 1321 1322 #if !defined(SK_BUILD_FOR_GOOGLE3) 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 1340 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 1398 SkISize SkottieSrc::size() const { 1399 // Padding for grid. 1400 return SkISize::Make(kTileCount * (fTileSize.width() + 1), 1401 kTileCount * (fTileSize.height() + 1)); 1402 } 1403 1404 Name SkottieSrc::name() const { return fName; } 1405 1406 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 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 1447 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 1459 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 1468 Name SVGSrc::name() const { return fName; } 1469 1470 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 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 1491 int MSKPSrc::pageCount() const { return fPages.count(); } 1492 1493 SkISize MSKPSrc::size() const { return this->size(0); } 1494 SkISize MSKPSrc::size(int i) const { 1495 return i >= 0 && i < fPages.count() ? fPages[i].fSize.toCeil() : SkISize{0, 0}; 1496 } 1497 1498 Error MSKPSrc::draw(SkCanvas* c) const { return this->draw(0, c); } 1499 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 1522 Name MSKPSrc::name() const { return SkOSPath::Basename(fPath.c_str()); } 1523 1524 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 1525 1526 Error NullSink::draw(const Src& src, SkBitmap*, SkWStream*, SkString*) const { 1527 return src.draw(SkMakeNullCanvas().get()); 1528 } 1529 1530 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 1531 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 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 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 1622 Error GPUSink::draw(const Src& src, SkBitmap* dst, SkWStream* dstStream, SkString* log) const { 1623 return this->onDraw(src, dst, dstStream, log, fBaseContextOptions); 1624 } 1625 1626 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 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