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