Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
DMSrcSink.cpp
Go to the documentation of this file.
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 "dm/DMSrcSink.h"
13#include "include/core/SkData.h"
39#include "modules/skcms/skcms.h"
43#include "src/base/SkRandom.h"
44#include "src/base/SkTLazy.h"
48#include "src/core/SkOSFile.h"
52#include "src/core/SkRecorder.h"
61#include "src/utils/SkOSPath.h"
64#include "tools/DDLTileHelper.h"
65#include "tools/EncodeUtils.h"
66#include "tools/GpuToolUtils.h"
67#include "tools/Resources.h"
69#include "tools/ToolUtils.h"
75
76#if defined(SK_BUILD_FOR_WIN)
81
82#include <XpsObjectModel.h>
83#endif
84
85#if defined(SK_ENABLE_SKOTTIE)
88#endif
89
90#if defined(SK_ENABLE_SVG)
94#include "src/xml/SkXMLWriter.h"
95#endif
96
97#if defined(SK_GRAPHITE)
104// TODO: Remove this src include once we figure out public readPixels call for Graphite.
108
109#if defined(SK_ENABLE_PRECOMPILE)
119#endif // SK_ENABLE_PRECOMPILE
120
121#endif // SK_GRAPHITE
122
123
124#if defined(SK_ENABLE_ANDROID_UTILS)
126#endif
127#include "tests/TestUtils.h"
128
129#include <cmath>
130#include <functional>
131
132using namespace skia_private;
133
134static DEFINE_bool(RAW_threading, true, "Allow RAW decodes to run on multiple threads?");
135static DEFINE_int(mskpFrame, 0, "Which MSKP frame to draw?");
136
137DECLARE_int(gpuThreads);
138
141
142namespace DM {
143
144GMSrc::GMSrc(skiagm::GMFactory factory) : fFactory(factory) {}
145
146Result GMSrc::draw(SkCanvas* canvas, GraphiteTestContext* testContext) const {
147 std::unique_ptr<skiagm::GM> gm(fFactory());
148 if (gm->isBazelOnly()) {
149 // We skip Bazel-only GMs because they might overlap with existing DM functionality. See
150 // comments in the skiagm::GM::isBazelOnly function declaration for context.
151 return Result(Result::Status::Skip, SkString("Bazel-only GM"));
152 }
153 SkString msg;
154
155 skiagm::DrawResult gpuSetupResult = gm->gpuSetup(canvas, &msg, testContext);
156 switch (gpuSetupResult) {
157 case skiagm::DrawResult::kOk : break;
160 default: SK_ABORT("");
161 }
162
163 skiagm::DrawResult drawResult = gm->draw(canvas, &msg);
164 switch (drawResult) {
168 default: SK_ABORT("");
169 }
170
171 // Note: we don't call "gpuTeardown" here because, when testing DDL recording, we want
172 // the gpu-backed images to live past the lifetime of the GM.
173}
174
176 std::unique_ptr<skiagm::GM> gm(fFactory());
177 return gm->getISize();
178}
179
181 std::unique_ptr<skiagm::GM> gm(fFactory());
182 return gm->getName();
183}
184
186 std::unique_ptr<skiagm::GM> gm(fFactory());
187 gm->modifyGrContextOptions(options);
188}
189
190#if defined(SK_GRAPHITE)
192 std::unique_ptr<skiagm::GM> gm(fFactory());
193 gm->modifyGraphiteContextOptions(options);
194}
195#endif
196
197/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
198
199static SkString get_scaled_name(const Path& path, float scale) {
200 return SkStringPrintf("%s_%.3f", SkOSPath::Basename(path.c_str()).c_str(), scale);
201}
202
203#ifdef SK_ENABLE_ANDROID_UTILS
204BRDSrc::BRDSrc(Path path, Mode mode, CodecSrc::DstColorType dstColorType, uint32_t sampleSize)
205 : fPath(path)
206 , fMode(mode)
207 , fDstColorType(dstColorType)
208 , fSampleSize(sampleSize)
209{}
210
211bool BRDSrc::veto(SinkFlags flags) const {
212 // No need to test to non-raster or indirect backends.
213 return flags.type != SinkFlags::kRaster
214 || flags.approach != SinkFlags::kDirect;
215}
216
217static std::unique_ptr<android::skia::BitmapRegionDecoder> create_brd(Path path) {
220}
221
222static inline void alpha8_to_gray8(SkBitmap* bitmap) {
223 // Android requires kGray8 bitmaps to be tagged as kAlpha8. Here we convert
224 // them back to kGray8 so our test framework can draw them correctly.
225 if (kAlpha_8_SkColorType == bitmap->info().colorType()) {
226 SkImageInfo newInfo = bitmap->info().makeColorType(kGray_8_SkColorType)
227 .makeAlphaType(kOpaque_SkAlphaType);
228 *const_cast<SkImageInfo*>(&bitmap->info()) = newInfo;
229 }
230}
231
232Result BRDSrc::draw(SkCanvas* canvas, GraphiteTestContext*) const {
235 CodecSrc::kGetFromCanvas_DstColorType != fDstColorType)
236 {
237 return Result::Skip("Testing non-565 to 565 is uninteresting.");
238 }
239 switch (fDstColorType) {
240 case CodecSrc::kGetFromCanvas_DstColorType:
241 break;
242 case CodecSrc::kGrayscale_Always_DstColorType:
244 break;
245 default:
246 SkASSERT(false);
247 break;
248 }
249
250 auto brd = create_brd(fPath);
251 if (nullptr == brd) {
252 return Result::Skip("Could not create brd for %s.", fPath.c_str());
253 }
254
255 auto recommendedCT = brd->computeOutputColorType(colorType);
256 if (kRGB_565_SkColorType == colorType && recommendedCT != colorType) {
257 return Result::Skip("Skip decoding non-opaque to 565.");
258 }
259 colorType = recommendedCT;
260
261 auto colorSpace = brd->computeOutputColorSpace(colorType, nullptr);
262
263 const uint32_t width = brd->width();
264 const uint32_t height = brd->height();
265 // Visually inspecting very small output images is not necessary.
266 if ((width / fSampleSize <= 10 || height / fSampleSize <= 10) && 1 != fSampleSize) {
267 return Result::Skip("Scaling very small images is uninteresting.");
268 }
269 switch (fMode) {
270 case kFullImage_Mode: {
272 if (!brd->decodeRegion(&bitmap, nullptr, SkIRect::MakeXYWH(0, 0, width, height),
273 fSampleSize, colorType, false, colorSpace)) {
274 return Result::Fatal("Cannot decode (full) region.");
275 }
276 alpha8_to_gray8(&bitmap);
277
278 canvas->drawImage(bitmap.asImage(), 0, 0);
279 return Result::Ok();
280 }
281 case kDivisor_Mode: {
282 const uint32_t divisor = 2;
283 if (width < divisor || height < divisor) {
284 return Result::Skip("Divisor is larger than image dimension.");
285 }
286
287 // Use a border to test subsets that extend outside the image.
288 // We will not allow the border to be larger than the image dimensions. Allowing
289 // these large borders causes off by one errors that indicate a problem with the
290 // test suite, not a problem with the implementation.
291 const uint32_t maxBorder = std::min(width, height) / (fSampleSize * divisor);
292 const uint32_t scaledBorder = std::min(5u, maxBorder);
293 const uint32_t unscaledBorder = scaledBorder * fSampleSize;
294
295 // We may need to clear the canvas to avoid uninitialized memory.
296 // Assume we are scaling a 780x780 image with sampleSize = 8.
297 // The output image should be 97x97.
298 // Each subset will be 390x390.
299 // Each scaled subset be 48x48.
300 // Four scaled subsets will only fill a 96x96 image.
301 // The bottom row and last column will not be touched.
302 // This is an unfortunate result of our rounding rules when scaling.
303 // Maybe we need to consider testing scaled subsets without trying to
304 // combine them to match the full scaled image? Or maybe this is the
305 // best we can do?
306 canvas->clear(0);
307
308 for (uint32_t x = 0; x < divisor; x++) {
309 for (uint32_t y = 0; y < divisor; y++) {
310 // Calculate the subset dimensions
311 uint32_t subsetWidth = width / divisor;
312 uint32_t subsetHeight = height / divisor;
313 const int left = x * subsetWidth;
314 const int top = y * subsetHeight;
315
316 // Increase the size of the last subset in each row or column, when the
317 // divisor does not divide evenly into the image dimensions
318 subsetWidth += (x + 1 == divisor) ? (width % divisor) : 0;
319 subsetHeight += (y + 1 == divisor) ? (height % divisor) : 0;
320
321 // Increase the size of the subset in order to have a border on each side
322 const int decodeLeft = left - unscaledBorder;
323 const int decodeTop = top - unscaledBorder;
324 const uint32_t decodeWidth = subsetWidth + unscaledBorder * 2;
325 const uint32_t decodeHeight = subsetHeight + unscaledBorder * 2;
327 if (!brd->decodeRegion(&bitmap, nullptr, SkIRect::MakeXYWH(decodeLeft,
328 decodeTop, decodeWidth, decodeHeight), fSampleSize, colorType, false,
329 colorSpace)) {
330 return Result::Fatal("Cannot decode region.");
331 }
332
333 alpha8_to_gray8(&bitmap);
334 canvas->drawImageRect(bitmap.asImage().get(),
335 SkRect::MakeXYWH((SkScalar) scaledBorder, (SkScalar) scaledBorder,
336 (SkScalar) (subsetWidth / fSampleSize),
337 (SkScalar) (subsetHeight / fSampleSize)),
338 SkRect::MakeXYWH((SkScalar) (left / fSampleSize),
339 (SkScalar) (top / fSampleSize),
340 (SkScalar) (subsetWidth / fSampleSize),
341 (SkScalar) (subsetHeight / fSampleSize)),
342 SkSamplingOptions(), nullptr,
344 }
345 }
346 return Result::Ok();
347 }
348 default:
349 SkASSERT(false);
350 return Result::Fatal("Error: Should not be reached.");
351 }
352}
353
354SkISize BRDSrc::size() const {
355 auto brd = create_brd(fPath);
356 if (brd) {
357 return {std::max(1, brd->width() / (int)fSampleSize),
358 std::max(1, brd->height() / (int)fSampleSize)};
359 }
360 return {0, 0};
361}
362
363Name BRDSrc::name() const {
364 // We will replicate the names used by CodecSrc so that images can
365 // be compared in Gold.
366 if (1 == fSampleSize) {
367 return SkOSPath::Basename(fPath.c_str());
368 }
369 return get_scaled_name(fPath, 1.0f / (float) fSampleSize);
370}
371
372#endif // SK_ENABLE_ANDROID_UTILS
373
374/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
375
376static bool serial_from_path_name(const SkString& path) {
377 if (!FLAGS_RAW_threading) {
378 static const char* const exts[] = {
379 "arw", "cr2", "dng", "nef", "nrw", "orf", "raf", "rw2", "pef", "srw",
380 "ARW", "CR2", "DNG", "NEF", "NRW", "ORF", "RAF", "RW2", "PEF", "SRW",
381 };
382 const char* actualExt = strrchr(path.c_str(), '.');
383 if (actualExt) {
384 actualExt++;
385 for (auto* ext : exts) {
386 if (0 == strcmp(ext, actualExt)) {
387 return true;
388 }
389 }
390 }
391 }
392 return false;
393}
394
395CodecSrc::CodecSrc(Path path, Mode mode, DstColorType dstColorType, SkAlphaType dstAlphaType,
396 float scale)
397 : fPath(path)
398 , fMode(mode)
399 , fDstColorType(dstColorType)
400 , fDstAlphaType(dstAlphaType)
401 , fScale(scale)
402 , fRunSerially(serial_from_path_name(path))
403{}
404
406 // Test to direct raster backends (8888 and 565).
407 return flags.type != SinkFlags::kRaster || flags.approach != SinkFlags::kDirect;
408}
409
410// Allows us to test decodes to non-native 8888.
413 return;
414 }
415
416 for (int y = 0; y < bitmap.height(); y++) {
417 uint32_t* row = (uint32_t*) bitmap.getAddr(0, y);
418 SkOpts::RGBA_to_BGRA(row, row, bitmap.width());
419 }
420}
421
422static bool get_decode_info(SkImageInfo* decodeInfo, SkColorType canvasColorType,
423 CodecSrc::DstColorType dstColorType, SkAlphaType dstAlphaType) {
424 switch (dstColorType) {
426 if (kRGB_565_SkColorType == canvasColorType) {
427 return false;
428 }
429 *decodeInfo = decodeInfo->makeColorType(kGray_8_SkColorType);
430 break;
432 if (kRGB_565_SkColorType == canvasColorType
433 || kRGBA_F16_SkColorType == canvasColorType) {
434 return false;
435 }
436#ifdef SK_PMCOLOR_IS_RGBA
437 *decodeInfo = decodeInfo->makeColorType(kBGRA_8888_SkColorType);
438#else
439 *decodeInfo = decodeInfo->makeColorType(kRGBA_8888_SkColorType);
440#endif
441 break;
442 default:
443 if (kRGB_565_SkColorType == canvasColorType &&
444 kOpaque_SkAlphaType != decodeInfo->alphaType()) {
445 return false;
446 }
447
448 *decodeInfo = decodeInfo->makeColorType(canvasColorType);
449 break;
450 }
451
452 *decodeInfo = decodeInfo->makeAlphaType(dstAlphaType);
453 return true;
454}
455
456static void draw_to_canvas(SkCanvas* canvas, const SkImageInfo& info, void* pixels, size_t rowBytes,
457 CodecSrc::DstColorType dstColorType,
458 SkScalar left = 0, SkScalar top = 0) {
460 bitmap.installPixels(info, pixels, rowBytes);
461 swap_rb_if_necessary(bitmap, dstColorType);
462 canvas->drawImage(bitmap.asImage(), left, top);
463}
464
465// For codec srcs, we want the "draw" step to be a memcpy. Any interesting color space or
466// color format conversions should be performed by the codec. Sometimes the output of the
467// decode will be in an interesting color space. On our srgb and f16 backends, we need to
468// "pretend" that the color space is standard sRGB to avoid triggering color conversion
469// at draw time.
471 *info = info->makeColorSpace(SkColorSpace::MakeSRGB());
472}
473
476 if (!encoded) {
477 return Result::Fatal("Couldn't read %s.", fPath.c_str());
478 }
479
480 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(encoded));
481 if (nullptr == codec) {
482 return Result::Fatal("Couldn't create codec for %s.", fPath.c_str());
483 }
484
485 SkImageInfo decodeInfo = codec->getInfo();
486 if (!get_decode_info(&decodeInfo, canvas->imageInfo().colorType(), fDstColorType,
487 fDstAlphaType)) {
488 return Result::Skip("Skipping uninteresting test.");
489 }
490
491 // Try to scale the image if it is desired
492 SkISize size = codec->getScaledDimensions(fScale);
493
494 std::unique_ptr<SkAndroidCodec> androidCodec;
495 if (1.0f != fScale && fMode == kAnimated_Mode) {
496 androidCodec = SkAndroidCodec::MakeFromData(encoded);
497 size = androidCodec->getSampledDimensions(1 / fScale);
498 }
499
500 if (size == decodeInfo.dimensions() && 1.0f != fScale) {
501 return Result::Skip("Test without scaling is uninteresting.");
502 }
503
504 // Visually inspecting very small output images is not necessary. We will
505 // cover these cases in unit testing.
506 if ((size.width() <= 10 || size.height() <= 10) && 1.0f != fScale) {
507 return Result::Skip("Scaling very small images is uninteresting.");
508 }
509 decodeInfo = decodeInfo.makeDimensions(size);
510
511 const int bpp = decodeInfo.bytesPerPixel();
512 const size_t rowBytes = size.width() * bpp;
513 const size_t safeSize = decodeInfo.computeByteSize(rowBytes);
514 SkAutoMalloc pixels(safeSize);
515
517 if (kCodecZeroInit_Mode == fMode) {
518 memset(pixels.get(), 0, size.height() * rowBytes);
519 options.fZeroInitialized = SkCodec::kYes_ZeroInitialized;
520 }
521
522 SkImageInfo bitmapInfo = decodeInfo;
523 set_bitmap_color_space(&bitmapInfo);
524 if (kRGBA_8888_SkColorType == decodeInfo.colorType() ||
525 kBGRA_8888_SkColorType == decodeInfo.colorType()) {
526 bitmapInfo = bitmapInfo.makeColorType(kN32_SkColorType);
527 }
528
529 switch (fMode) {
530 case kAnimated_Mode: {
531 SkAndroidCodec::AndroidOptions androidOptions;
532 if (fScale != 1.0f) {
533 SkASSERT(androidCodec);
534 androidOptions.fSampleSize = 1 / fScale;
535 auto dims = androidCodec->getSampledDimensions(androidOptions.fSampleSize);
536 decodeInfo = decodeInfo.makeDimensions(dims);
537 }
538
539 std::vector<SkCodec::FrameInfo> frameInfos = androidCodec
540 ? androidCodec->codec()->getFrameInfo() : codec->getFrameInfo();
541 if (frameInfos.size() <= 1) {
542 return Result::Fatal("%s is not an animated image.", fPath.c_str());
543 }
544
545 // As in CodecSrc::size(), compute a roughly square grid to draw the frames
546 // into. "factor" is the number of frames to draw on one row. There will be
547 // up to "factor" rows as well.
548 const float root = sqrt((float) frameInfos.size());
549 const int factor = sk_float_ceil2int(root);
550
551 // Used to cache a frame that future frames will depend on.
552 SkAutoMalloc priorFramePixels;
553 int cachedFrame = SkCodec::kNoFrame;
554 for (int i = 0; static_cast<size_t>(i) < frameInfos.size(); i++) {
555 androidOptions.fFrameIndex = i;
556 // Check for a prior frame
557 const int reqFrame = frameInfos[i].fRequiredFrame;
558 if (reqFrame != SkCodec::kNoFrame && reqFrame == cachedFrame
559 && priorFramePixels.get()) {
560 // Copy into pixels
561 memcpy(pixels.get(), priorFramePixels.get(), safeSize);
562 androidOptions.fPriorFrame = reqFrame;
563 } else {
564 androidOptions.fPriorFrame = SkCodec::kNoFrame;
565 }
566 SkCodec::Result result = androidCodec
567 ? androidCodec->getAndroidPixels(decodeInfo, pixels.get(), rowBytes,
568 &androidOptions)
569 : codec->getPixels(decodeInfo, pixels.get(), rowBytes, &androidOptions);
570 if (SkCodec::kInvalidInput == result && i > 0) {
571 // Some of our test images have truncated later frames. Treat that
572 // the same as incomplete.
574 }
575 switch (result) {
579 // If the next frame depends on this one, store it in priorFrame.
580 // It is possible that we may discard a frame that future frames depend on,
581 // but the codec will simply redecode the discarded frame.
582 // Do this before calling draw_to_canvas, which premultiplies in place. If
583 // we're decoding to unpremul, we want to pass the unmodified frame to the
584 // codec for decoding the next frame.
585 if (static_cast<size_t>(i+1) < frameInfos.size()
586 && frameInfos[i+1].fRequiredFrame == i) {
587 memcpy(priorFramePixels.reset(safeSize), pixels.get(), safeSize);
588 cachedFrame = i;
589 }
590
591 SkAutoCanvasRestore acr(canvas, true);
592 const int xTranslate = (i % factor) * decodeInfo.width();
593 const int yTranslate = (i / factor) * decodeInfo.height();
594 canvas->translate(SkIntToScalar(xTranslate), SkIntToScalar(yTranslate));
595 draw_to_canvas(canvas, bitmapInfo, pixels.get(), rowBytes, fDstColorType);
596 if (result != SkCodec::kSuccess) {
597 return Result::Ok();
598 }
599 break;
600 }
602 if (i > 0 && (decodeInfo.colorType() == kRGB_565_SkColorType)) {
603 return Result::Skip(
604 "Cannot decode frame %i to 565 (%s).", i, fPath.c_str());
605 }
606 [[fallthrough]];
607 default:
608 return Result::Fatal(
609 "Couldn't getPixels for frame %i in %s.", i, fPath.c_str());
610 }
611 }
612 break;
613 }
615 case kCodec_Mode: {
616 switch (codec->getPixels(decodeInfo, pixels.get(), rowBytes, &options)) {
618 // We consider these to be valid, since we should still decode what is
619 // available.
622 break;
623 default:
624 // Everything else is considered a failure.
625 return Result::Fatal("Couldn't getPixels %s.", fPath.c_str());
626 }
627
628 draw_to_canvas(canvas, bitmapInfo, pixels.get(), rowBytes, fDstColorType);
629 break;
630 }
631 case kScanline_Mode: {
632 void* dst = pixels.get();
633 uint32_t height = decodeInfo.height();
634 const bool useIncremental = [this]() {
635 auto exts = { "png", "PNG", "gif", "GIF" };
636 for (auto ext : exts) {
637 if (fPath.endsWith(ext)) {
638 return true;
639 }
640 }
641 return false;
642 }();
643 // ico may use the old scanline method or the new one, depending on whether it
644 // internally holds a bmp or a png.
645 const bool ico = fPath.endsWith("ico");
646 bool useOldScanlineMethod = !useIncremental && !ico;
647 if (useIncremental || ico) {
648 if (SkCodec::kSuccess == codec->startIncrementalDecode(decodeInfo, dst,
649 rowBytes, &options)) {
650 int rowsDecoded;
651 auto result = codec->incrementalDecode(&rowsDecoded);
653 codec->fillIncompleteImage(decodeInfo, dst, rowBytes,
655 rowsDecoded);
656 }
657 } else {
658 if (useIncremental) {
659 // Error: These should support incremental decode.
660 return Result::Fatal("Could not start incremental decode");
661 }
662 // Otherwise, this is an ICO. Since incremental failed, it must contain a BMP,
663 // which should work via startScanlineDecode
664 useOldScanlineMethod = true;
665 }
666 }
667
668 if (useOldScanlineMethod) {
669 if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo)) {
670 return Result::Fatal("Could not start scanline decoder");
671 }
672
673 // We do not need to check the return value. On an incomplete
674 // image, memory will be filled with a default value.
675 codec->getScanlines(dst, height, rowBytes);
676 }
677
678 draw_to_canvas(canvas, bitmapInfo, dst, rowBytes, fDstColorType);
679 break;
680 }
681 case kStripe_Mode: {
682 const int height = decodeInfo.height();
683 // This value is chosen arbitrarily. We exercise more cases by choosing a value that
684 // does not align with image blocks.
685 const int stripeHeight = 37;
686 const int numStripes = (height + stripeHeight - 1) / stripeHeight;
687 void* dst = pixels.get();
688
689 // Decode odd stripes
690 if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo, &options)) {
691 return Result::Fatal("Could not start scanline decoder");
692 }
693
694 // This mode was designed to test the new skip scanlines API in libjpeg-turbo.
695 // Jpegs have kTopDown_SkScanlineOrder, and at this time, it is not interesting
696 // to run this test for image types that do not have this scanline ordering.
697 // We only run this on Jpeg, which is always kTopDown.
698 SkASSERT(SkCodec::kTopDown_SkScanlineOrder == codec->getScanlineOrder());
699
700 for (int i = 0; i < numStripes; i += 2) {
701 // Skip a stripe
702 const int linesToSkip = std::min(stripeHeight, height - i * stripeHeight);
703 codec->skipScanlines(linesToSkip);
704
705 // Read a stripe
706 const int startY = (i + 1) * stripeHeight;
707 const int linesToRead = std::min(stripeHeight, height - startY);
708 if (linesToRead > 0) {
709 codec->getScanlines(SkTAddOffset<void>(dst, rowBytes * startY), linesToRead,
710 rowBytes);
711 }
712 }
713
714 // Decode even stripes
715 const SkCodec::Result startResult = codec->startScanlineDecode(decodeInfo);
716 if (SkCodec::kSuccess != startResult) {
717 return Result::Fatal("Failed to restart scanline decoder with same parameters.");
718 }
719 for (int i = 0; i < numStripes; i += 2) {
720 // Read a stripe
721 const int startY = i * stripeHeight;
722 const int linesToRead = std::min(stripeHeight, height - startY);
723 codec->getScanlines(SkTAddOffset<void>(dst, rowBytes * startY), linesToRead,
724 rowBytes);
725
726 // Skip a stripe
727 const int linesToSkip = std::min(stripeHeight, height - (i + 1) * stripeHeight);
728 if (linesToSkip > 0) {
729 codec->skipScanlines(linesToSkip);
730 }
731 }
732
733 draw_to_canvas(canvas, bitmapInfo, dst, rowBytes, fDstColorType);
734 break;
735 }
737 const int width = decodeInfo.width();
738 const int height = decodeInfo.height();
739 // This value is chosen because, as we move across the image, it will sometimes
740 // align with the jpeg block sizes and it will sometimes not. This allows us
741 // to test interestingly different code paths in the implementation.
742 const int tileSize = 36;
743 SkIRect subset;
744 for (int x = 0; x < width; x += tileSize) {
745 subset = SkIRect::MakeXYWH(x, 0, std::min(tileSize, width - x), height);
746 options.fSubset = &subset;
747 if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo, &options)) {
748 return Result::Fatal("Could not start scanline decoder.");
749 }
750
751 codec->getScanlines(SkTAddOffset<void>(pixels.get(), x * bpp), height, rowBytes);
752 }
753
754 draw_to_canvas(canvas, bitmapInfo, pixels.get(), rowBytes, fDstColorType);
755 break;
756 }
757 case kSubset_Mode: {
758 // Arbitrarily choose a divisor.
759 int divisor = 2;
760 // Total width/height of the image.
761 const int W = codec->getInfo().width();
762 const int H = codec->getInfo().height();
763 if (divisor > W || divisor > H) {
764 return Result::Skip("Cannot codec subset: divisor %d is too big "
765 "for %s with dimensions (%d x %d)", divisor,
766 fPath.c_str(), W, H);
767 }
768 // subset dimensions
769 // SkWebpCodec, the only one that supports subsets, requires even top/left boundaries.
770 const int w = SkAlign2(W / divisor);
771 const int h = SkAlign2(H / divisor);
772 SkIRect subset;
773 options.fSubset = &subset;
774 SkBitmap subsetBm;
775 // We will reuse pixel memory from bitmap.
776 void* dst = pixels.get();
777 // Keep track of left and top (for drawing subsetBm into canvas). We could use
778 // fScale * x and fScale * y, but we want integers such that the next subset will start
779 // where the last one ended. So we'll add decodeInfo.width() and height().
780 int left = 0;
781 for (int x = 0; x < W; x += w) {
782 int top = 0;
783 for (int y = 0; y < H; y+= h) {
784 // Do not make the subset go off the edge of the image.
785 const int preScaleW = std::min(w, W - x);
786 const int preScaleH = std::min(h, H - y);
787 subset.setXYWH(x, y, preScaleW, preScaleH);
788 // And scale
789 // FIXME: Should we have a version of getScaledDimensions that takes a subset
790 // into account?
791 const int scaledW = std::max(1, SkScalarRoundToInt(preScaleW * fScale));
792 const int scaledH = std::max(1, SkScalarRoundToInt(preScaleH * fScale));
793 decodeInfo = decodeInfo.makeWH(scaledW, scaledH);
794 SkImageInfo subsetBitmapInfo = bitmapInfo.makeWH(scaledW, scaledH);
795 size_t subsetRowBytes = subsetBitmapInfo.minRowBytes();
796 const SkCodec::Result result = codec->getPixels(decodeInfo, dst, subsetRowBytes,
797 &options);
798 switch (result) {
802 break;
803 default:
804 return Result::Fatal("subset codec failed to decode (%d, %d, %d, %d) "
805 "from %s with dimensions (%d x %d)\t error %d",
806 x, y, decodeInfo.width(), decodeInfo.height(),
807 fPath.c_str(), W, H, result);
808 }
809 draw_to_canvas(canvas, subsetBitmapInfo, dst, subsetRowBytes, fDstColorType,
811
812 // translate by the scaled height.
813 top += decodeInfo.height();
814 }
815 // translate by the scaled width.
816 left += decodeInfo.width();
817 }
818 return Result::Ok();
819 }
820 default:
821 SkASSERT(false);
822 return Result::Fatal("Invalid fMode");
823 }
824 return Result::Ok();
825}
826
829 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(encoded));
830 if (nullptr == codec) {
831 return {0, 0};
832 }
833
834 if (fMode != kAnimated_Mode) {
835 return codec->getScaledDimensions(fScale);
836 }
837
838 // We'll draw one of each frame, so make it big enough to hold them all
839 // in a grid. The grid will be roughly square, with "factor" frames per
840 // row and up to "factor" rows.
841 const size_t count = codec->getFrameInfo().size();
842 const float root = sqrt((float) count);
843 const int factor = sk_float_ceil2int(root);
844
845 auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec));
846 auto imageSize = androidCodec->getSampledDimensions(1 / fScale);
847 imageSize.fWidth = imageSize.fWidth * factor;
848 imageSize.fHeight = imageSize.fHeight * sk_float_ceil2int((float) count / (float) factor);
849 return imageSize;
850}
851
854 if (fMode == kAnimated_Mode) {
855 name.append("_animated");
856 }
857 if (1.0f == fScale) {
858 return name;
859 }
860 return get_scaled_name(name.c_str(), fScale);
861}
862
863/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
864
866 SkAlphaType dstAlphaType, int sampleSize)
867 : fPath(path)
868 , fDstColorType(dstColorType)
869 , fDstAlphaType(dstAlphaType)
870 , fSampleSize(sampleSize)
871 , fRunSerially(serial_from_path_name(path))
872{}
873
875 // No need to test decoding to non-raster or indirect backend.
876 return flags.type != SinkFlags::kRaster
877 || flags.approach != SinkFlags::kDirect;
878}
879
882 if (!encoded) {
883 return Result::Fatal("Couldn't read %s.", fPath.c_str());
884 }
885 std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::MakeFromData(encoded));
886 if (nullptr == codec) {
887 return Result::Fatal("Couldn't create android codec for %s.", fPath.c_str());
888 }
889
890 SkImageInfo decodeInfo = codec->getInfo();
891 if (!get_decode_info(&decodeInfo, canvas->imageInfo().colorType(), fDstColorType,
892 fDstAlphaType)) {
893 return Result::Skip("Skipping uninteresting test.");
894 }
895
896 // Scale the image if it is desired.
897 SkISize size = codec->getSampledDimensions(fSampleSize);
898
899 // Visually inspecting very small output images is not necessary. We will
900 // cover these cases in unit testing.
901 if ((size.width() <= 10 || size.height() <= 10) && 1 != fSampleSize) {
902 return Result::Skip("Scaling very small images is uninteresting.");
903 }
904 decodeInfo = decodeInfo.makeDimensions(size);
905
906 int bpp = decodeInfo.bytesPerPixel();
907 size_t rowBytes = size.width() * bpp;
908 SkAutoMalloc pixels(size.height() * rowBytes);
909
911 SkImageInfo bitmapInfo = decodeInfo;
912 set_bitmap_color_space(&bitmapInfo);
913 if (kRGBA_8888_SkColorType == decodeInfo.colorType() ||
914 kBGRA_8888_SkColorType == decodeInfo.colorType()) {
915 bitmapInfo = bitmapInfo.makeColorType(kN32_SkColorType);
916 }
917
918 // Create options for the codec.
920 options.fSampleSize = fSampleSize;
921
922 switch (codec->getAndroidPixels(decodeInfo, pixels.get(), rowBytes, &options)) {
926 break;
927 default:
928 return Result::Fatal("Couldn't getPixels %s.", fPath.c_str());
929 }
930 draw_to_canvas(canvas, bitmapInfo, pixels.get(), rowBytes, fDstColorType);
931 return Result::Ok();
932}
933
936 std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::MakeFromData(encoded));
937 if (nullptr == codec) {
938 return {0, 0};
939 }
940 return codec->getSampledDimensions(fSampleSize);
941}
942
944 // We will replicate the names used by CodecSrc so that images can
945 // be compared in Gold.
946 if (1 == fSampleSize) {
947 return SkOSPath::Basename(fPath.c_str());
948 }
949 return get_scaled_name(fPath, 1.0f / (float) fSampleSize);
950}
951
952/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
953
954ImageGenSrc::ImageGenSrc(Path path, Mode mode, SkAlphaType alphaType, bool isGpu)
955 : fPath(path)
956 , fMode(mode)
957 , fDstAlphaType(alphaType)
958 , fIsGpu(isGpu)
959 , fRunSerially(serial_from_path_name(path))
960{}
961
963 if (fIsGpu) {
964 // MSAA runs tend to run out of memory and tests the same code paths as regular gpu configs.
965 return flags.type != SinkFlags::kGPU || flags.approach != SinkFlags::kDirect ||
966 flags.multisampled == SinkFlags::kMultisampled;
967 }
968
969 return flags.type != SinkFlags::kRaster || flags.approach != SinkFlags::kDirect;
970}
971
973 if (kRGB_565_SkColorType == canvas->imageInfo().colorType()) {
974 return Result::Skip("Uninteresting to test image generator to 565.");
975 }
976
978 if (!encoded) {
979 return Result::Fatal("Couldn't read %s.", fPath.c_str());
980 }
981
982#if defined(SK_BUILD_FOR_WIN)
983 // Initialize COM in order to test with WIC.
984 SkAutoCoInitialize com;
985 if (!com.succeeded()) {
986 return Result::Fatal("Could not initialize COM.");
987 }
988#endif
989
990 std::unique_ptr<SkImageGenerator> gen(nullptr);
991 switch (fMode) {
992 case kCodec_Mode:
994 if (!gen) {
995 return Result::Fatal("Could not create codec image generator.");
996 }
997 break;
998 case kPlatform_Mode: {
999#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
1000 gen = SkImageGeneratorCG::MakeFromEncodedCG(encoded);
1001#elif defined(SK_BUILD_FOR_WIN)
1002 gen = SkImageGeneratorWIC::MakeFromEncodedWIC(encoded);
1003#elif defined(SK_ENABLE_NDK_IMAGES)
1004 gen = SkImageGeneratorNDK::MakeFromEncodedNDK(encoded);
1005#endif
1006 if (!gen) {
1007 return Result::Fatal("Could not create platform image generator.");
1008 }
1009 break;
1010 }
1011 default:
1012 SkASSERT(false);
1013 return Result::Fatal("Invalid image generator mode");
1014 }
1015
1016 // Test deferred decoding path on GPU
1017 if (fIsGpu) {
1019 if (!image) {
1020 return Result::Fatal("Could not create image from codec image generator.");
1021 }
1022 canvas->drawImage(image, 0, 0);
1023 return Result::Ok();
1024 }
1025
1026 // Test various color and alpha types on CPU
1027 SkImageInfo decodeInfo = gen->getInfo().makeAlphaType(fDstAlphaType);
1028
1029 int bpp = decodeInfo.bytesPerPixel();
1030 size_t rowBytes = decodeInfo.width() * bpp;
1031 SkAutoMalloc pixels(decodeInfo.height() * rowBytes);
1032 if (!gen->getPixels(decodeInfo, pixels.get(), rowBytes)) {
1034#if defined(SK_BUILD_FOR_WIN)
1035 if (kPlatform_Mode == fMode) {
1036 // Do not issue a fatal error for WIC flakiness.
1037 status = Result::Status::Skip;
1038 }
1039#endif
1040 return Result(
1041 status,
1042 SkStringPrintf("Image generator could not getPixels() for %s\n", fPath.c_str()));
1043 }
1044
1045 set_bitmap_color_space(&decodeInfo);
1046 draw_to_canvas(canvas, decodeInfo, pixels.get(), rowBytes,
1048 return Result::Ok();
1049}
1050
1053 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(encoded));
1054 if (nullptr == codec) {
1055 return {0, 0};
1056 }
1057 return codec->getInfo().dimensions();
1058}
1059
1061 return SkOSPath::Basename(fPath.c_str());
1062}
1063
1064/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1065
1066ColorCodecSrc::ColorCodecSrc(Path path, bool decode_to_dst) : fPath(path)
1067 , fDecodeToDst(decode_to_dst) {}
1068
1070 // Test to direct raster backends (8888 and 565).
1071 return flags.type != SinkFlags::kRaster || flags.approach != SinkFlags::kDirect;
1072}
1073
1076 if (!encoded) {
1077 return Result::Fatal("Couldn't read %s.", fPath.c_str());
1078 }
1079
1080 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(encoded));
1081 if (nullptr == codec) {
1082 return Result::Fatal("Couldn't create codec for %s.", fPath.c_str());
1083 }
1084
1085 SkImageInfo info = codec->getInfo();
1086 if (SkEncodedOriginSwapsWidthHeight(codec->getOrigin())) {
1088 }
1089 if (fDecodeToDst) {
1090 SkImageInfo canvasInfo = canvas->imageInfo();
1091 if (!canvasInfo.colorSpace()) {
1092 // This will skip color conversion, and the resulting images will
1093 // look different from images they are compared against in Gold, but
1094 // that doesn't mean they are wrong. We have a test verifying that
1095 // passing a null SkColorSpace skips conversion, so skip this
1096 // misleading test.
1097 return Result::Skip("Skipping decoding without color transform.");
1098 }
1099 info = canvasInfo.makeDimensions(info.dimensions());
1100 }
1101
1102 auto [image, result] = codec->getImage(info);
1103 switch (result) {
1104 case SkCodec::kSuccess:
1107 canvas->drawImage(image, 0,0);
1108 return Result::Ok();
1110 // TODO(mtklein): why are there formats we can't decode to?
1111 return Result::Skip("SkCodec can't decode to this format.");
1112 default:
1113 return Result::Fatal("Couldn't getPixels %s. Error code %d", fPath.c_str(), result);
1114 }
1115}
1116
1119 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(encoded));
1120 if (nullptr == codec) {
1121 return {0, 0};
1122 }
1123 return {codec->getInfo().width(), codec->getInfo().height()};
1124}
1125
1127 return SkOSPath::Basename(fPath.c_str());
1128}
1129
1130/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1131
1132static DEFINE_int(skpViewportSize, 1000,
1133 "Width & height of the viewport used to crop skp rendering.");
1134
1135SKPSrc::SKPSrc(Path path) : fPath(path) { }
1136
1138 struct DeserializationContext {
1139 GrDirectContext* fDirectContext = nullptr;
1140#if defined(SK_GRAPHITE)
1141 skgpu::graphite::Recorder* fRecorder = nullptr;
1142#endif
1143 } ctx {
1145#if defined(SK_GRAPHITE)
1146 canvas->recorder()
1147#endif
1148 };
1149
1150 SkDeserialProcs procs;
1151 procs.fImageProc = [](const void* data, size_t size, void* ctx) -> sk_sp<SkImage> {
1154 image = image->makeRasterImage(); // force decoding
1155
1156 if (image) {
1157 DeserializationContext* context = reinterpret_cast<DeserializationContext*>(ctx);
1158
1159 if (context->fDirectContext) {
1160 return SkImages::TextureFromImage(context->fDirectContext, image);
1161 }
1162 }
1163 return image;
1164 };
1165 procs.fImageCtx = &ctx;
1166
1167 // SKPs may have typefaces encoded in them (e.g. with FreeType). We can try falling back
1168 // to the Test FontMgr (possibly a native one) if we have do not have FreeType built-in.
1169 procs.fTypefaceProc = [](const void* data, size_t size, void*) -> sk_sp<SkTypeface> {
1170 SkStream** stream = reinterpret_cast<SkStream**>(const_cast<void*>(data));
1172 };
1173
1174
1175 std::unique_ptr<SkStream> stream = SkStream::MakeFromFile(fPath.c_str());
1176 if (!stream) {
1177 return Result::Fatal("Couldn't read %s.", fPath.c_str());
1178 }
1179 sk_sp<SkPicture> pic(SkPicture::MakeFromStream(stream.get(), &procs));
1180 if (!pic) {
1181 return Result::Fatal("Couldn't parse file %s.", fPath.c_str());
1182 }
1183 stream = nullptr; // Might as well drop this when we're done with it.
1184 canvas->clipRect(SkRect::MakeWH(FLAGS_skpViewportSize, FLAGS_skpViewportSize));
1185 canvas->drawPicture(pic);
1186 return Result::Ok();
1187}
1188
1189static SkRect get_cull_rect_for_skp(const char* path) {
1190 std::unique_ptr<SkStream> stream = SkStream::MakeFromFile(path);
1191 if (!stream) {
1192 return SkRect::MakeEmpty();
1193 }
1195 if (!SkPicture_StreamIsSKP(stream.get(), &info)) {
1196 return SkRect::MakeEmpty();
1197 }
1198
1199 return info.fCullRect;
1200}
1201
1203 SkRect viewport = get_cull_rect_for_skp(fPath.c_str());
1204 if (!viewport.intersect((SkRect::MakeWH(FLAGS_skpViewportSize, FLAGS_skpViewportSize)))) {
1205 return {0, 0};
1206 }
1207 return viewport.roundOut().size();
1208}
1209
1210Name SKPSrc::name() const { return SkOSPath::Basename(fPath.c_str()); }
1211
1212/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1213
1214BisectSrc::BisectSrc(Path path, const char* trail) : INHERITED(path), fTrail(trail) {}
1215
1217 struct FoundPath {
1218 SkPath fPath;
1219 SkPaint fPaint;
1221 };
1222
1223 // This subclass of SkCanvas just extracts all the SkPaths (drawn via drawPath) from an SKP.
1224 class PathFindingCanvas : public SkCanvas {
1225 public:
1226 PathFindingCanvas(int width, int height) : SkCanvas(width, height, nullptr) {}
1227 const TArray<FoundPath>& foundPaths() const { return fFoundPaths; }
1228
1229 private:
1230 void onDrawPath(const SkPath& path, const SkPaint& paint) override {
1231 fFoundPaths.push_back() = {path, paint, this->getTotalMatrix()};
1232 }
1233
1234 TArray<FoundPath> fFoundPaths;
1235 };
1236
1237 PathFindingCanvas pathFinder(canvas->getBaseLayerSize().width(),
1238 canvas->getBaseLayerSize().height());
1239 Result result = this->INHERITED::draw(&pathFinder, testContext);
1240 if (!result.isOk()) {
1241 return result;
1242 }
1243
1244 int start = 0, end = pathFinder.foundPaths().size();
1245 for (const char* ch = fTrail.c_str(); *ch; ++ch) {
1246 int midpt = (start + end) / 2;
1247 if ('l' == *ch) {
1248 start = midpt;
1249 } else if ('r' == *ch) {
1250 end = midpt;
1251 }
1252 }
1253
1254 for (int i = start; i < end; ++i) {
1255 const FoundPath& path = pathFinder.foundPaths()[i];
1256 SkAutoCanvasRestore acr(canvas, true);
1257 canvas->concat(path.fViewMatrix);
1258 canvas->drawPath(path.fPath, path.fPaint);
1259 }
1260
1261 return Result::Ok();
1262}
1263
1264/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1265
1266#if defined(SK_ENABLE_SKOTTIE)
1267static DEFINE_bool(useLottieGlyphPaths, false,
1268 "Prioritize embedded glyph paths over native fonts.");
1269
1270SkottieSrc::SkottieSrc(Path path) : fPath(std::move(path)) {}
1271
1272Result SkottieSrc::draw(SkCanvas* canvas, GraphiteTestContext*) const {
1274 // DM should have already registered the codecs necessary for DataURIResourceProviderProxy
1275 // to decode images.
1276 auto resource_provider = skresources::DataURIResourceProviderProxy::Make(
1278 predecode,
1280
1281 static constexpr char kInterceptPrefix[] = "__";
1282 auto precomp_interceptor =
1283 sk_make_sp<skottie_utils::ExternalAnimationPrecompInterceptor>(resource_provider,
1284 kInterceptPrefix);
1285 uint32_t flags = 0;
1286 if (FLAGS_useLottieGlyphPaths) {
1288 }
1289
1290 auto animation = skottie::Animation::Builder(flags)
1292 .setResourceProvider(std::move(resource_provider))
1293 .setPrecompInterceptor(std::move(precomp_interceptor))
1295 .makeFromFile(fPath.c_str());
1296 if (!animation) {
1297 return Result::Fatal("Unable to parse file: %s", fPath.c_str());
1298 }
1299
1300 canvas->drawColor(SK_ColorWHITE);
1301
1302 const auto t_rate = 1.0f / (kTileCount * kTileCount - 1);
1303
1304 // Draw the frames in a shuffled order to exercise non-linear
1305 // frame progression. The film strip will still be in order left-to-right,
1306 // top-down, just not drawn in that order.
1307 static constexpr int frameOrder[] = { 4, 0, 3, 1, 2 };
1308 static_assert(std::size(frameOrder) == kTileCount, "");
1309
1310 for (int i = 0; i < kTileCount; ++i) {
1311 const SkScalar y = frameOrder[i] * kTileSize;
1312
1313 for (int j = 0; j < kTileCount; ++j) {
1314 const SkScalar x = frameOrder[j] * kTileSize;
1315 SkRect dest = SkRect::MakeXYWH(x, y, kTileSize, kTileSize);
1316
1317 const auto t = t_rate * (frameOrder[i] * kTileCount + frameOrder[j]);
1318 {
1319 SkAutoCanvasRestore acr(canvas, true);
1320 canvas->clipRect(dest, true);
1321 canvas->concat(SkMatrix::RectToRect(SkRect::MakeSize(animation->size()), dest,
1323 animation->seek(t);
1324 animation->render(canvas);
1325 }
1326 }
1327 }
1328
1329 return Result::Ok();
1330}
1331
1332SkISize SkottieSrc::size() const {
1333 return SkISize::Make(kTargetSize, kTargetSize);
1334}
1335
1336Name SkottieSrc::name() const { return SkOSPath::Basename(fPath.c_str()); }
1337
1338bool SkottieSrc::veto(SinkFlags flags) const {
1339 // No need to test to non-(raster||gpu||vector) or indirect backends.
1340 bool type_ok = flags.type == SinkFlags::kRaster
1341 || flags.type == SinkFlags::kGPU
1342 || flags.type == SinkFlags::kVector;
1343
1344 return !type_ok || flags.approach != SinkFlags::kDirect;
1345}
1346#endif
1347
1348/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1349#if defined(SK_ENABLE_SVG)
1350// Used when the image doesn't have an intrinsic size.
1351static const SkSize kDefaultSVGSize = {1000, 1000};
1352
1353// Used to force-scale tiny fixed-size images.
1354static const SkSize kMinimumSVGSize = {128, 128};
1355
1356SVGSrc::SVGSrc(Path path)
1357 : fName(SkOSPath::Basename(path.c_str()))
1358 , fScale(1) {
1359
1360 auto stream = SkStream::MakeFromFile(path.c_str());
1361 if (!stream) {
1362 return;
1363 }
1364
1365 // DM should have already registered the codecs necessary for DataURIResourceProviderProxy
1366 // to decode images.
1370 predecode,
1372
1373 fDom = SkSVGDOM::Builder()
1374 .setResourceProvider(std::move(rp))
1377 .make(*stream);
1378 if (!fDom) {
1379 return;
1380 }
1381
1382 const SkSize& sz = fDom->containerSize();
1383 if (sz.isEmpty()) {
1384 // no intrinsic size
1385 fDom->setContainerSize(kDefaultSVGSize);
1386 } else {
1387 fScale = std::max(1.f, std::max(kMinimumSVGSize.width() / sz.width(),
1388 kMinimumSVGSize.height() / sz.height()));
1389 }
1390}
1391
1392Result SVGSrc::draw(SkCanvas* canvas, GraphiteTestContext*) const {
1393 if (!fDom) {
1394 return Result::Fatal("Unable to parse file: %s", fName.c_str());
1395 }
1396
1397 SkAutoCanvasRestore acr(canvas, true);
1398 canvas->scale(fScale, fScale);
1399 canvas->drawColor(SK_ColorWHITE);
1400 fDom->render(canvas);
1401
1402 return Result::Ok();
1403}
1404
1405SkISize SVGSrc::size() const {
1406 if (!fDom) {
1407 return {0, 0};
1408 }
1409
1410 return SkSize{fDom->containerSize().width() * fScale, fDom->containerSize().height() * fScale}
1411 .toRound();
1412}
1413
1414Name SVGSrc::name() const { return fName; }
1415
1416bool SVGSrc::veto(SinkFlags flags) const {
1417 // No need to test to non-(raster||gpu||vector) or indirect backends.
1418 bool type_ok = flags.type == SinkFlags::kRaster
1419 || flags.type == SinkFlags::kGPU
1420 || flags.type == SinkFlags::kVector;
1421
1422 return !type_ok || flags.approach != SinkFlags::kDirect;
1423}
1424
1425#endif // defined(SK_ENABLE_SVG)
1426/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1427
1429 std::unique_ptr<SkStreamAsset> stream = SkStream::MakeFromFile(fPath.c_str());
1430 int count = SkMultiPictureDocument::ReadPageCount(stream.get());
1431 if (count > 0) {
1432 fPages.reset(count);
1434 fPages.size()));
1435 }
1436}
1437
1438int MSKPSrc::pageCount() const { return fPages.size(); }
1439
1440SkISize MSKPSrc::size() const { return this->size(FLAGS_mskpFrame); }
1442 return i >= 0 && i < fPages.size() ? fPages[i].fSize.toCeil() : SkISize{0, 0};
1443}
1444
1446 return this->draw(FLAGS_mskpFrame, c, testContext);
1447}
1449 if (this->pageCount() == 0) {
1450 return Result::Fatal("Unable to parse MultiPictureDocument file: %s", fPath.c_str());
1451 }
1452 if (i >= fPages.size() || i < 0) {
1453 return Result::Fatal("MultiPictureDocument page number out of range: %d", i);
1454 }
1455 SkPicture* page = fPages[i].fPicture.get();
1456 if (!page) {
1457 std::unique_ptr<SkStreamAsset> stream = SkStream::MakeFromFile(fPath.c_str());
1458 if (!stream) {
1459 return Result::Fatal("Unable to open file: %s", fPath.c_str());
1460 }
1461 if (!SkMultiPictureDocument::Read(stream.get(), &fPages[0], fPages.size())) {
1462 return Result::Fatal("SkMultiPictureDocument reader failed on page %d: %s", i,
1463 fPath.c_str());
1464 }
1465 page = fPages[i].fPicture.get();
1466 }
1467 canvas->drawPicture(page);
1468 return Result::Ok();
1469}
1470
1471Name MSKPSrc::name() const { return SkOSPath::Basename(fPath.c_str()); }
1472
1473/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1474
1476 return src.draw(SkMakeNullCanvas().get(), /*GraphiteTestContext=*/nullptr);
1477}
1478
1479/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1480
1481static Result compare_bitmaps(const SkBitmap& reference, const SkBitmap& bitmap) {
1482 // The dimensions are a property of the Src only, and so should be identical.
1483 SkASSERT(reference.computeByteSize() == bitmap.computeByteSize());
1484 if (reference.computeByteSize() != bitmap.computeByteSize()) {
1485 return Result::Fatal("Dimensions don't match reference");
1486 }
1487 // All SkBitmaps in DM are tight, so this comparison is easy.
1488 if (0 != memcmp(reference.getPixels(), bitmap.getPixels(), reference.computeByteSize())) {
1489 SkString encoded;
1490 SkString errString("Pixels don't match reference");
1491 if (ToolUtils::BitmapToBase64DataURI(reference, &encoded)) {
1492 errString.append("\nExpected: ");
1493 errString.append(encoded);
1494 } else {
1495 errString.append("\nExpected image failed to encode: ");
1496 errString.append(encoded);
1497 }
1499 errString.append("\nActual: ");
1500 errString.append(encoded);
1501 } else {
1502 errString.append("\nActual image failed to encode: ");
1503 errString.append(encoded);
1504 }
1505 return Result(Result::Status::Fatal, errString);
1506 }
1507 return Result::Ok();
1508}
1509
1510/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1511
1512static DEFINE_bool(gpuStats, false, "Append GPU stats to the log for each GPU task?");
1513static DEFINE_bool(preAbandonGpuContext, false,
1514 "Test abandoning the GrContext before running the test.");
1515static DEFINE_bool(abandonGpuContext, false,
1516 "Test abandoning the GrContext after running each test.");
1517static DEFINE_bool(releaseAndAbandonGpuContext, false,
1518 "Test releasing all gpu resources and abandoning the GrContext "
1519 "after running each test");
1520static DEFINE_bool(drawOpClip, false, "Clip each GrDrawOp to its device bounds for testing.");
1521static DEFINE_bool(programBinaryCache, true, "Use in-memory program binary cache");
1522
1524 const GrContextOptions& grCtxOptions)
1525 : fContextType(config->getContextType())
1526 , fContextOverrides(config->getContextOverrides())
1527 , fSurfType(config->getSurfType())
1528 , fSampleCount(config->getSamples())
1529 , fSurfaceFlags(config->getSurfaceFlags())
1530 , fColorType(config->getColorType())
1531 , fAlphaType(config->getAlphaType())
1532 , fBaseContextOptions(grCtxOptions) {
1533 if (FLAGS_programBinaryCache) {
1534 fBaseContextOptions.fPersistentCache = &fMemoryCache;
1535 }
1536}
1537
1538Result GPUSink::draw(const Src& src, SkBitmap* dst, SkWStream* dstStream, SkString* log) const {
1539 return this->onDraw(src, dst, dstStream, log, fBaseContextOptions);
1540}
1541
1544
1546 SkSurfaceProps props(fSurfaceFlags, kRGB_H_SkPixelGeometry);
1547
1548 switch (fSurfType) {
1551 context, skgpu::Budgeted::kNo, info, fSampleCount, &props);
1552 break;
1555 info,
1557 fSampleCount,
1558 skgpu::Mipmapped::kNo,
1559 GrProtected::kNo,
1560 &props);
1561 break;
1564 info,
1566 fSampleCount,
1567 GrProtected::kNo,
1568 &props);
1569 break;
1570 }
1571
1572 return surface;
1573}
1574
1576 SkCanvas* canvas = surface->getCanvas();
1577 SkISize size = surface->imageInfo().dimensions();
1578
1580 dst->allocPixels(info);
1581 return canvas->readPixels(*dst, 0, 0);
1582}
1583
1585 const GrContextOptions& baseOptions,
1586 std::function<void(GrDirectContext*)> initContext,
1587 std::function<SkCanvas*(SkCanvas*)> wrapCanvas) const {
1588 GrContextOptions grOptions = baseOptions;
1589
1590 // We don't expect the src to mess with the persistent cache or the executor.
1591 SkDEBUGCODE(auto cache = grOptions.fPersistentCache);
1592 SkDEBUGCODE(auto exec = grOptions.fExecutor);
1593 src.modifyGrContextOptions(&grOptions);
1594 SkASSERT(cache == grOptions.fPersistentCache);
1595 SkASSERT(exec == grOptions.fExecutor);
1596
1597 GrContextFactory factory(grOptions);
1598 auto direct = factory.getContextInfo(fContextType, fContextOverrides).directContext();
1599 if (initContext) {
1600 initContext(direct);
1601 }
1602
1603 const int maxDimension = direct->priv().caps()->maxTextureSize();
1604 if (maxDimension < std::max(src.size().width(), src.size().height())) {
1605 return Result::Skip("Src too large to create a texture.\n");
1606 }
1607
1608 sk_sp<SkSurface> surface = this->createDstSurface(direct, src.size());
1609 if (!surface) {
1610 return Result::Fatal("Could not create a surface.");
1611 }
1612 if (FLAGS_preAbandonGpuContext) {
1613 factory.abandonContexts();
1614 }
1615
1616 auto canvas = surface->getCanvas();
1617 if (wrapCanvas != nullptr) {
1618 canvas = wrapCanvas(canvas);
1619 }
1620
1621 Result result = src.draw(canvas, /*GraphiteTestContext=*/nullptr);
1622 if (!result.isOk()) {
1623 return result;
1624 }
1625 direct->flushAndSubmit(surface.get(), GrSyncCpu::kNo);
1626 if (FLAGS_gpuStats) {
1627 direct->priv().dumpCacheStats(log);
1628 direct->priv().dumpGpuStats(log);
1629 direct->priv().dumpContextStats(log);
1630 }
1631
1632 this->readBack(surface.get(), dst);
1633
1634 if (FLAGS_abandonGpuContext) {
1635 factory.abandonContexts();
1636 } else if (FLAGS_releaseAndAbandonGpuContext) {
1638 }
1639
1640 if (grOptions.fPersistentCache) {
1641 direct->storeVkPipelineCacheData();
1642 }
1643 return Result::Ok();
1644}
1645
1646/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1649
1651 GrContextOptions grOptions = this->baseContextOptions();
1652 // Force padded atlas entries for slug drawing.
1653 grOptions.fSupportBilerpFromGlyphAtlas |= true;
1654
1656
1657 return onDraw(src, dst, write, log, grOptions, nullptr,
1658 [&](SkCanvas* canvas){
1659 testCanvas.init(canvas);
1660 return testCanvas.get();
1661 });
1662}
1663
1664/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1668
1670 const Src& src, SkBitmap* dst, SkWStream* write, SkString* log) const {
1671 GrContextOptions grOptions = this->baseContextOptions();
1672 // Force padded atlas entries for slug drawing.
1673 grOptions.fSupportBilerpFromGlyphAtlas |= true;
1674
1676
1677 return onDraw(src, dst, write, log, grOptions, nullptr,
1678 [&](SkCanvas* canvas){
1679 testCanvas.init(canvas);
1680 return testCanvas.get();
1681 });
1682}
1683
1684/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1688
1690 const Src& src, SkBitmap* dst, SkWStream* write, SkString* log) const {
1691 GrContextOptions grOptions = this->baseContextOptions();
1692 // Force padded atlas entries for slug drawing.
1693 grOptions.fSupportBilerpFromGlyphAtlas |= true;
1694
1696
1697 return onDraw(src, dst, write, log, grOptions, nullptr,
1698 [&](SkCanvas* canvas) {
1699 testCanvas.init(canvas);
1700 return testCanvas.get();
1701 });
1702}
1703
1704/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1706 const GrContextOptions& grCtxOptions)
1707 : INHERITED(config, grCtxOptions)
1708 , fCacheType(config->getTestPersistentCache()) {}
1709
1711 SkString* log) const {
1712 // Draw twice, once with a cold cache, and again with a warm cache. Verify that we get the same
1713 // result.
1714 sk_gpu_test::MemoryCache memoryCache;
1715 GrContextOptions contextOptions = this->baseContextOptions();
1716 contextOptions.fPersistentCache = &memoryCache;
1717 if (fCacheType == 2) {
1719 }
1720
1721 Result result = this->onDraw(src, dst, wStream, log, contextOptions);
1722 if (!result.isOk() || !dst) {
1723 return result;
1724 }
1725
1726 SkBitmap reference;
1727 SkString refLog;
1728 SkDynamicMemoryWStream refStream;
1729 memoryCache.resetCacheStats();
1730 Result refResult = this->onDraw(src, &reference, &refStream, &refLog, contextOptions);
1731 if (!refResult.isOk()) {
1732 return refResult;
1733 }
1734 SkASSERT(!memoryCache.numCacheMisses());
1735 SkASSERT(!memoryCache.numCacheStores());
1736
1737 return compare_bitmaps(reference, *dst);
1738}
1739
1740
1741/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1742
1744 const GrContextOptions& grCtxOptions)
1745 : INHERITED(config, grCtxOptions) {}
1746
1748 SkString* log) const {
1749 // Three step process:
1750 // 1) Draw once with an SkSL cache, and store off the shader blobs.
1751 // 2) For the second context, pre-compile the shaders to warm the cache.
1752 // 3) Draw with the second context, ensuring that we get the same result, and no cache misses.
1753 sk_gpu_test::MemoryCache memoryCache;
1754 GrContextOptions contextOptions = this->baseContextOptions();
1755 contextOptions.fPersistentCache = &memoryCache;
1757
1758 Result result = this->onDraw(src, dst, wStream, log, contextOptions);
1759 if (!result.isOk() || !dst) {
1760 return result;
1761 }
1762
1763 auto precompileShaders = [&memoryCache](GrDirectContext* dContext) {
1764 memoryCache.foreach([dContext](sk_sp<const SkData> key,
1765 sk_sp<SkData> data,
1766 const SkString& /*description*/,
1767 int /*count*/) {
1768 SkAssertResult(dContext->precompileShader(*key, *data));
1769 });
1770 };
1771
1772 sk_gpu_test::MemoryCache replayCache;
1773 GrContextOptions replayOptions = this->baseContextOptions();
1774 // Ensure that the runtime cache is large enough to hold all of the shaders we pre-compile
1775 replayOptions.fRuntimeProgramCacheSize = memoryCache.numCacheMisses();
1776 replayOptions.fPersistentCache = &replayCache;
1777
1778 SkBitmap reference;
1779 SkString refLog;
1780 SkDynamicMemoryWStream refStream;
1781 Result refResult = this->onDraw(src, &reference, &refStream, &refLog, replayOptions,
1782 precompileShaders);
1783 if (!refResult.isOk()) {
1784 return refResult;
1785 }
1786 SkASSERT(!replayCache.numCacheMisses());
1787
1788 return compare_bitmaps(reference, *dst);
1789}
1790
1791/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1793 : INHERITED(config, ctxOptions)
1794 , fRecordingExecutor(SkExecutor::MakeLIFOThreadPool(1))
1795 , fGPUExecutor(SkExecutor::MakeFIFOThreadPool(1, false)) {
1796}
1797
1798Result GPUDDLSink::ddlDraw(const Src& src,
1799 sk_sp<SkSurface> dstSurface,
1800 SkTaskGroup* recordingTaskGroup,
1801 SkTaskGroup* gpuTaskGroup,
1802 sk_gpu_test::TestContext* gpuTestCtx,
1803 GrDirectContext* dContext) const {
1804
1805 // We have to do this here bc characterization can hit the SkGpuDevice's thread guard (i.e.,
1806 // leaving it until the DDLTileHelper ctor will result in multiple threads trying to use the
1807 // same context (this thread and the gpuThread - which will be uploading textures)).
1808 GrSurfaceCharacterization dstCharacterization;
1809 SkAssertResult(dstSurface->characterize(&dstCharacterization));
1810
1811 auto size = src.size();
1812 SkPictureRecorder recorder;
1813 Result result = src.draw(recorder.beginRecording(SkIntToScalar(size.width()),
1814 SkIntToScalar(size.height())),
1815 /*GraphiteTestContext=*/nullptr);
1816 if (!result.isOk()) {
1817 return result;
1818 }
1819 sk_sp<SkPicture> inputPicture(recorder.finishRecordingAsPicture());
1820
1821 // this is our ultimate final drawing area/rect
1822 SkIRect viewport = SkIRect::MakeWH(size.fWidth, size.fHeight);
1823
1824 auto supportedYUVADataTypes = skgpu::ganesh::SupportedTextureFormats(*dContext);
1825 DDLPromiseImageHelper promiseImageHelper(supportedYUVADataTypes);
1826 sk_sp<SkPicture> newSKP = promiseImageHelper.recreateSKP(dContext, inputPicture.get());
1827 if (!newSKP) {
1828 return Result::Fatal("GPUDDLSink: Couldn't recreate the SKP");
1829 }
1830
1831 // 'gpuTestCtx/gpuThreadCtx' is being shifted to the gpuThread. Leave the main (this)
1832 // thread w/o a context.
1833 gpuTestCtx->makeNotCurrent();
1834
1835 // Job one for the GPU thread is to make 'gpuTestCtx' current!
1836 gpuTaskGroup->add([gpuTestCtx] { gpuTestCtx->makeCurrent(); });
1837
1838 // TODO: move the image upload to the utility thread
1839 promiseImageHelper.uploadAllToGPU(gpuTaskGroup, dContext);
1840
1841 // Care must be taken when using 'gpuThreadCtx' bc it moves between the gpu-thread and this
1842 // one. About all it can be consistently used for is GrCaps access and 'defaultBackendFormat'
1843 // calls.
1844 constexpr int kNumDivisions = 3;
1845 DDLTileHelper tiles(dContext, dstCharacterization, viewport,
1846 kNumDivisions, kNumDivisions,
1847 /* addRandomPaddingToDst */ false);
1848
1849 tiles.createBackendTextures(gpuTaskGroup, dContext);
1850
1851 tiles.kickOffThreadedWork(recordingTaskGroup, gpuTaskGroup, dContext, newSKP.get());
1852
1853 // We have to wait for the recording threads to schedule all their work on the gpu thread
1854 // before we can schedule the composition draw and the flush. Note that the gpu thread
1855 // is not blocked at this point and this thread is borrowing recording work.
1856 recordingTaskGroup->wait();
1857
1858 // Note: at this point the recording thread(s) are stalled out w/ nothing to do.
1859
1860 if (FLAGS_preAbandonGpuContext) {
1861 dContext->abandonContext();
1862 }
1863
1864 // The recording threads have already scheduled the drawing of each tile's DDL on the gpu
1865 // thread. The composition DDL must be scheduled last bc it relies on the result of all
1866 // the tiles' rendering. Additionally, bc we're aliasing the tiles' backend textures,
1867 // there is nothing in the DAG to automatically force the required order.
1868 gpuTaskGroup->add([dstSurface, ddl = tiles.composeDDL()]() {
1869 skgpu::ganesh::DrawDDL(dstSurface, ddl);
1870 });
1871
1872 // This should be the only explicit flush for the entire DDL draw.
1873 gpuTaskGroup->add([dContext]() {
1874 // We need to ensure all the GPU work is finished so
1875 // the following 'deleteAllFromGPU' call will work
1876 // on Vulkan.
1877 // TODO: switch over to using the promiseImage callbacks
1878 // to free the backendTextures. This is complicated a
1879 // bit by which thread possesses the direct context.
1880 dContext->flush();
1881 dContext->submit(GrSyncCpu::kYes);
1882 });
1883
1884 // The backend textures are created on the gpuThread by the 'uploadAllToGPU' call.
1885 // It is simpler to also delete them at this point on the gpuThread.
1886 promiseImageHelper.deleteAllFromGPU(gpuTaskGroup, dContext);
1887
1888 tiles.deleteBackendTextures(gpuTaskGroup, dContext);
1889
1890 // A flush has already been scheduled on the gpu thread along with the clean up of the backend
1891 // textures so it is safe to schedule making 'gpuTestCtx' not current on the gpuThread.
1892 gpuTaskGroup->add([gpuTestCtx] { gpuTestCtx->makeNotCurrent(); });
1893
1894 // All the work is scheduled on the gpu thread, we just need to wait
1895 gpuTaskGroup->wait();
1896
1897 return Result::Ok();
1898}
1899
1900Result GPUDDLSink::draw(const Src& src, SkBitmap* dst, SkWStream*, SkString* log) const {
1901 GrContextOptions contextOptions = this->baseContextOptions();
1902 src.modifyGrContextOptions(&contextOptions);
1903 contextOptions.fPersistentCache = nullptr;
1904 contextOptions.fExecutor = nullptr;
1905
1906 GrContextFactory factory(contextOptions);
1907
1908 // This captures the context destined to be the main gpu context
1909 ContextInfo mainCtxInfo = factory.getContextInfo(this->contextType(), this->contextOverrides());
1910 sk_gpu_test::TestContext* mainTestCtx = mainCtxInfo.testContext();
1911 auto mainCtx = mainCtxInfo.directContext();
1912 if (!mainCtx) {
1913 return Result::Fatal("Could not create context.");
1914 }
1915
1916 SkASSERT(mainCtx->priv().getGpu());
1917
1918 // TODO: make use of 'otherCtx' for uploads & compilation
1919#if 0
1920 // This captures the context destined to be the utility context. It is in a share group
1921 // with the main context
1922 ContextInfo otherCtxInfo = factory.getSharedContextInfo(mainCtx);
1923 sk_gpu_test::TestContext* otherTestCtx = otherCtxInfo.testContext();
1924 auto otherCtx = otherCtxInfo.directContext();
1925 if (!otherCtx) {
1926 return Result::Fatal("Cound not create shared context.");
1927 }
1928
1929 SkASSERT(otherCtx->priv().getGpu());
1930#endif
1931
1932 SkTaskGroup recordingTaskGroup(*fRecordingExecutor);
1933 SkTaskGroup gpuTaskGroup(*fGPUExecutor);
1934
1935 // Make sure 'mainCtx' is current
1936 mainTestCtx->makeCurrent();
1937
1938 sk_sp<SkSurface> surface = this->createDstSurface(mainCtx, src.size());
1939 if (!surface) {
1940 return Result::Fatal("Could not create a surface.");
1941 }
1942
1943 Result result = this->ddlDraw(src, surface, &recordingTaskGroup, &gpuTaskGroup,
1944 mainTestCtx, mainCtx);
1945 if (!result.isOk()) {
1946 return result;
1947 }
1948
1949 // 'ddlDraw' will have made 'mainCtx' not current on the gpuThread
1950 mainTestCtx->makeCurrent();
1951
1952 if (FLAGS_gpuStats) {
1953 mainCtx->priv().dumpCacheStats(log);
1954 mainCtx->priv().dumpGpuStats(log);
1955 mainCtx->priv().dumpContextStats(log);
1956
1957#if 0
1958 otherCtx->priv().dumpCacheStats(log);
1959 otherCtx->priv().dumpGpuStats(log);
1960 otherCtx->priv().dumpContextStats(log);
1961#endif
1962 }
1963
1964 if (!this->readBack(surface.get(), dst)) {
1965 return Result::Fatal("Could not readback from surface.");
1966 }
1967
1968 return Result::Ok();
1969}
1970
1971/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1972static Result draw_skdocument(const Src& src, SkDocument* doc, SkWStream* dst) {
1973 if (src.size().isEmpty()) {
1974 return Result::Fatal("Source has empty dimensions");
1975 }
1976 SkASSERT(doc);
1977 int pageCount = src.pageCount();
1978 for (int i = 0; i < pageCount; ++i) {
1979 int width = src.size(i).width(), height = src.size(i).height();
1980 SkCanvas* canvas =
1982 if (!canvas) {
1983 return Result::Fatal("SkDocument::beginPage(w,h) returned nullptr");
1984 }
1985 Result result = src.draw(i, canvas, /*GraphiteTestContext=*/nullptr);
1986 if (!result.isOk()) {
1987 return result;
1988 }
1989 doc->endPage();
1990 }
1991 doc->close();
1992 dst->flush();
1993 return Result::Ok();
1994}
1995
1996Result PDFSink::draw(const Src& src, SkBitmap*, SkWStream* dst, SkString*) const {
1997 SkPDF::Metadata metadata;
1998 metadata.fTitle = src.name();
1999 metadata.fSubject = "rendering correctness test";
2000 metadata.fCreator = "Skia/DM";
2001 metadata.fRasterDPI = fRasterDpi;
2002 metadata.fPDFA = fPDFA;
2003#if SK_PDF_TEST_EXECUTOR
2004 std::unique_ptr<SkExecutor> executor = SkExecutor::MakeFIFOThreadPool();
2005 metadata.fExecutor = executor.get();
2006#endif
2007 auto doc = SkPDF::MakeDocument(dst, metadata);
2008 if (!doc) {
2009 return Result::Fatal("SkPDF::MakeDocument() returned nullptr");
2010 }
2011 return draw_skdocument(src, doc.get(), dst);
2012}
2013
2014/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
2015
2017
2018#if defined(SK_SUPPORT_XPS)
2019static SkTScopedComPtr<IXpsOMObjectFactory> make_xps_factory() {
2020 IXpsOMObjectFactory* factory;
2021 HRN(CoCreateInstance(CLSID_XpsOMObjectFactory,
2022 nullptr,
2023 CLSCTX_INPROC_SERVER,
2024 IID_PPV_ARGS(&factory)));
2025 return SkTScopedComPtr<IXpsOMObjectFactory>(factory);
2026}
2027
2028Result XPSSink::draw(const Src& src, SkBitmap*, SkWStream* dst, SkString*) const {
2029 SkAutoCoInitialize com;
2030 if (!com.succeeded()) {
2031 return Result::Fatal("Could not initialize COM.");
2032 }
2033 SkTScopedComPtr<IXpsOMObjectFactory> factory = make_xps_factory();
2034 if (!factory) {
2035 return Result::Fatal("Failed to create XPS Factory.");
2036 }
2037 auto doc = SkXPS::MakeDocument(dst, factory.get());
2038 if (!doc) {
2039 return Result::Fatal("SkXPS::MakeDocument() returned nullptr");
2040 }
2041 return draw_skdocument(src, doc.get(), dst);
2042}
2043#else
2044Result XPSSink::draw(const Src& src, SkBitmap*, SkWStream* dst, SkString*) const {
2045 return Result::Fatal("XPS not supported on this platform.");
2046}
2047#endif
2048
2050 static SkSerialProcs procs;
2051 procs.fImageProc = [](SkImage* img, void*) -> sk_sp<SkData> {
2052 return SkPngEncoder::Encode(as_IB(img)->directContext(), img, SkPngEncoder::Options{});
2053 };
2054 return procs;
2055}
2056
2057/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
2058
2060
2061Result SKPSink::draw(const Src& src, SkBitmap*, SkWStream* dst, SkString*) const {
2062 auto size = SkSize::Make(src.size());
2063 SkPictureRecorder recorder;
2064 Result result = src.draw(recorder.beginRecording(size.width(), size.height()),
2065 /*GraphiteTestContext=*/nullptr);
2066 if (!result.isOk()) {
2067 return result;
2068 }
2070 recorder.finishRecordingAsPicture()->serialize(dst, &procs);
2071 return Result::Ok();
2072}
2073
2074/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
2075
2077 DebugCanvas debugCanvas(src.size().width(), src.size().height());
2078 Result result = src.draw(&debugCanvas, /*GraphiteTestContext=*/nullptr);
2079 if (!result.isOk()) {
2080 return result;
2081 }
2082 std::unique_ptr<SkCanvas> nullCanvas = SkMakeNullCanvas();
2083 UrlDataManager dataManager(SkString("data"));
2085 writer.beginObject(); // root
2086 debugCanvas.toJSON(writer, dataManager, nullCanvas.get());
2087 writer.endObject(); // root
2088 writer.flush();
2089 return Result::Ok();
2090}
2091
2092/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
2093
2094SVGSink::SVGSink(int pageIndex) : fPageIndex(pageIndex) {}
2095
2096Result SVGSink::draw(const Src& src, SkBitmap*, SkWStream* dst, SkString*) const {
2097#if defined(SK_ENABLE_SVG)
2098 if (src.pageCount() > 1) {
2099 int pageCount = src.pageCount();
2100 if (fPageIndex > pageCount - 1) {
2101 return Result::Fatal("Page index %d too high for document with only %d pages.",
2102 fPageIndex, pageCount);
2103 }
2104 }
2105 return src.draw(fPageIndex,
2107 SkIntToScalar(src.size().height())),
2108 dst)
2109 .get(),
2110 /*GraphiteTestContext=*/nullptr);
2111#else
2112 (void)fPageIndex;
2113 return Result::Fatal("SVG sink is disabled.");
2114#endif // SK_ENABLE_SVG
2115}
2116
2117/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
2118
2121
2123 const SkISize size = src.size();
2124 if (size.isEmpty()) {
2126 SkStringPrintf("Skipping empty source: %s", src.name().c_str()));
2127 }
2128
2129 dst->allocPixelsFlags(SkImageInfo::Make(size, this->colorInfo()),
2131
2132 SkSurfaceProps props(/*flags=*/0, kRGB_H_SkPixelGeometry);
2133 auto surface = SkSurfaces::WrapPixels(dst->pixmap(), &props);
2134 return src.draw(surface->getCanvas(), /*GraphiteTestContext=*/nullptr);
2135}
2136
2137/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
2138
2139#if defined(SK_GRAPHITE)
2140
2141GraphiteSink::GraphiteSink(const SkCommandLineConfigGraphite* config)
2142 : fOptions(config->getOptions())
2143 , fContextType(config->getContextType())
2144 , fSurfaceType(config->getSurfaceType())
2145 , fColorType(config->getColorType())
2146 , fAlphaType(config->getAlphaType()) {}
2147
2148Result GraphiteSink::draw(const Src& src,
2149 SkBitmap* dst,
2150 SkWStream* dstStream,
2151 SkString* log) const {
2153 // If we've copied context options from an external source we can't trust that the
2154 // priv pointer is still in scope, so assume it should be NULL and set our own up.
2155 SkASSERT(!options.fContextOptions.fOptionsPriv);
2157 options.fContextOptions.fOptionsPriv = &optionsPriv;
2158
2159 src.modifyGraphiteContextOptions(&options.fContextOptions);
2160
2162 skiatest::graphite::ContextInfo ctxInfo = factory.getContextInfo(fContextType);
2163 skgpu::graphite::Context* context = ctxInfo.fContext;
2164 if (!context) {
2165 return Result::Fatal("Could not create a context.");
2166 }
2167
2168 std::unique_ptr<skgpu::graphite::Recorder> recorder =
2169 context->makeRecorder(ToolUtils::CreateTestingRecorderOptions());
2170 if (!recorder) {
2171 return Result::Fatal("Could not create a recorder.");
2172 }
2173
2174 {
2175 sk_sp<SkSurface> surface = this->makeSurface(recorder.get(), src.size());
2176 if (!surface) {
2177 return Result::Fatal("Could not create a surface.");
2178 }
2179 dst->allocPixels(surface->imageInfo());
2180 Result result = src.draw(surface->getCanvas(), ctxInfo.fTestContext);
2181 if (!result.isOk()) {
2182 return result;
2183 }
2184
2185 SkPixmap pm;
2186 if (!dst->peekPixels(&pm) ||
2187 !surface->readPixels(pm, 0, 0)) {
2188 return Result::Fatal("Could not readback from surface.");
2189 }
2190 }
2191
2192 std::unique_ptr<skgpu::graphite::Recording> recording = recorder->snap();
2193 if (!recording) {
2194 return Result::Fatal("Could not create a recording.");
2195 }
2196
2198 info.fRecording = recording.get();
2199 if (!context->insertRecording(info)) {
2200 return Result::Fatal("Context::insertRecording failed.");
2201 }
2202 ctxInfo.fTestContext->syncedSubmit(context);
2203
2204 return Result::Ok();
2205}
2206
2207sk_sp<SkSurface> GraphiteSink::makeSurface(skgpu::graphite::Recorder* recorder,
2208 SkISize dimensions) const {
2210 auto ii = SkImageInfo::Make(dimensions, this->colorInfo());
2211 switch (fSurfaceType) {
2213 return SkSurfaces::RenderTarget(recorder, ii, skgpu::Mipmapped::kNo, &props);
2214
2215 case SurfaceType::kWrapTextureView:
2216 return sk_gpu_test::MakeBackendTextureViewSurface(recorder,
2217 ii,
2218 skgpu::Mipmapped::kNo,
2219 skgpu::Protected::kNo,
2220 &props);
2221 }
2223}
2224
2225/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
2226
2227#if defined(SK_ENABLE_PRECOMPILE)
2228
2229GraphitePrecompileTestingSink::GraphitePrecompileTestingSink(
2230 const SkCommandLineConfigGraphite* config) : GraphiteSink(config) {}
2231
2232GraphitePrecompileTestingSink::~GraphitePrecompileTestingSink() {}
2233
2234Result GraphitePrecompileTestingSink::drawSrc(
2235 const Src& src,
2236 skgpu::graphite::Context* context,
2237 skiatest::graphite::GraphiteTestContext* testContext) const {
2238 if (!fRecorder) {
2239 fRecorder = context->makeRecorder(ToolUtils::CreateTestingRecorderOptions());
2240 if (!fRecorder) {
2241 return Result::Fatal("Could not create a recorder.");
2242 }
2243 }
2244
2245 sk_sp<SkSurface> surface = this->makeSurface(fRecorder.get(), src.size());
2246 if (!surface) {
2247 return Result::Fatal("Could not create a surface.");
2248 }
2249 Result result = src.draw(surface->getCanvas(), testContext);
2250 if (!result.isOk()) {
2251 return result;
2252 }
2253
2254 std::unique_ptr<skgpu::graphite::Recording> recording = fRecorder->snap();
2255 if (!recording) {
2256 return Result::Fatal("Could not create a recording.");
2257 }
2258
2260 info.fRecording = recording.get();
2261 if (!context->insertRecording(info)) {
2262 return Result::Fatal("Context::insertRecording failed.");
2263 }
2265 return Result::Fatal("Context::submit failed.");
2266 }
2267
2268 return Result::Ok();
2269}
2270
2271Result GraphitePrecompileTestingSink::resetAndRecreatePipelines(
2272 skgpu::graphite::Context* context) const {
2273 using namespace skgpu::graphite;
2274
2275 SkASSERT(fRecorder);
2276
2277 RuntimeEffectDictionary* rteDict = fRecorder->priv().runtimeEffectDictionary();
2278
2279 std::vector<skgpu::UniqueKey> origKeys;
2280
2281 UniqueKeyUtils::FetchUniqueKeys(context->priv().globalCache(), &origKeys);
2282
2283 SkDEBUGCODE(int numBeforeReset = context->priv().globalCache()->numGraphicsPipelines();)
2284 SkASSERT(numBeforeReset == (int) origKeys.size());
2285
2286 context->priv().globalCache()->resetGraphicsPipelines();
2287
2288 SkASSERT(context->priv().globalCache()->numGraphicsPipelines() == 0);
2289
2290 for (const skgpu::UniqueKey& k : origKeys) {
2291 // TODO: add a separate path that decomposes the keys into PaintOptions
2292 // and uses them to Precompile
2293 GraphicsPipelineDesc pipelineDesc;
2294 RenderPassDesc renderPassDesc;
2295
2296 if (!UniqueKeyUtils::ExtractKeyDescs(context, k, &pipelineDesc, &renderPassDesc)) {
2297 continue;
2298 }
2299
2300 Precompile(context, rteDict, pipelineDesc, renderPassDesc);
2301 }
2302
2303 SkDEBUGCODE(int postRecreate = context->priv().globalCache()->numGraphicsPipelines();)
2304
2305 SkASSERT(numBeforeReset == postRecreate);
2306
2307 {
2308 std::vector<skgpu::UniqueKey> recreatedKeys;
2309
2310 UniqueKeyUtils::FetchUniqueKeys(context->priv().globalCache(), &recreatedKeys);
2311
2312 for (const skgpu::UniqueKey& origKey : origKeys) {
2313 if(std::find(recreatedKeys.begin(), recreatedKeys.end(), origKey) ==
2314 recreatedKeys.end()) {
2315 sk_sp<GraphicsPipeline> pipeline =
2316 context->priv().globalCache()->findGraphicsPipeline(origKey);
2317 SkASSERT(!pipeline);
2318
2319#ifdef SK_DEBUG
2320 const RendererProvider* rendererProvider = context->priv().rendererProvider();
2321 const ShaderCodeDictionary* dict = context->priv().shaderCodeDictionary();
2322
2323 {
2324 GraphicsPipelineDesc originalPipelineDesc;
2325 RenderPassDesc originalRenderPassDesc;
2326 UniqueKeyUtils::ExtractKeyDescs(context, origKey,
2327 &originalPipelineDesc,
2328 &originalRenderPassDesc);
2329
2330 SkDebugf("------- Missing key from rebuilt keys:\n");
2331 origKey.dump("original key:");
2332 UniqueKeyUtils::DumpDescs(rendererProvider, dict,
2333 originalPipelineDesc,
2334 originalRenderPassDesc);
2335 }
2336
2337 SkDebugf("Have %d recreated keys -----------------\n", (int) recreatedKeys.size());
2338 int count = 0;
2339 for (const skgpu::UniqueKey& recreatedKey : recreatedKeys) {
2340
2341 GraphicsPipelineDesc recreatedPipelineDesc;
2342 RenderPassDesc recreatedRenderPassDesc;
2343 UniqueKeyUtils::ExtractKeyDescs(context, recreatedKey,
2344 &recreatedPipelineDesc,
2345 &recreatedRenderPassDesc);
2346
2347 SkDebugf("%d ----\n", count++);
2348 recreatedKey.dump("recreated key:");
2349 UniqueKeyUtils::DumpDescs(rendererProvider, dict,
2350 recreatedPipelineDesc,
2351 recreatedRenderPassDesc);
2352 }
2353#endif
2354
2355 SK_ABORT("missing");
2356 }
2357 }
2358 }
2359
2360 return Result::Ok();
2361}
2362
2363Result GraphitePrecompileTestingSink::draw(const Src& src,
2364 SkBitmap* dst,
2365 SkWStream* dstStream,
2366 SkString* log) const {
2368 // If we've copied context options from an external source we can't trust that the
2369 // priv pointer is still in scope, so assume it should be NULL and set our own up.
2370 SkASSERT(!options.fContextOptions.fOptionsPriv);
2372 options.fContextOptions.fOptionsPriv = &optionsPriv;
2373
2374 src.modifyGraphiteContextOptions(&options.fContextOptions);
2375
2377 skiatest::graphite::ContextInfo ctxInfo = factory.getContextInfo(fContextType);
2378 skgpu::graphite::Context* context = ctxInfo.fContext;
2379 if (!context) {
2380 return Result::Fatal("Could not create a context.");
2381 }
2382
2383 // First, clear out any miscellaneous Pipelines that might be cluttering up the global cache.
2384 context->priv().globalCache()->resetGraphicsPipelines();
2385
2386 // Draw the Src for the first time, populating the global pipeline cache.
2387 Result result = this->drawSrc(src, context, ctxInfo.fTestContext);
2388 if (!result.isOk()) {
2389 fRecorder.reset();
2390 return result;
2391 }
2392
2393 // Call resetAndRecreatePipelines to clear out all the Pipelines in the global cache and then
2394 // regenerate them using the Precompilation system.
2395 result = this->resetAndRecreatePipelines(context);
2396 if (!result.isOk()) {
2397 fRecorder.reset();
2398 return result;
2399 }
2400
2401 // Draw the Src for the second time. This shouldn't create any new Pipelines since the ones
2402 // generated via Precompilation should be sufficient.
2403 result = this->drawSrc(src, context, ctxInfo.fTestContext);
2404 if (!result.isOk()) {
2405 fRecorder.reset();
2406 return result;
2407 }
2408
2409 fRecorder.reset();
2410
2411 // TODO: verify that no additional pipelines were created during the second 'drawSrc' call
2412 return Result::Ok();
2413}
2414#endif // SK_ENABLE_PRECOMPILE
2415
2416#endif // SK_GRAPHITE
2417
2418/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
2419
2420// Handy for front-patching a Src. Do whatever up-front work you need, then call draw_to_canvas(),
2421// passing the Sink draw() arguments, a size, and a function draws into an SkCanvas.
2422// Several examples below.
2423
2425
2427 SkString* log, SkISize size, const DrawToCanvasFn& draw) {
2428 class ProxySrc : public Src {
2429 public:
2430 ProxySrc(SkISize size, const DrawToCanvasFn& draw) : fSize(size), fDraw(draw) {}
2431 Result draw(SkCanvas* canvas, GraphiteTestContext* testContext) const override {
2432 return fDraw(canvas, testContext);
2433 }
2434 Name name() const override { return "ProxySrc"; }
2435 SkISize size() const override { return fSize; }
2436 private:
2437 SkISize fSize;
2438 const DrawToCanvasFn& fDraw;
2439 };
2440 return sink->draw(ProxySrc(size, draw), bitmap, stream, log);
2441}
2442
2443/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
2444
2445static DEFINE_bool(check, true, "If true, have most Via- modes fail if they affect the output.");
2446
2447// Is *bitmap identical to what you get drawing src into sink?
2448static Result check_against_reference(const SkBitmap* bitmap, const Src& src, Sink* sink) {
2449 // We can only check raster outputs.
2450 // (Non-raster outputs like .pdf, .skp, .svg may differ but still draw identically.)
2451 if (FLAGS_check && bitmap) {
2452 SkBitmap reference;
2453 SkString log;
2454 SkDynamicMemoryWStream wStream;
2455 Result result = sink->draw(src, &reference, &wStream, &log);
2456 // If we can draw into this Sink via some pipeline, we should be able to draw directly.
2457 SkASSERT(result.isOk());
2458 if (!result.isOk()) {
2459 return result;
2460 }
2461 return compare_bitmaps(reference, *bitmap);
2462 }
2463 return Result::Ok();
2464}
2465
2466/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
2467
2468static SkISize auto_compute_translate(SkMatrix* matrix, int srcW, int srcH) {
2469 SkRect bounds = SkRect::MakeIWH(srcW, srcH);
2470 matrix->mapRect(&bounds);
2471 matrix->postTranslate(-bounds.x(), -bounds.y());
2472 return {SkScalarRoundToInt(bounds.width()), SkScalarRoundToInt(bounds.height())};
2473}
2474
2475ViaMatrix::ViaMatrix(SkMatrix matrix, Sink* sink) : Via(sink), fMatrix(matrix) {}
2476
2477Result ViaMatrix::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
2478 SkMatrix matrix = fMatrix;
2479 SkISize size = auto_compute_translate(&matrix, src.size().width(), src.size().height());
2480 return draw_to_canvas(fSink.get(), bitmap, stream, log, size,
2481 [&](SkCanvas* canvas,
2482 Src::GraphiteTestContext* testContext) {
2483 canvas->concat(matrix);
2484 return src.draw(canvas, testContext);
2485 });
2486}
2487
2488// Undoes any flip or 90 degree rotate without changing the scale of the bitmap.
2489// This should be pixel-preserving.
2490ViaUpright::ViaUpright(SkMatrix matrix, Sink* sink) : Via(sink), fMatrix(matrix) {}
2491
2492Result ViaUpright::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
2493 Result result = fSink->draw(src, bitmap, stream, log);
2494 if (!result.isOk()) {
2495 return result;
2496 }
2497
2498 SkMatrix inverse;
2499 if (!fMatrix.rectStaysRect() || !fMatrix.invert(&inverse)) {
2500 return Result::Fatal("Cannot upright --matrix.");
2501 }
2502 SkMatrix upright = SkMatrix::I();
2503 upright.setScaleX(SkScalarSignAsScalar(inverse.getScaleX()));
2504 upright.setScaleY(SkScalarSignAsScalar(inverse.getScaleY()));
2505 upright.setSkewX(SkScalarSignAsScalar(inverse.getSkewX()));
2506 upright.setSkewY(SkScalarSignAsScalar(inverse.getSkewY()));
2507
2508 SkBitmap uprighted;
2509 SkISize size = auto_compute_translate(&upright, bitmap->width(), bitmap->height());
2510 uprighted.allocPixels(bitmap->info().makeDimensions(size));
2511
2512 SkCanvas canvas(uprighted);
2513 canvas.concat(upright);
2514 SkPaint paint;
2515 paint.setBlendMode(SkBlendMode::kSrc);
2516 canvas.drawImage(bitmap->asImage(), 0, 0, SkSamplingOptions(), &paint);
2517
2518 *bitmap = uprighted;
2519 return Result::Ok();
2520}
2521
2522/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
2523
2525 const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
2526 // Record our Src into a picture.
2527 auto size = src.size();
2528 SkPictureRecorder recorder;
2529 Result result = src.draw(recorder.beginRecording(SkIntToScalar(size.width()),
2530 SkIntToScalar(size.height())),
2531 /*GraphiteTestContext=*/nullptr);
2532 if (!result.isOk()) {
2533 return result;
2534 }
2536
2538 // Serialize it and then deserialize it.
2539 sk_sp<SkPicture> deserialized = SkPicture::MakeFromData(pic->serialize(&procs).get());
2540
2541 result = draw_to_canvas(fSink.get(), bitmap, stream, log, size,
2542 [&](SkCanvas* canvas, Src::GraphiteTestContext*) {
2543 canvas->drawPicture(deserialized);
2544 return Result::Ok();
2545 });
2546 if (!result.isOk()) {
2547 return result;
2548 }
2549
2550 return check_against_reference(bitmap, src, fSink.get());
2551}
2552
2553/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
2554
2555Result ViaPicture::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
2556 auto size = src.size();
2557 Result result = draw_to_canvas(fSink.get(), bitmap, stream, log, size,
2558 [&](SkCanvas* canvas, Src::GraphiteTestContext* testContext) {
2559 SkPictureRecorder recorder;
2560 sk_sp<SkPicture> pic;
2561 Result result = src.draw(recorder.beginRecording(SkIntToScalar(size.width()),
2562 SkIntToScalar(size.height())),
2563 testContext);
2564 if (!result.isOk()) {
2565 return result;
2566 }
2567 pic = recorder.finishRecordingAsPicture();
2568 canvas->drawPicture(pic);
2569 return result;
2570 });
2571 if (!result.isOk()) {
2572 return result;
2573 }
2574
2575 return check_against_reference(bitmap, src, fSink.get());
2576}
2577
2578/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
2579
2580Result ViaRuntimeBlend::draw(const Src& src,
2582 SkWStream* stream,
2583 SkString* log) const {
2584 class RuntimeBlendFilterCanvas : public SkPaintFilterCanvas {
2585 public:
2586 RuntimeBlendFilterCanvas(SkCanvas* canvas) : INHERITED(canvas) { }
2587
2588 protected:
2589 bool onFilter(SkPaint& paint) const override {
2590 if (std::optional<SkBlendMode> mode = paint.asBlendMode()) {
2591 paint.setBlender(GetRuntimeBlendForBlendMode(*mode));
2592 }
2593 return true;
2594 }
2595
2596 private:
2598 };
2599
2600 return draw_to_canvas(fSink.get(), bitmap, stream, log, src.size(),
2601 [&](SkCanvas* canvas, Src::GraphiteTestContext* testContext) {
2602 RuntimeBlendFilterCanvas runtimeBlendCanvas{canvas};
2603 return src.draw(&runtimeBlendCanvas, testContext);
2604 });
2605}
2606
2607/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
2608
2609#ifdef TEST_VIA_SVG
2610Result ViaSVG::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
2611 auto size = src.size();
2612 return draw_to_canvas(fSink.get(), bitmap, stream, log, size,
2613 [&](SkCanvas* canvas, Src::GraphiteTestContext* testContext) -> Result {
2614 SkDynamicMemoryWStream wstream;
2615 SkXMLStreamWriter writer(&wstream);
2616 Result result = src.draw(SkSVGCanvas::Make(SkRect::Make(size), &writer).get(),
2617 testContext);
2618 if (!result.isOk()) {
2619 return result;
2620 }
2621
2622 auto shapingFactory = SkShapers::BestAvailable();
2624 // When rendering our SVGs we want to be sure we are using shaping.
2625 // If we fail to make a shaper, then it can mean something like skunicode is misconfigured.
2626 SkASSERT(shapingFactory->makeShaper(fontMgr));
2627
2628 std::unique_ptr<SkStream> rstream(wstream.detachAsStream());
2630 .setFontManager(std::move(fontMgr))
2631 .setTextShapingFactory(std::move(shapingFactory))
2632 .make(*rstream);
2633 if (dom) {
2634 dom->setContainerSize(SkSize::Make(size));
2635 dom->render(canvas);
2636 }
2637 return Result::Ok();
2638 });
2639}
2640#endif
2641
2642} // namespace DM
SkPath fPath
SkMatrix fViewMatrix
#define DEFINE_bool(name, defaultValue, helpString)
#define DEFINE_int(name, defaultValue, helpString)
#define DECLARE_int(name)
const char * options
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
const char * fName
int count
static GrDirectContext * GrAsDirectContext(GrContext_Base *base)
@ kBottomLeft_GrSurfaceOrigin
Definition GrTypes.h:149
@ kTopLeft_GrSurfaceOrigin
Definition GrTypes.h:148
SkColorType fColorType
#define check(reporter, ref, unref, make, kill)
sk_sp< SkBlender > GetRuntimeBlendForBlendMode(SkBlendMode mode)
static constexpr T SkAlign2(T x)
Definition SkAlign.h:15
SkAlphaType
Definition SkAlphaType.h:26
@ kOpaque_SkAlphaType
pixel is opaque
Definition SkAlphaType.h:28
#define SkAssertResult(cond)
Definition SkAssert.h:123
#define SkUNREACHABLE
Definition SkAssert.h:135
#define SK_ABORT(message,...)
Definition SkAssert.h:70
#define SkASSERT_RELEASE(cond)
Definition SkAssert.h:100
#define SkASSERT(cond)
Definition SkAssert.h:116
SkColorType
Definition SkColorType.h:19
@ kBGRA_8888_SkColorType
pixel with 8 bits for blue, green, red, alpha; in 32-bit word
Definition SkColorType.h:26
@ kRGBA_F16_SkColorType
pixel with half floats for red, green, blue, alpha;
Definition SkColorType.h:38
@ kAlpha_8_SkColorType
pixel with alpha in 8-bit byte
Definition SkColorType.h:21
@ kGray_8_SkColorType
pixel with grayscale level in 8-bit byte
Definition SkColorType.h:35
@ kRGB_565_SkColorType
pixel with 5 bits red, 6 bits green, 5 bits blue, in 16-bit word
Definition SkColorType.h:22
@ kRGBA_8888_SkColorType
pixel with 8 bits for red, green, blue, alpha; in 32-bit word
Definition SkColorType.h:24
constexpr SkColor SK_ColorWHITE
Definition SkColor.h:122
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
#define SkDEBUGCODE(...)
Definition SkDebug.h:23
static bool SkEncodedOriginSwapsWidthHeight(SkEncodedOrigin origin)
#define sk_float_ceil2int(x)
static SkColorType colorType(AImageDecoder *decoder, const AImageDecoderHeaderInfo *headerInfo)
static SkImage_Base * as_IB(SkImage *image)
SK_API std::unique_ptr< SkCanvas > SkMakeNullCanvas()
bool SkPicture_StreamIsSKP(SkStream *stream, SkPictInfo *pInfo)
static bool left(const SkPoint &p0, const SkPoint &p1)
#define INHERITED(method,...)
static SkScalar SkScalarSignAsScalar(SkScalar x)
Definition SkScalar.h:95
#define SkScalarRoundToInt(x)
Definition SkScalar.h:37
#define SkIntToScalar(x)
Definition SkScalar.h:57
SK_API SkString static SkString SkStringPrintf()
Definition SkString.h:287
@ kRGB_H_SkPixelGeometry
#define W
Definition aaa.cpp:17
static void draw(SkCanvas *canvas, SkRect &target, int x, int y)
Definition aaclip.cpp:27
SkISize size() const override
AndroidCodecSrc(Path, CodecSrc::DstColorType, SkAlphaType, int sampleSize)
bool veto(SinkFlags) const override
Result draw(SkCanvas *, GraphiteTestContext *) const override
Name name() const override
Result draw(SkCanvas *, GraphiteTestContext *) const override
BisectSrc(Path path, const char *trail)
bool veto(SinkFlags) const override
SkISize size() const override
@ kGetFromCanvas_DstColorType
Definition DMSrcSink.h:167
@ kGrayscale_Always_DstColorType
Definition DMSrcSink.h:168
@ kNonNative8888_Always_DstColorType
Definition DMSrcSink.h:169
Name name() const override
Result draw(SkCanvas *, GraphiteTestContext *) const override
@ kCroppedScanline_Mode
Definition DMSrcSink.h:162
@ kCodecZeroInit_Mode
Definition DMSrcSink.h:159
Result draw(SkCanvas *, GraphiteTestContext *) const override
Name name() const override
bool veto(SinkFlags) const override
ColorCodecSrc(Path, bool decode_to_dst)
SkISize size() const override
Result draw(const Src &, SkBitmap *, SkWStream *, SkString *) const override
Name name() const override
GMSrc(skiagm::GMFactory)
Result draw(SkCanvas *, GraphiteTestContext *) const override
void modifyGrContextOptions(GrContextOptions *options) const override
SkISize size() const override
GPUDDLSink(const SkCommandLineConfigGpu *, const GrContextOptions &)
Result draw(const Src &, SkBitmap *, SkWStream *, SkString *) const override
GPUPersistentCacheTestingSink(const SkCommandLineConfigGpu *, const GrContextOptions &)
Result draw(const Src &, SkBitmap *, SkWStream *, SkString *) const override
Result draw(const Src &, SkBitmap *, SkWStream *, SkString *) const override
GPUPrecompileTestingSink(const SkCommandLineConfigGpu *, const GrContextOptions &)
GPURemoteSlugSink(const SkCommandLineConfigGpu *, const GrContextOptions &)
Result draw(const Src &, SkBitmap *, SkWStream *, SkString *) const override
Result draw(const Src &, SkBitmap *, SkWStream *, SkString *) const override
GPUSerializeSlugSink(const SkCommandLineConfigGpu *, const GrContextOptions &)
const sk_gpu_test::GrContextFactory::ContextOverrides & contextOverrides() const
Definition DMSrcSink.h:378
GPUSink(const SkCommandLineConfigGpu *, const GrContextOptions &)
skgpu::ContextType contextType() const
Definition DMSrcSink.h:377
Result onDraw(const Src &, SkBitmap *, SkWStream *, SkString *, const GrContextOptions &baseOptions, std::function< void(GrDirectContext *)> initContext=nullptr, std::function< SkCanvas *(SkCanvas *)> wrapCanvas=nullptr) const
Result draw(const Src &, SkBitmap *, SkWStream *, SkString *) const override
const GrContextOptions & baseContextOptions() const
Definition DMSrcSink.h:389
bool readBack(SkSurface *, SkBitmap *dst) const
SkColorInfo colorInfo() const override
Definition DMSrcSink.h:391
sk_sp< SkSurface > createDstSurface(GrDirectContext *, SkISize size) const
GPUSlugSink(const SkCommandLineConfigGpu *, const GrContextOptions &)
Result draw(const Src &, SkBitmap *, SkWStream *, SkString *) const override
bool veto(SinkFlags) const override
Name name() const override
ImageGenSrc(Path, Mode, SkAlphaType, bool)
SkISize size() const override
Result draw(SkCanvas *, GraphiteTestContext *) const override
SkISize size() const override
Result draw(SkCanvas *c, GraphiteTestContext *) const override
int pageCount() const override
MSKPSrc(Path path)
Name name() const override
Result draw(const Src &src, SkBitmap *, SkWStream *, SkString *) const override
SkScalar fRasterDpi
Definition DMSrcSink.h:503
Result draw(const Src &, SkBitmap *, SkWStream *, SkString *) const override
Result draw(const Src &, SkBitmap *, SkWStream *, SkString *) const override
RasterSink(SkColorType)
SkColorInfo colorInfo() const override
Definition DMSrcSink.h:524
bool isOk()
Definition DMSrcSink.h:72
static Result Ok()
Definition DMSrcSink.h:50
Result draw(const Src &, SkBitmap *, SkWStream *, SkString *) const override
SKPSrc(Path path)
Name name() const override
Result draw(SkCanvas *, GraphiteTestContext *) const override
SkISize size() const override
Result draw(const Src &, SkBitmap *, SkWStream *, SkString *) const override
SVGSink(int pageIndex=0)
Result draw(const Src &, SkBitmap *, SkWStream *, SkString *) const override
ViaMatrix(SkMatrix, Sink *)
Result draw(const Src &, SkBitmap *, SkWStream *, SkString *) const override
Result draw(const Src &, SkBitmap *, SkWStream *, SkString *) const override
Result draw(const Src &, SkBitmap *, SkWStream *, SkString *) const override
ViaUpright(SkMatrix, Sink *)
std::unique_ptr< Sink > fSink
Definition DMSrcSink.h:643
Result draw(const Src &, SkBitmap *, SkWStream *, SkString *) const override
void toJSON(SkJSONWriter &writer, UrlDataManager &urlDataManager, SkCanvas *)
bool submit(GrSyncCpu sync=GrSyncCpu::kNo)
GrSemaphoresSubmitted flush(const GrFlushInfo &info)
void abandonContext() override
static std::unique_ptr< SkAndroidCodec > MakeFromData(sk_sp< SkData >, SkPngChunkReader *=nullptr)
static std::unique_ptr< SkAndroidCodec > MakeFromCodec(std::unique_ptr< SkCodec >)
void * reset(size_t size=0, OnShrink shrink=kAlloc_OnShrink)
void * get()
void allocPixels(const SkImageInfo &info, size_t rowBytes)
Definition SkBitmap.cpp:258
size_t computeByteSize() const
Definition SkBitmap.h:293
void * getPixels() const
Definition SkBitmap.h:283
@ kZeroPixels_AllocFlag
zero pixel memory. No effect. This is the default.
Definition SkBitmap.h:435
void clipRect(const SkRect &rect, SkClipOp op, bool doAntiAlias)
void translate(SkScalar dx, SkScalar dy)
void drawColor(SkColor color, SkBlendMode mode=SkBlendMode::kSrcOver)
Definition SkCanvas.h:1182
virtual GrRecordingContext * recordingContext() const
virtual skgpu::graphite::Recorder * recorder() const
virtual SkISize getBaseLayerSize() const
Definition SkCanvas.cpp:373
@ kStrict_SrcRectConstraint
sample only inside bounds; slower
Definition SkCanvas.h:1542
void clear(SkColor color)
Definition SkCanvas.h:1199
void drawImageRect(const SkImage *, const SkRect &src, const SkRect &dst, const SkSamplingOptions &, const SkPaint *, SrcRectConstraint)
void drawPath(const SkPath &path, const SkPaint &paint)
void scale(SkScalar sx, SkScalar sy)
void concat(const SkMatrix &matrix)
void drawPicture(const SkPicture *picture)
Definition SkCanvas.h:1961
SkImageInfo imageInfo() const
bool readPixels(const SkImageInfo &dstInfo, void *dstPixels, size_t dstRowBytes, int srcX, int srcY)
Definition SkCanvas.cpp:386
void drawImage(const SkImage *image, SkScalar left, SkScalar top)
Definition SkCanvas.h:1528
static std::unique_ptr< SkImageGenerator > MakeFromEncodedCodec(sk_sp< SkData >, std::optional< SkAlphaType >=std::nullopt)
static std::unique_ptr< SkCodec > MakeFromData(sk_sp< SkData >, SkSpan< const SkCodecs::Decoder > decoders, SkPngChunkReader *=nullptr)
Definition SkCodec.cpp:241
@ kYes_ZeroInitialized
Definition SkCodec.h:308
@ kNo_ZeroInitialized
Definition SkCodec.h:315
@ kTopDown_SkScanlineOrder
Definition SkCodec.h:581
@ kInvalidConversion
Definition SkCodec.h:96
@ kIncompleteInput
Definition SkCodec.h:84
@ kInvalidInput
Definition SkCodec.h:109
@ kSuccess
Definition SkCodec.h:80
@ kErrorInInput
Definition SkCodec.h:91
static constexpr int kNoFrame
Definition SkCodec.h:650
static sk_sp< SkColorSpace > MakeSRGB()
static sk_sp< SkData > MakeWithoutCopy(const void *data, size_t length)
Definition SkData.h:116
static sk_sp< SkData > MakeFromFileName(const char path[])
Definition SkData.cpp:148
void close()
SkCanvas * beginPage(SkScalar width, SkScalar height, const SkRect *content=nullptr)
void endPage()
static std::unique_ptr< SkExecutor > MakeFIFOThreadPool(int threads=0, bool allowBorrowing=true)
sk_sp< SkImage > makeRasterImage(GrDirectContext *, CachingHint cachingHint=kDisallow_CachingHint) const
Definition SkImage.cpp:267
void beginObject(const char *name=nullptr, bool multiline=true)
static SkMatrix RectToRect(const SkRect &src, const SkRect &dst, ScaleToFit mode=kFill_ScaleToFit)
Definition SkMatrix.h:157
SkScalar getSkewY() const
Definition SkMatrix.h:430
SkMatrix & setSkewX(SkScalar v)
Definition SkMatrix.h:518
@ kCenter_ScaleToFit
scales and aligns to center
Definition SkMatrix.h:139
SkScalar getSkewX() const
Definition SkMatrix.h:438
bool invert(SkMatrix *inverse) const
Definition SkMatrix.h:1206
bool rectStaysRect() const
Definition SkMatrix.h:271
SkScalar getScaleX() const
Definition SkMatrix.h:415
static const SkMatrix & I()
SkScalar getScaleY() const
Definition SkMatrix.h:422
SkMatrix & setScaleY(SkScalar v)
Definition SkMatrix.h:506
SkMatrix & setSkewY(SkScalar v)
Definition SkMatrix.h:512
SkMatrix & setScaleX(SkScalar v)
Definition SkMatrix.h:500
static SkString Basename(const char *fullPath)
Definition SkOSPath.cpp:23
static SkString Dirname(const char *fullPath)
Definition SkOSPath.cpp:36
virtual bool onFilter(SkPaint &paint) const =0
SkCanvas * beginRecording(const SkRect &bounds, sk_sp< SkBBoxHierarchy > bbh)
sk_sp< SkPicture > finishRecordingAsPicture()
sk_sp< SkData > serialize(const SkSerialProcs *procs=nullptr) const
static sk_sp< SkPicture > MakeFromData(const SkData *data, const SkDeserialProcs *procs=nullptr)
static sk_sp< SkPicture > MakeFromStream(SkStream *stream, const SkDeserialProcs *procs=nullptr)
static std::unique_ptr< SkCanvas > Make(const SkRect &bounds, SkWStream *, uint32_t flags=0)
Builder & setTextShapingFactory(sk_sp< SkShapers::Factory >)
Definition SkSVGDOM.cpp:400
sk_sp< SkSVGDOM > make(SkStream &) const
Definition SkSVGDOM.cpp:405
Builder & setFontManager(sk_sp< SkFontMgr >)
Definition SkSVGDOM.cpp:390
Builder & setResourceProvider(sk_sp< skresources::ResourceProvider >)
Definition SkSVGDOM.cpp:395
static std::unique_ptr< SkStreamAsset > MakeFromFile(const char path[])
Definition SkStream.cpp:922
void append(const char text[])
Definition SkString.h:203
bool endsWith(const char suffixStr[]) const
Definition SkString.h:146
const char * c_str() const
Definition SkString.h:133
T * init(Args &&... args)
Definition SkTLazy.h:45
T * get()
Definition SkTLazy.h:83
void add(std::function< void(void)> fn)
static sk_sp< SkTypeface > MakeDeserialize(SkStream *, sk_sp< SkFontMgr > lastResortMgr)
static std::unique_ptr< BitmapRegionDecoder > Make(sk_sp< SkData > data)
GrDirectContext * directContext() const
TestContext * testContext() const
ContextInfo getContextInfo(ContextType type, ContextOverrides=ContextOverrides::kNone)
ContextInfo getSharedContextInfo(GrDirectContext *shareContext, uint32_t shareIndex=0)
T * get() const
Definition SkRefCnt.h:303
const GlobalCache * globalCache() const
Definition ContextPriv.h:40
const RendererProvider * rendererProvider() const
Definition ContextPriv.h:46
const ShaderCodeDictionary * shaderCodeDictionary() const
Definition ContextPriv.h:34
std::unique_ptr< Recorder > makeRecorder(const RecorderOptions &={})
Definition Context.cpp:130
bool submit(SyncToCpu=SyncToCpu::kNo)
Definition Context.cpp:148
bool insertRecording(const InsertRecordingInfo &)
Definition Context.cpp:142
sk_sp< GraphicsPipeline > findGraphicsPipeline(const UniqueKey &) SK_EXCLUDES(fSpinLock)
void reset(int n)
Definition SkTArray.h:139
int size() const
Definition SkTArray.h:416
void syncedSubmit(skgpu::graphite::Context *)
Builder & setResourceProvider(sk_sp< ResourceProvider >)
Definition Skottie.cpp:309
Builder & setFontManager(sk_sp< SkFontMgr >)
Definition Skottie.cpp:314
Builder & setPrecompInterceptor(sk_sp< PrecompInterceptor >)
Definition Skottie.cpp:334
Builder & setTextShapingFactory(sk_sp< SkShapers::Factory >)
Definition Skottie.cpp:344
sk_sp< Animation > makeFromFile(const char path[])
Definition Skottie.cpp:459
static sk_sp< DataURIResourceProviderProxy > Make(sk_sp< ResourceProvider > rp, ImageDecodeStrategy=ImageDecodeStrategy::kLazyDecode, sk_sp< const SkFontMgr > fontMgr=nullptr)
static sk_sp< FileResourceProvider > Make(SkString base_dir, ImageDecodeStrategy=ImageDecodeStrategy::kLazyDecode)
const Paint & paint
VkSurfaceKHR surface
Definition main.cc:49
sk_sp< SkFontMgr > fontMgr
Definition examples.cpp:32
sk_sp< SkImage > image
Definition examples.cpp:29
float SkScalar
Definition extension.cpp:12
FlutterSemanticsFlag flags
glong glong end
GAsyncResult * result
const char * name
Definition fuchsia.cc:50
double y
double x
static Result compare_bitmaps(const SkBitmap &reference, const SkBitmap &bitmap)
static SkSerialProcs serial_procs_using_png()
static bool serial_from_path_name(const SkString &path)
static void draw_to_canvas(SkCanvas *canvas, const SkImageInfo &info, void *pixels, size_t rowBytes, CodecSrc::DstColorType dstColorType, SkScalar left=0, SkScalar top=0)
static SkISize auto_compute_translate(SkMatrix *matrix, int srcW, int srcH)
static SkRect get_cull_rect_for_skp(const char *path)
static void set_bitmap_color_space(SkImageInfo *info)
ImplicitString Path
Definition DMSrcSink.h:39
static Result check_against_reference(const SkBitmap *bitmap, const Src &src, Sink *sink)
ImplicitString Name
Definition DMSrcSink.h:38
static SkString get_scaled_name(const Path &path, float scale)
static bool get_decode_info(SkImageInfo *decodeInfo, SkColorType canvasColorType, CodecSrc::DstColorType dstColorType, SkAlphaType dstAlphaType)
static Result draw_skdocument(const Src &src, SkDocument *doc, SkWStream *dst)
static void swap_rb_if_necessary(SkBitmap &bitmap, CodecSrc::DstColorType dstColorType)
std::function< DM::Result(SkCanvas *, Src::GraphiteTestContext *)> DrawToCanvasFn
SK_API sk_sp< SkImage > DeferredFromEncodedData(sk_sp< SkData > encoded, std::optional< SkAlphaType > alphaType=std::nullopt)
SK_API sk_sp< SkImage > DeferredFromGenerator(std::unique_ptr< SkImageGenerator > imageGenerator)
SK_API sk_sp< SkImage > TextureFromImage(GrDirectContext *, const SkImage *, skgpu::Mipmapped=skgpu::Mipmapped::kNo, skgpu::Budgeted=skgpu::Budgeted::kYes)
SK_API bool Read(SkStreamSeekable *src, SkDocumentPage *dstArray, int dstArrayCount, const SkDeserialProcs *=nullptr)
SK_API int ReadPageCount(SkStreamSeekable *src)
bool ReadPageSizes(SkStreamSeekable *stream, SkDocumentPage *dstArray, int dstArrayCount)
Swizzle_8888_u32 RGBA_to_BGRA
SK_API sk_sp< SkDocument > MakeDocument(SkWStream *stream, const Metadata &metadata)
SK_API SkImageInfo SwapWidthHeight(const SkImageInfo &info)
SK_API bool Encode(SkWStream *dst, const SkPixmap &src, const Options &options)
sk_sp< Factory > BestAvailable()
SK_API sk_sp< SkSurface > WrapPixels(const SkImageInfo &imageInfo, void *pixels, size_t rowBytes, const SkSurfaceProps *surfaceProps=nullptr)
SK_API sk_sp< SkSurface > RenderTarget(GrRecordingContext *context, skgpu::Budgeted budgeted, const SkImageInfo &imageInfo, int sampleCount, GrSurfaceOrigin surfaceOrigin, const SkSurfaceProps *surfaceProps, bool shouldCreateWithMips=false, bool isProtected=false)
sk_sp< SkSurface > makeSurface(SkCanvas *canvas, const SkImageInfo &info, const SkSurfaceProps *props)
bool BitmapToBase64DataURI(const SkBitmap &bitmap, SkString *dst)
sk_sp< SkFontMgr > TestFontMgr()
void FetchUniqueKeys(GlobalCache *globalCache, std::vector< UniqueKey > *keys)
bool ExtractKeyDescs(Context *context, const UniqueKey &origKey, GraphicsPipelineDesc *pipelineDesc, RenderPassDesc *renderPassDesc)
Definition dom.py:1
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition switches.h:57
Definition gen.py:1
dst
Definition cp.py:12
sk_sp< SkSurface > MakeBackendRenderTargetSurface(GrDirectContext *dContext, const SkImageInfo &ii, GrSurfaceOrigin origin, int sampleCnt, GrProtected isProtected, const SkSurfaceProps *props)
sk_sp< SkSurface > MakeBackendTextureSurface(GrDirectContext *dContext, const SkImageInfo &ii, GrSurfaceOrigin origin, int sampleCnt, skgpu::Mipmapped mipmapped, GrProtected isProtected, const SkSurfaceProps *props)
SkYUVAPixmapInfo::SupportedDataTypes SupportedTextureFormats(const GrImageContext &context)
bool Precompile(Context *context, RuntimeEffectDictionary *rteDict, const GraphicsPipelineDesc &pipelineDesc, const RenderPassDesc &renderPassDesc)
std::function< std::unique_ptr< skiagm::GM >()> GMFactory
Definition gm.h:239
DrawResult
Definition gm.h:104
Definition ref_ptr.h:256
dest
Definition zip.py:79
SkScalar w
constexpr struct @268 tiles[]
SkScalar h
int32_t height
int32_t width
void write(SkWStream *wStream, const T &text)
Definition skqp.cpp:188
const Scalar scale
virtual Result draw(const Src &, SkBitmap *, SkWStream *, SkString *log) const =0
virtual void modifyGrContextOptions(GrContextOptions *) const
Definition DMSrcSink.h:99
virtual void modifyGraphiteContextOptions(skgpu::graphite::ContextOptions *) const
Definition DMSrcSink.h:100
PersistentCache * fPersistentCache
SkExecutor * fExecutor
ShaderCacheStrategy fShaderCacheStrategy
Definition SkMD5.cpp:130
SkDeserialTypefaceProc fTypefaceProc
SkDeserialImageProc fImageProc
static constexpr SkIRect MakeWH(int32_t w, int32_t h)
Definition SkRect.h:56
void setXYWH(int32_t x, int32_t y, int32_t width, int32_t height)
Definition SkRect.h:268
static constexpr SkIRect MakeXYWH(int32_t x, int32_t y, int32_t w, int32_t h)
Definition SkRect.h:104
static constexpr SkISize Make(int32_t w, int32_t h)
Definition SkSize.h:20
constexpr int32_t width() const
Definition SkSize.h:36
constexpr int32_t height() const
Definition SkSize.h:37
SkImageInfo makeWH(int newWidth, int newHeight) const
SkImageInfo makeAlphaType(SkAlphaType newAlphaType) const
size_t minRowBytes() const
size_t computeByteSize(size_t rowBytes) const
SkImageInfo makeDimensions(SkISize newSize) const
SkColorSpace * colorSpace() const
int bytesPerPixel() const
SkISize dimensions() const
int width() const
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)
SkAlphaType alphaType() const
SkColorType colorType() const
int height() const
SkImageInfo makeColorType(SkColorType newColorType) const
SkExecutor * fExecutor
static constexpr SkRect MakeEmpty()
Definition SkRect.h:595
bool intersect(const SkRect &r)
Definition SkRect.cpp:114
static SkRect MakeIWH(int w, int h)
Definition SkRect.h:623
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition SkRect.h:659
void roundOut(SkIRect *dst) const
Definition SkRect.h:1241
static constexpr SkRect MakeSize(const SkSize &size)
Definition SkRect.h:633
static constexpr SkRect MakeWH(float w, float h)
Definition SkRect.h:609
SkSerialImageProc fImageProc
static constexpr SkSize Make(SkScalar w, SkScalar h)
Definition SkSize.h:56
bool isEmpty() const
Definition SkSize.h:71
SkScalar width() const
Definition SkSize.h:76
SkScalar height() const
Definition SkSize.h:77
GraphiteTestContext * fTestContext
skgpu::graphite::Context * fContext
SkBlendMode fMode
Definition xfermodes.cpp:52