47#include <initializer_list>
55#if defined(SK_GRAPHITE)
79 using BoundsAnalysis = FilterResult::BoundsAnalysis;
84 bool preserveDeviceState) {
85 image.draw(ctx,
device, preserveDeviceState,
nullptr);
92 FilterResult::ShaderFlags::kNone, sampleBounds);
98 if (analysis & FilterResult::BoundsAnalysis::kRequiresLayerCrop) {
104 analysis |= BoundsAnalysis::kDstBoundsNotCovered;
105 analysis |= BoundsAnalysis::kRequiresShaderTiling;
107 analysis |= BoundsAnalysis::kRequiresDecalInLayerSpace;
109 return image.getAnalyzedShaderView(ctx,
image.sampling(), analysis);
125 return m.isTranslate() &&
137 image.fBoundary == FilterResult::PixelBoundary::kTransparent) {
141 if (!(analysis & BoundsAnalysis::kHasLayerFillingEffect) &&
160 if (analysis & BoundsAnalysis::kHasLayerFillingEffect ||
168 image.fBoundary == FilterResult::PixelBoundary::kTransparent)) {
179 return image.fBoundary == FilterResult::PixelBoundary::kUnknown;
191static constexpr float kRGBTolerance = 8.f / 255.f;
192static constexpr float kAATolerance = 2.f / 255.f;
193static constexpr float kDefaultMaxAllowedPercentImageDiff = 1.f;
194static const float kFuzzyKernel[3][3] = {{0.9f, 0.9f, 0.9f},
197static_assert(std::size(kFuzzyKernel) == std::size(kFuzzyKernel[0]),
"Kernel must be square");
198static constexpr int kKernelSize = std::size(kFuzzyKernel);
200static constexpr bool kLogAllBitmaps =
false;
203 if (!actual || !expected) {
204 return !actual && !expected;
209 return actualData && actualData->equals(expectedData.
get());
231 struct TransformParams {
240 std::optional<LayerSpace<SkIRect>> fExpectedBounds;
242 struct RescaleParams {
255 , fExpectedSampling(expectedSampling)
256 , fExpectedTileMode(expectedTileMode)
257 , fExpectedColorFilter(
std::move(expectedColorFilter)) {}
259 ApplyAction(
const SkIRect& cropRect,
268 , fExpectedSampling(expectedSampling)
269 , fExpectedTileMode(expectedTileMode)
270 , fExpectedColorFilter(
std::move(expectedColorFilter)) {}
277 : fAction(
std::move(colorFilter))
279 , fExpectedSampling(expectedSampling)
280 , fExpectedTileMode(expectedTileMode)
281 , fExpectedColorFilter(
std::move(expectedColorFilter)) {}
288 : fAction(RescaleParams{
scale})
290 , fExpectedSampling(expectedSampling)
291 , fExpectedTileMode(expectedTileMode)
292 , fExpectedColorFilter(
std::move(expectedColorFilter)) {}
297 if (
auto* t = std::get_if<TransformParams>(&fAction)) {
299 return t->fMatrix.inverseMapRect(desiredOutput, &out)
301 }
else if (
auto* c = std::get_if<CropParams>(&fAction)) {
308 std::holds_alternative<RescaleParams>(fAction)) {
309 return desiredOutput;
316 if (
auto* t = std::get_if<TransformParams>(&fAction)) {
318 }
else if (
auto* c = std::get_if<CropParams>(&fAction)) {
319 return in.
applyCrop(ctx, c->fRect, c->fTileMode);
322 }
else if (
auto*
s = std::get_if<RescaleParams>(&fAction)) {
328 Expect
expectation()
const {
return fExpectation; }
330 SkTileMode expectedTileMode()
const {
return fExpectedTileMode; }
331 const SkColorFilter* expectedColorFilter()
const {
return fExpectedColorFilter.get(); }
332 bool isRescaling()
const {
return std::holds_alternative<RescaleParams>(fAction); }
334 int expectedOffscreenSurfaces()
const {
335 if (fExpectation != Expect::kNewImage) {
338 if (
auto*
s = std::get_if<RescaleParams>(&fAction)) {
339 float minScale = std::min(
s->fScale.width(),
s->fScale.height());
340 if (minScale >= 1.f - 0.001f) {
347 }
while(minScale < 0.8f);
357 if (
auto* t = std::get_if<TransformParams>(&fAction)) {
358 if (inputBounds.isEmpty()) {
361 return t->fMatrix.mapRect(inputBounds);
362 }
else if (
auto* c = std::get_if<CropParams>(&fAction)) {
363 if (c->fExpectedBounds) {
364 return *c->fExpectedBounds;
368 if (!intersection.intersect(inputBounds)) {
380 }
else if (std::holds_alternative<RescaleParams>(fAction)) {
392 Expect effectiveExpectation = fExpectation;
394 if (desiredOutput.isEmpty()) {
396 effectiveExpectation = Expect::kEmptyImage;
402 canvas.translate(-desiredOutput.left(), -desiredOutput.top());
410 if (effectiveExpectation != Expect::kEmptyImage) {
412 paint.setAntiAlias(
true);
417 if (
auto* t = std::get_if<TransformParams>(&fAction)) {
421 if (!
m.isTranslate() ||
427 }
else if (
auto* c = std::get_if<CropParams>(&fAction)) {
434 source =
source->makeSubset({imageBounds.left() - origin.x(),
435 imageBounds.top() - origin.y(),
436 imageBounds.right() - origin.x(),
437 imageBounds.bottom() - origin.y()});
438 origin = imageBounds.topLeft();
444 clear_device(paddedDevice.get());
445 paddedDevice->drawSpecial(
source.get(),
447 origin.y() - c->fRect.top()),
451 origin = c->fRect.topLeft();
453 tileMode = c->fTileMode;
455 paint.setColorFilter(*cf);
456 }
else if (
auto*
s = std::get_if<RescaleParams>(&fAction)) {
458 if (
s->fScale.width() != 1.f ||
s->fScale.height() != 1.f) {
468 clear_device(stepDevice.get());
469 stepDevice->drawSpecial(
source.get(),
479 canvas.translate(origin.x(), origin.y());
480 canvas.scale(1.f /
s->fScale.width(), 1.f /
s->fScale.height());
488 canvas.drawPaint(
paint);
495 std::variant<TransformParams,
511class FilterResultImageResolver {
521 FilterResultImageResolver(Method method) : fMethod(method) {}
523 const char* methodName()
const {
525 case Method::kImageAndOffset:
return "imageAndOffset";
526 case Method::kDrawToCanvas:
return "drawToCanvas";
527 case Method::kShader:
return "asShader";
528 case Method::kClippedShader:
return "asShaderClipped";
529 case Method::kStrictShader:
return "strictShader";
536 if (fMethod == Method::kImageAndOffset) {
539 return {resolved, origin};
542 return {
nullptr, {}};
553 if (fMethod > Method::kDrawToCanvas) {
555 if (fMethod == Method::kShader) {
562 }
else if (fMethod == Method::kClippedShader) {
567 auto [pixels, origin] = this->resolve(
575 paint.setShader(std::move(shader));
576 canvas.drawPaint(
paint);
578 SkASSERT(fMethod == Method::kDrawToCanvas);
595 using ResolveMethod = FilterResultImageResolver::Method;
603#if defined(SK_GANESH)
606 , fDirectContext(context)
614#if defined(SK_GRAPHITE)
617 , fRecorder(recorder)
618 , fBackend(
skif::MakeGraphiteBackend(recorder, {},
kColorType)) {}
632 float allowedPercentImageDiff,
633 int transparentCheckBorderTolerance) {
636 SkBitmap expectedBM = this->readPixels(expectedImage);
640 return this->compareImages(ctx, expectedBM, expectedOrigin, actual,
641 ResolveMethod::kImageAndOffset,
642 allowedPercentImageDiff, transparentCheckBorderTolerance) &&
643 this->compareImages(ctx, expectedBM, expectedOrigin, actual,
644 ResolveMethod::kDrawToCanvas,
645 allowedPercentImageDiff, transparentCheckBorderTolerance) &&
646 this->compareImages(ctx, expectedBM, expectedOrigin, actual,
647 ResolveMethod::kShader,
648 allowedPercentImageDiff, transparentCheckBorderTolerance) &&
649 this->compareImages(ctx, expectedBM, expectedOrigin, actual,
650 ResolveMethod::kClippedShader,
651 allowedPercentImageDiff, transparentCheckBorderTolerance);
655 FilterResultImageResolver expectedResolver{ResolveMethod::kStrictShader};
656 auto [expectedImage, expectedOrigin] = expectedResolver.resolve(ctx, actual);
657 SkBitmap expectedBM = this->readPixels(expectedImage.get());
658 return this->compareImages(ctx, expectedBM, expectedOrigin, actual,
659 ResolveMethod::kImageAndOffset,
665 sk_sp<SkDevice> sourceSurface = fBackend->makeDevice(size, std::move(colorSpace));
674 size.height() / 2.f});
678 canvas.concat(rotation);
685 float sz =
size.width() <= 16.f ||
size.height() <= 16.f ? 2.f : 8.f;
686 for (
float y = coverBounds.
fTop;
y < coverBounds.
fBottom;
y += sz) {
687 for (
float x = coverBounds.
fLeft;
x < coverBounds.
fRight;
x += sz) {
689 p.setColor(colors[(
color++) % std::size(colors)]);
701 float allowedPercentImageDiff,
int transparentCheckBorderTolerance) {
702 FilterResultImageResolver resolver{method};
703 auto [actualImage, actualOrigin] = resolver.resolve(ctx, actual);
705 SkBitmap actualBM = this->readPixels(actualImage.get());
707 if (!this->compareBitmaps(expected, expectedOrigin, actualBM, actualOrigin,
708 allowedPercentImageDiff, transparentCheckBorderTolerance,
710 if (!fLoggedErrorImage) {
711 SkDebugf(
"FilterResult comparison failed for method %s\n", resolver.methodName());
712 this->logBitmaps(expected, actualBM, badPixels);
713 fLoggedErrorImage =
true;
716 }
else if (kLogAllBitmaps) {
717 this->logBitmaps(expected, actualBM, badPixels);
723 bool compareBitmaps(
const SkBitmap& expected,
727 float allowedPercentImageDiff,
728 int transparentCheckBorderTolerance,
730 SkIRect excludeTransparentCheck;
731 if (actual.
empty()) {
741 const bool contained = expectedBounds.
contains(actualBounds);
743 "actual image [%d %d %d %d] not contained within expected [%d %d %d %d]",
746 expectedBounds.
fLeft, expectedBounds.
fTop,
756 for (
int y = 0;
y < actual.
height(); ++
y) {
757 for (
int x = 0;
x < actual.
width(); ++
x) {
761 if (actualColor != expectedColor &&
762 !this->approxColor(this->boxFilter(actual,
x,
y),
763 this->boxFilter(expected, ep.
fX, ep.
fY))) {
770 const int totalCount = expected.
width() * expected.
height();
771 const float percentError = 100.f * errorCount / (
float) totalCount;
772 const bool approxMatch = percentError <= allowedPercentImageDiff;
774 "%d pixels were too different from %d total (%f %% vs. %f %%)",
775 errorCount, totalCount, percentError, allowedPercentImageDiff);
782 excludeTransparentCheck = actualBounds.
makeOffset(-expectedOrigin);
786 excludeTransparentCheck.
outset(transparentCheckBorderTolerance,
787 transparentCheckBorderTolerance);
790 int badTransparencyCount = 0;
791 for (
int y = 0;
y < expected.
height(); ++
y) {
792 for (
int x = 0;
x < expected.
width(); ++
x) {
793 if (!excludeTransparentCheck.
isEmpty() && excludeTransparentCheck.
contains(
x,
y)) {
801 const bool onEdge = !excludeTransparentCheck.
isEmpty() &&
804 onEdge ? kAATolerance : 0.f)) {
806 badTransparencyCount++;
811 REPORTER_ASSERT(fReporter, badTransparencyCount == 0,
"Unexpected non-transparent pixels");
812 return badTransparencyCount == 0;
817 float tolerance = kRGBTolerance)
const {
824 float r = (apm.
fR + bpm.
fR) / 2.f;
825 float dr = (apm.
fR - bpm.
fR);
826 float dg = (apm.
fG - bpm.
fG);
827 float db = (apm.
fB - bpm.
fB);
829 return delta <= tolerance;
833 static constexpr int kKernelOffset = kKernelSize / 2;
835 float netWeight = 0.f;
836 for (
int sy =
y - kKernelOffset; sy <=
y + kKernelOffset; ++sy) {
837 for (
int sx =
x - kKernelOffset; sx <=
x + kKernelOffset; ++sx) {
838 float weight = kFuzzyKernel[sy -
y + kKernelOffset][sx -
x + kKernelOffset];
840 if (sx < 0 || sx >= bm.
width() || sy < 0 || sy >= bm.
height()) {
857 return sum.
unpremul() * (1.f / netWeight);
865 [[maybe_unused]]
int srcX = specialImage->
subset().
fLeft;
866 [[maybe_unused]]
int srcY = specialImage->
subset().
fTop;
871#if defined(SK_GANESH)
872 if (fDirectContext) {
879#if defined(SK_GRAPHITE)
884 auto proxyII = ii.
makeWH(view.width(), view.height());
886 bm.
pixmap(), view.proxy(), proxyII, srcX, srcY));
897 void logBitmaps(
const SkBitmap& expected,
904 if (!actual.
empty()) {
909 SkDebugf(
"Actual: null (fully transparent)\n\n");
912 if (!badPixels.
empty()) {
916 for (
auto p : badPixels) {
926#if defined(SK_GANESH)
929#if defined(SK_GRAPHITE)
935 bool fLoggedErrorImage =
false;
942 float allowedPercentImageDiff=kDefaultMaxAllowedPercentImageDiff,
943 int transparentCheckBorderTolerance=0)
946 , fAllowedPercentImageDiff(allowedPercentImageDiff)
947 , fTransparentCheckBorderTolerance(transparentCheckBorderTolerance)
964 std::optional<SkTileMode> expectedTileMode = {},
965 std::optional<SkIRect> expectedBounds = {}) {
967 if (!expectedTileMode) {
968 expectedTileMode = tileMode;
970 std::optional<LayerSpace<SkIRect>> expectedLayerBounds;
971 if (expectedBounds) {
974 fActions.emplace_back(crop, tileMode, expectedLayerBounds, expectation,
975 this->getDefaultExpectedSampling(expectation),
977 this->getDefaultExpectedColorFilter(expectation));
988 std::optional<SkSamplingOptions> expectedSampling = {}) {
991 if (!expectedSampling.has_value()) {
994 fActions.emplace_back(matrix, sampling, expectation, *expectedSampling,
995 this->getDefaultExpectedTileMode(expectation,
997 this->getDefaultExpectedColorFilter(expectation));
1007 if (!expectedColorFilter.has_value()) {
1009 colorFilter, this->getDefaultExpectedColorFilter(expectation));
1012 fActions.emplace_back(std::move(colorFilter), expectation,
1013 this->getDefaultExpectedSampling(expectation),
1014 this->getDefaultExpectedTileMode(expectation, affectsTransparent),
1015 std::move(*expectedColorFilter));
1021 std::optional<SkTileMode> expectedTileMode = {}) {
1023 if (!expectedTileMode) {
1024 expectedTileMode = this->getDefaultExpectedTileMode(expectation,
1028 this->getDefaultExpectedSampling(expectation),
1030 this->getDefaultExpectedColorFilter(expectation));
1034 void run(
const SkIRect& requestedOutput)
const {
1036 this->
run(requestedOutput,
true);
1037 this->
run(requestedOutput,
false);
1040 void run(
const SkIRect& requestedOutput,
bool backPropagateDesiredOutput)
const {
1044 fRunner,
SkStringPrintf(
"backpropagate output: %d", backPropagateDesiredOutput));
1047 std::vector<LayerSpace<SkIRect>> desiredOutputs;
1048 desiredOutputs.resize(fActions.size(), desiredOutput);
1049 if (!backPropagateDesiredOutput) {
1053 auto inputBounds = fSourceBounds;
1054 for (
int i = 0; i < (
int) fActions.size() - 1; ++i) {
1055 desiredOutputs[i] = fActions[i].expectedBounds(inputBounds);
1062 inputBounds = desiredOutputs[i];
1067 for (
int i = (
int) fActions.size() - 2; i >= 0; --i) {
1068 if (backPropagateDesiredOutput ||
1070 desiredOutputs[i] = fActions[i+1].requiredInput(desiredOutputs[i+1]);
1077 if (!fSourceBounds.isEmpty()) {
1080 fSourceBounds.topLeft());
1083 Context baseContext{fRunner.refBackend(),
1097 if (!expectedImage) {
1098 sk_sp<SkDevice> expectedSurface = fRunner.backend()->makeDevice({1, 1}, colorSpace);
1099 clear_device(expectedSurface.
get());
1106 for (
int i = 0; i < (
int) fActions.size(); ++i) {
1118 Expect correctedExpectation = fActions[i].expectation();
1122 expectedBounds = desiredOutputs[i];
1123 if (desiredOutputs[i].isEmpty()) {
1124 correctedExpectation = Expect::kEmptyImage;
1126 }
else if (!expectedBounds.intersect(desiredOutputs[i])) {
1131 backPropagateDesiredOutput);
1133 correctedExpectation = Expect::kEmptyImage;
1136 int numOffscreenSurfaces = fActions[i].expectedOffscreenSurfaces();
1137 int actualShaderDraws =
stats.fNumShaderBasedTilingDraws +
stats.fNumShaderClampedDraws;
1138 int expectedShaderTiledDraws = 0;
1139 bool actualNewImage =
output.image() &&
1141 switch(correctedExpectation) {
1142 case Expect::kNewImage:
1145 expectedShaderTiledDraws = numOffscreenSurfaces;
1148 case Expect::kDeferredImage:
1151 case Expect::kEmptyImage:
1157 "expected %d, got %d",
1158 numOffscreenSurfaces,
stats.fNumOffscreenSurfaces);
1159 REPORTER_ASSERT(fRunner, actualShaderDraws <= expectedShaderTiledDraws,
1160 "expected %d+%d <= %d",
1161 stats.fNumShaderBasedTilingDraws,
stats.fNumShaderClampedDraws,
1162 expectedShaderTiledDraws);
1163 const bool rescaling = fActions[i].isRescaling();
1166 ctx,
source, rescaling));
1169 ctx,
source, rescaling));
1173 auto actualBounds =
output.layerBounds();
1179 if (correctedExpectation == Expect::kDeferredImage ||
1188 fActions[i].expectedColorFilter()));
1189 if (actualShaderDraws < expectedShaderTiledDraws ||
1193 REPORTER_ASSERT(fRunner, fRunner.validateOptimizedImage(ctx, output));
1197 expectedImage = fActions[i].renderExpectedImage(ctx,
1198 std::move(expectedImage),
1201 expectedOrigin = desiredOutputs[i].topLeft();
1202 if (!fRunner.compareImages(ctx,
1203 expectedImage.
get(),
1206 fAllowedPercentImageDiff,
1207 fTransparentCheckBorderTolerance)) {
1220 if (expectation != Expect::kDeferredImage || fActions.empty()) {
1223 return fActions[fActions.size() - 1].expectedSampling();
1228 SkTileMode getDefaultExpectedTileMode(Expect expectation,
bool cfAffectsTransparency)
const {
1229 if (expectation == Expect::kNewImage && cfAffectsTransparency) {
1231 }
else if (expectation != Expect::kDeferredImage || fActions.empty()) {
1234 return fActions[fActions.size() - 1].expectedTileMode();
1240 if (expectation != Expect::kDeferredImage || fActions.empty()) {
1243 return sk_ref_sp(fActions[fActions.size() - 1].expectedColorFilter());
1249 float fAllowedPercentImageDiff;
1250 int fTransparentCheckBorderTolerance;
1258 std::vector<ApplyAction> fActions;
1281#if defined(SK_GANESH)
1282#define DEF_GANESH_TEST_SUITE(name, ctsEnforcement) \
1283 DEF_GANESH_TEST_FOR_CONTEXTS(FilterResult_ganesh_##name, \
1284 skgpu::IsNativeBackend, \
1289 TestRunner runner(r, ctxInfo.directContext()); \
1290 test_suite_##name(runner); \
1293#define DEF_GANESH_TEST_SUITE(name)
1296#if defined(SK_GRAPHITE)
1297#define DEF_GRAPHITE_TEST_SUITE(name, ctsEnforcement) \
1298 DEF_CONDITIONAL_GRAPHITE_TEST_FOR_ALL_CONTEXTS(FilterResult_graphite_##name, \
1299 skgpu::IsNativeBackend, \
1305 using namespace skgpu::graphite; \
1306 auto recorder = context->makeRecorder(); \
1307 TestRunner runner(r, recorder.get()); \
1308 test_suite_##name(runner); \
1309 std::unique_ptr<Recording> recording = recorder->snap(); \
1311 ERRORF(r, "Failed to make recording"); \
1314 InsertRecordingInfo insertInfo; \
1315 insertInfo.fRecording = recording.get(); \
1316 context->insertRecording(insertInfo); \
1317 testContext->syncedSubmit(context); \
1320#define DEF_GRAPHITE_TEST_SUITE(name)
1323#define DEF_TEST_SUITE(name, runner, ganeshCtsEnforcement, graphiteCtsEnforcement) \
1324 static void test_suite_##name(TestRunner&); \
1327 DEF_GANESH_TEST_SUITE(name, ganeshCtsEnforcement) \
1328 DEF_TEST(FilterResult_raster_##name, reporter) { \
1329 TestRunner runner(reporter); \
1330 test_suite_##name(runner); \
1332 void test_suite_##name(TestRunner& runner)
1342 TestCase(r,
"applyCrop() to empty source")
1344 .applyCrop({0, 0, 10, 10}, tm, Expect::kEmptyImage)
1345 .
run({0, 0, 20, 20});
1348 TestCase(r,
"applyTransform() to empty source")
1351 .run({10, 10, 20, 20});
1353 TestCase(r,
"applyColorFilter() to empty source")
1355 .applyColorFilter(alpha_modulate(0.5f), Expect::kEmptyImage)
1356 .run({0, 0, 10, 10});
1358 TestCase(r,
"Transparency-affecting color filter overrules empty source")
1360 .applyColorFilter(affect_transparent(
SkColors::kBlue), Expect::kNewImage,
1362 .run({0, 0, 10, 10});
1369 TestCase(r,
"applyCrop() + empty output becomes empty")
1370 .source({0, 0, 10, 10})
1371 .applyCrop({2, 2, 8, 8}, tm, Expect::kEmptyImage)
1375 TestCase(r,
"applyTransform() + empty output becomes empty")
1376 .source({0, 0, 10, 10})
1380 TestCase(r,
"applyColorFilter() + empty output becomes empty")
1381 .source({0, 0, 10, 10})
1382 .applyColorFilter(alpha_modulate(0.5f), Expect::kEmptyImage)
1385 TestCase(r,
"Transpency-affecting color filter + empty output is empty")
1386 .source({0, 0, 10, 10})
1387 .applyColorFilter(affect_transparent(
SkColors::kBlue), Expect::kEmptyImage)
1400 const Expect nonDecalExpectsNewImage = tm ==
SkTileMode::kDecal ? Expect::kDeferredImage
1401 : Expect::kNewImage;
1402 TestCase(r,
"applyCrop() contained in source and output")
1403 .source({0, 0, 20, 20})
1404 .applyCrop({8, 8, 12, 12}, tm, Expect::kDeferredImage)
1405 .
run({4, 4, 16, 16});
1407 TestCase(r,
"applyCrop() contained in source, intersects output")
1408 .source({0, 0, 20, 20})
1409 .applyCrop({4, 4, 12, 12}, tm, Expect::kDeferredImage)
1410 .
run({8, 8, 16, 16});
1412 TestCase(r,
"applyCrop() intersects source, contained in output")
1413 .source({10, 10, 20, 20})
1414 .applyCrop({4, 4, 16, 16}, tm, nonDecalExpectsNewImage)
1415 .
run({0, 0, 20, 20});
1417 TestCase(r,
"applyCrop() intersects source and output")
1418 .source({0, 0, 10, 10})
1419 .applyCrop({5, -5, 15, 5}, tm, nonDecalExpectsNewImage)
1420 .
run({7, -2, 12, 8});
1422 TestCase(r,
"applyCrop() contains source, intersects output")
1423 .source({4, 4, 16, 16})
1424 .applyCrop({0, 0, 20, 20}, tm, nonDecalExpectsNewImage)
1425 .
run({-5, -5, 18, 18});
1429 TestCase(r,
"applyCrop() intersects source, contains output")
1430 .source({0, 0, 20, 20})
1432 .
run({0, 5, 20, 15});
1434 TestCase(r,
"applyCrop() contains source and output")
1435 .source({0, 0, 10, 10})
1448 TestCase(r,
"applyCrop() disjoint from source, intersects output")
1449 .source({0, 0, 10, 10})
1450 .applyCrop({11, 11, 20, 20}, tm, Expect::kEmptyImage)
1451 .
run({0, 0, 15, 15});
1453 TestCase(r,
"applyCrop() disjoint from source, intersects output disjoint from source")
1454 .source({0, 0, 10, 10})
1455 .applyCrop({11, 11, 20, 20}, tm, Expect::kEmptyImage)
1456 .
run({12, 12, 18, 18});
1458 TestCase(r,
"applyCrop() disjoint from source and output")
1459 .source({0, 0, 10, 10})
1460 .applyCrop({12, 12, 18, 18}, tm, Expect::kEmptyImage)
1461 .
run({-1, -1, 11, 11});
1463 TestCase(r,
"applyCrop() disjoint from source and output disjoint from source")
1464 .source({0, 0, 10, 10})
1465 .applyCrop({-10, 10, -1, -1}, tm, Expect::kEmptyImage)
1466 .
run({11, 11, 20, 20});
1474 : Expect::kNewImage;
1475 TestCase(r,
"applyCrop() intersects source, disjoint from output disjoint from source")
1476 .source({0, 0, 10, 10})
1477 .applyCrop({-5, -5, 5, 5}, tm, nonDecalExpectsImage)
1478 .
run({12, 12, 18, 18});
1480 TestCase(r,
"applyCrop() intersects source, disjoint from output")
1481 .source({0, 0, 10, 10})
1482 .applyCrop({-5, -5, 5, 5}, tm, nonDecalExpectsImage)
1483 .
run({6, 6, 18, 18});
1489 TestCase(r,
"applyCrop() is empty")
1490 .source({0, 0, 10, 10})
1492 .run({0, 0, 10, 10});
1494 TestCase(r,
"applyCrop() emptiness propagates")
1495 .source({0, 0, 10, 10})
1496 .applyCrop({1, 1, 9, 9}, tm, Expect::kDeferredImage)
1498 .run({0, 0, 10, 10});
1504 TestCase(r,
"Disjoint applyCrop() after kDecal become empty")
1505 .source({0, 0, 10, 10})
1507 .applyCrop({6, 6, 10, 10}, tm, Expect::kEmptyImage)
1508 .
run({0, 0, 10, 10});
1511 TestCase(r,
"Disjoint tiling applyCrop() before kDecal is not empty and combines")
1512 .source({0, 0, 10, 10})
1513 .applyCrop({0, 0, 4, 4}, tm, Expect::kDeferredImage)
1515 .
run({0, 0, 10, 10});
1517 TestCase(r,
"Disjoint non-decal applyCrops() are not empty")
1518 .source({0, 0, 10, 10})
1519 .applyCrop({0, 0, 4, 4}, tm, Expect::kDeferredImage)
1521 : Expect::kNewImage)
1522 .
run({0, 0, 10, 10});
1529 TestCase(r,
"Decal applyCrop() always combines with any other crop")
1530 .source({0, 0, 20, 20})
1531 .applyCrop({5, 5, 15, 15}, tm, Expect::kDeferredImage)
1533 .
run({0, 0, 20, 20});
1536 TestCase(r,
"Decal applyCrop() before non-decal crop requires new image")
1537 .source({0, 0, 20, 20})
1539 .applyCrop({10, 10, 20, 20}, tm, Expect::kNewImage)
1540 .
run({0, 0, 20, 20});
1542 TestCase(r,
"Consecutive non-decal crops combine if both are clamp")
1543 .source({0, 0, 20, 20})
1544 .applyCrop({5, 5, 15, 15}, tm, Expect::kDeferredImage)
1545 .applyCrop({10, 10, 20, 20}, tm,
1547 : Expect::kNewImage)
1548 .
run({0, 0, 20, 20});
1557 TestCase(r,
"Periodic applyCrop() becomes a transform")
1558 .source({0, 0, 20, 20})
1559 .applyCrop({5, 5, 15, 15}, tm, Expect::kDeferredImage,
1561 .
run({25, 25, 35, 35});
1563 TestCase(r,
"Periodic applyCrop() with partial transparency still becomes a transform")
1564 .source({0, 0, 20, 20})
1565 .applyCrop({-5, -5, 15, 15}, tm, Expect::kDeferredImage,
1569 .
run({15, 15, 35, 35});
1571 TestCase(r,
"Periodic applyCrop() after complex transform can still simplify")
1572 .source({0, 0, 20, 20})
1574 .applyCrop({-5, -5, 25, 25}, tm, Expect::kDeferredImage,
1577 .
run({55,55,85,85});
1581 TestCase(r,
"Periodic applyCrop() with visible edge does not become a transform")
1582 .source({0, 0, 20, 20})
1583 .applyCrop({5, 5, 15, 15}, tm, Expect::kDeferredImage)
1584 .
run({10, 10, 20, 20});
1586 TestCase(r,
"Periodic applyCrop() with visible edge and transparency creates new image")
1587 .source({0, 0, 20, 20})
1588 .applyCrop({-5, -5, 15, 15}, tm, Expect::kNewImage)
1589 .
run({10, 10, 20, 20});
1591 TestCase(r,
"Periodic applyCropp() with visible edge and complex transform creates image")
1592 .source({0, 0, 20, 20})
1594 .applyCrop({-5, -5, 25, 25}, tm, Expect::kNewImage)
1595 .
run({20, 20, 50, 50});
1600 TestCase(r,
"Decal then clamp crop uses 1px buffer around intersection")
1601 .source({0, 0, 20, 20})
1603 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
1605 .
run({0, 0, 20, 20});
1607 TestCase(r,
"Decal then clamp crop uses 1px buffer around intersection, w/ alpha color filter")
1608 .source({0, 0, 20, 20})
1610 .applyColorFilter(affect_transparent(
SkColors::kCyan), Expect::kDeferredImage)
1612 .
run({0, 0, 20, 20});
1619 TestCase(r,
"applyTransform() integer translate")
1620 .source({0, 0, 10, 10})
1622 .run({0, 0, 10, 10});
1624 TestCase(r,
"applyTransform() fractional translate")
1625 .source({0, 0, 10, 10})
1627 .run({0, 0, 10, 10});
1629 TestCase(r,
"applyTransform() scale")
1630 .source({0, 0, 24, 24})
1632 .run({-16, -16, 96, 96});
1635 TestCase(r,
"applyTransform() with complex transform")
1636 .source({0, 0, 8, 8})
1638 .run({0, 0, 16, 16});
1643 TestCase(r,
"linear + linear combine")
1644 .source({0, 0, 8, 8})
1649 .run({0, 0, 16, 16});
1651 TestCase(r,
"equiv. bicubics combine")
1652 .source({0, 0, 8, 8})
1657 .run({0, 0, 16, 16});
1659 TestCase(r,
"linear + bicubic becomes bicubic")
1660 .source({0, 0, 8, 8})
1665 .run({0, 0, 16, 16});
1667 TestCase(r,
"bicubic + linear becomes bicubic")
1668 .source({0, 0, 8, 8})
1674 .run({0, 0, 16, 16});
1676 TestCase(r,
"aniso picks max level to combine")
1677 .source({0, 0, 8, 8})
1683 .run({0, 0, 16, 16});
1685 TestCase(r,
"aniso picks max level to combine (other direction)")
1686 .source({0, 0, 8, 8})
1691 .run({0, 0, 16, 16});
1693 TestCase(r,
"linear + aniso becomes aniso")
1694 .source({0, 0, 8, 8})
1699 .run({0, 0, 16, 16});
1701 TestCase(r,
"aniso + linear stays aniso")
1702 .source({0, 0, 8, 8})
1708 .run({0, 0, 16, 16});
1716 TestCase(r,
"different bicubics do not combine")
1717 .source({0, 0, 8, 8})
1722 .run({0, 0, 16, 16});
1724 TestCase(r,
"nearest + linear do not combine")
1725 .source({0, 0, 8, 8})
1730 .run({0, 0, 16, 16});
1732 TestCase(r,
"linear + nearest do not combine")
1733 .source({0, 0, 8, 8})
1738 .run({0, 0, 16, 16});
1740 TestCase(r,
"bicubic + aniso do not combine")
1741 .source({0, 0, 8, 8})
1746 .run({0, 0, 16, 16});
1748 TestCase(r,
"aniso + bicubic do not combine")
1749 .source({0, 0, 8, 8})
1754 .run({0, 0, 16, 16});
1756 TestCase(r,
"nearest + nearest do not combine")
1757 .source({0, 0, 8, 8})
1762 .run({0, 0, 16, 16});
1769 TestCase(r,
"integer translate+NN then bicubic combines")
1770 .source({0, 0, 8, 8})
1776 .run({0, 0, 16, 16});
1778 TestCase(r,
"bicubic then integer translate+NN combines")
1779 .source({0, 0, 8, 8})
1785 .run({0, 0, 16, 16});
1793 TestCase(r,
"Transform moves src image outside of requested output")
1794 .source({0, 0, 8, 8})
1798 TestCase(r,
"Transform moves src image outside of crop")
1799 .source({0, 0, 8, 8})
1801 .applyCrop({2, 2, 6, 6}, Expect::kEmptyImage)
1802 .
run({0, 0, 20, 20});
1804 TestCase(r,
"Transform moves cropped image outside of requested output")
1805 .source({0, 0, 8, 8})
1806 .applyCrop({1, 1, 4, 4}, Expect::kDeferredImage)
1812 TestCase(r,
"Crop after transform can always apply")
1813 .source({0, 0, 16, 16})
1815 .applyCrop({2, 2, 15, 15}, Expect::kDeferredImage)
1816 .
run({0, 0, 16, 16});
1820 TestCase(r,
"Crop after translate is lifted to image subset")
1821 .source({0, 0, 32, 32})
1823 .applyCrop({16, 16, 24, 24}, Expect::kDeferredImage)
1825 .run({0, 0, 32, 32});
1827 TestCase(r,
"Transform after unlifted crop triggers new image")
1828 .source({0, 0, 16, 16})
1830 .applyCrop({1, 1, 15, 15}, Expect::kDeferredImage)
1832 .run({0, 0, 16, 16});
1834 TestCase(r,
"Transform after unlifted crop with interior output does not trigger new image")
1835 .source({0, 0, 16, 16})
1837 .applyCrop({1, 1, 15, 15}, Expect::kDeferredImage)
1839 .run({4, 4, 12, 12});
1841 TestCase(r,
"Translate after unlifted crop does not trigger new image")
1842 .source({0, 0, 16, 16})
1844 .applyCrop({2, 2, 14, 14}, Expect::kDeferredImage)
1846 .run({0, 0, 16, 16});
1848 TestCase(r,
"Transform after large no-op crop does not trigger new image")
1849 .source({0, 0, 64, 64})
1851 .applyCrop({-64, -64, 128, 128}, Expect::kDeferredImage)
1853 .run({0, 0, 64, 64});
1863 TestCase(r,
"Transform after tile mode does not trigger new image")
1864 .source({0, 0, 64, 64})
1865 .applyCrop({2, 2, 32, 32}, tm, Expect::kDeferredImage)
1867 .run({0, 0, 64, 64});
1869 TestCase(r,
"Integer transform before tile mode does not trigger new image")
1870 .source({0, 0, 32, 32})
1872 .applyCrop({20, 20, 40, 40}, tm, Expect::kDeferredImage)
1873 .
run({0, 0, 64, 64});
1875 TestCase(r,
"Non-integer transform before tile mode triggers new image")
1876 .source({0, 0, 50, 40})
1878 .applyCrop({10, 10, 30, 30}, tm, Expect::kNewImage)
1879 .
run({0, 0, 50, 50});
1881 TestCase(r,
"Non-integer transform before tiling defers image if edges are hidden")
1882 .source({0, 0, 64, 64})
1884 .applyCrop({10, 10, 50, 50}, tm, Expect::kDeferredImage,
1886 .
run({11, 11, 49, 49});
1894 TestCase(r,
"applyColorFilter() defers image")
1895 .source({0, 0, 24, 24})
1896 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
1897 .run({0, 0, 32, 32});
1899 TestCase(r,
"applyColorFilter() composes with other color filters")
1900 .source({0, 0, 24, 24})
1901 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
1902 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
1903 .run({0, 0, 32, 32});
1905 TestCase(r,
"Transparency-affecting color filter fills output")
1906 .source({0, 0, 24, 24})
1907 .applyColorFilter(affect_transparent(
SkColors::kBlue), Expect::kDeferredImage)
1908 .run({-8, -8, 32, 32});
1912 TestCase(r,
"Transparency-affecting composition fills output (ATBx2)")
1913 .source({0, 0, 24, 24})
1914 .applyColorFilter(affect_transparent(
SkColors::kBlue), Expect::kDeferredImage)
1915 .applyColorFilter(affect_transparent(
SkColors::kRed), Expect::kDeferredImage)
1916 .run({-8, -8, 32, 32});
1918 TestCase(r,
"Transparency-affecting composition fills output (ATB,reg)")
1919 .source({0, 0, 24, 24})
1920 .applyColorFilter(affect_transparent(
SkColors::kBlue), Expect::kDeferredImage)
1921 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
1922 .run({-8, -8, 32, 32});
1924 TestCase(r,
"Transparency-affecting composition fills output (reg,ATB)")
1925 .source({0, 0, 24, 24})
1926 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
1927 .applyColorFilter(affect_transparent(
SkColors::kBlue), Expect::kDeferredImage)
1928 .run({-8, -8, 32, 32});
1933 TestCase(r,
"Transform composes with regular CF")
1934 .source({0, 0, 24, 24})
1936 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
1937 .run({0, 0, 24, 24});
1939 TestCase(r,
"Regular CF composes with transform")
1940 .source({0, 0, 24, 24})
1941 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
1943 .run({0, 0, 24, 24});
1945 TestCase(r,
"Transform composes with transparency-affecting CF")
1946 .source({0, 0, 24, 24})
1948 .applyColorFilter(affect_transparent(
SkColors::kBlue), Expect::kDeferredImage)
1949 .run({0, 0, 24, 24});
1955 TestCase(r,
"Transparency-affecting CF composes with transform")
1956 .source({0, 0, 24, 24})
1957 .applyColorFilter(affect_transparent(
SkColors::kBlue), Expect::kDeferredImage)
1959 .run({-50, -50, 50, 50});
1965 TestCase(r,
"Transform between regular color filters")
1966 .source({0, 0, 24, 24})
1967 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
1969 .applyColorFilter(alpha_modulate(0.75f), Expect::kDeferredImage)
1970 .run({0, 0, 24, 24});
1972 TestCase(r,
"Transform between transparency-affecting color filters")
1973 .source({0, 0, 24, 24})
1974 .applyColorFilter(affect_transparent(
SkColors::kBlue), Expect::kDeferredImage)
1976 .applyColorFilter(affect_transparent(
SkColors::kGreen), Expect::kDeferredImage)
1977 .run({0, 0, 24, 24});
1979 TestCase(r,
"Transform between ATB and regular color filters")
1980 .source({0, 0, 24, 24})
1981 .applyColorFilter(affect_transparent(
SkColors::kBlue), Expect::kDeferredImage)
1983 .applyColorFilter(alpha_modulate(0.75f), Expect::kDeferredImage)
1984 .run({0, 0, 24, 24});
1986 TestCase(r,
"Transform between regular and ATB color filters")
1987 .source({0, 0, 24, 24})
1988 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
1990 .applyColorFilter(affect_transparent(
SkColors::kGreen), Expect::kDeferredImage)
1991 .run({0, 0, 24, 24});
1996 TestCase(r,
"Regular color filter between transforms")
1997 .source({0, 0, 24, 24})
1999 .applyColorFilter(alpha_modulate(0.8f), Expect::kDeferredImage)
2001 .run({0, 0, 24, 24});
2003 TestCase(r,
"Transparency-affecting color filter between transforms")
2004 .source({0, 0, 24, 24})
2006 .applyColorFilter(affect_transparent(
SkColors::kRed), Expect::kDeferredImage)
2008 .run({0, 0, 24, 24});
2013 TestCase(r,
"Regular color filter after empty crop stays empty")
2014 .source({0, 0, 16, 16})
2016 .applyColorFilter(alpha_modulate(0.2f), Expect::kEmptyImage)
2017 .run({0, 0, 16, 16});
2019 TestCase(r,
"Transparency-affecting color filter after empty crop creates new image")
2020 .source({0, 0, 16, 16})
2022 .applyColorFilter(affect_transparent(
SkColors::kRed), Expect::kNewImage,
2024 .run({0, 0, 16, 16});
2026 TestCase(r,
"Regular color filter composes with crop")
2027 .source({0, 0, 32, 32})
2028 .applyColorFilter(alpha_modulate(0.7f), Expect::kDeferredImage)
2029 .applyCrop({8, 8, 24, 24}, tm, Expect::kDeferredImage)
2030 .
run({0, 0, 32, 32});
2032 TestCase(r,
"Crop composes with regular color filter")
2033 .source({0, 0, 32, 32})
2034 .applyCrop({8, 8, 24, 24}, tm, Expect::kDeferredImage)
2035 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
2036 .run({0, 0, 32, 32});
2039 TestCase(r,
"Transparency-affecting color filter restricted by crop")
2040 .source({0, 0, 32, 32})
2041 .applyColorFilter(affect_transparent(
SkColors::kRed), Expect::kDeferredImage)
2042 .applyCrop({8, 8, 24, 24}, tm, Expect::kDeferredImage)
2043 .
run({0, 0, 32, 32});
2045 TestCase(r,
"Crop composes with transparency-affecting color filter")
2046 .source({0, 0, 32, 32})
2047 .applyCrop({8, 8, 24, 24}, tm, Expect::kDeferredImage)
2048 .applyColorFilter(affect_transparent(
SkColors::kRed), Expect::kDeferredImage)
2049 .run({0, 0, 32, 32});
2056 TestCase(r,
"Crop between regular color filters")
2057 .source({0, 0, 32, 32})
2058 .applyColorFilter(alpha_modulate(0.8f), Expect::kDeferredImage)
2059 .applyCrop({8, 8, 24, 24}, tm, Expect::kDeferredImage)
2060 .applyColorFilter(alpha_modulate(0.4f), Expect::kDeferredImage)
2061 .run({0, 0, 32, 32});
2064 TestCase(r,
"Crop between transparency-affecting color filters requires new image")
2065 .source({0, 0, 32, 32})
2066 .applyColorFilter(affect_transparent(
SkColors::kGreen), Expect::kDeferredImage)
2068 .applyColorFilter(affect_transparent(
SkColors::kRed), Expect::kNewImage)
2069 .run({0, 0, 32, 32});
2071 TestCase(r,
"Output-constrained crop between transparency-affecting filters does not")
2072 .source({0, 0, 32, 32})
2073 .applyColorFilter(affect_transparent(
SkColors::kGreen), Expect::kDeferredImage)
2075 .applyColorFilter(affect_transparent(
SkColors::kRed), Expect::kDeferredImage)
2076 .run({8, 8, 24, 24});
2078 TestCase(r,
"Tiling between transparency-affecting color filters defers image")
2079 .source({0, 0, 32, 32})
2080 .applyColorFilter(affect_transparent(
SkColors::kGreen), Expect::kDeferredImage)
2081 .applyCrop({8, 8, 24, 24}, tm, Expect::kDeferredImage)
2082 .applyColorFilter(affect_transparent(
SkColors::kRed), Expect::kDeferredImage)
2083 .run({0, 0, 32, 32});
2086 TestCase(r,
"Crop between regular and ATB color filters")
2087 .source({0, 0, 32, 32})
2088 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
2089 .applyCrop({8, 8, 24, 24}, tm, Expect::kDeferredImage)
2090 .applyColorFilter(affect_transparent(
SkColors::kRed), Expect::kDeferredImage)
2091 .run({0, 0, 32, 32});
2093 TestCase(r,
"Crop between ATB and regular color filters")
2094 .source({0, 0, 32, 32})
2095 .applyColorFilter(affect_transparent(
SkColors::kRed), Expect::kDeferredImage)
2096 .applyCrop({8, 8, 24, 24}, tm, Expect::kDeferredImage)
2097 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
2098 .run({0, 0, 32, 32});
2106 Expect newImageIfNotDecalOrDoubleClamp =
2109 Expect::kNewImage : Expect::kDeferredImage;
2111 TestCase(r,
"Regular color filter between crops")
2112 .source({0, 0, 32, 32})
2113 .applyCrop({4, 4, 24, 24}, firstTM, Expect::kDeferredImage)
2114 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
2115 .applyCrop({15, 15, 32, 32}, secondTM, newImageIfNotDecalOrDoubleClamp,
2117 .
run({0, 0, 32, 32});
2119 TestCase(r,
"Transparency-affecting color filter between crops")
2120 .source({0, 0, 32, 32})
2121 .applyCrop({4, 4, 24, 24}, firstTM, Expect::kDeferredImage)
2122 .applyColorFilter(affect_transparent(
SkColors::kGreen), Expect::kDeferredImage)
2123 .applyCrop({15, 15, 32, 32}, secondTM, newImageIfNotDecalOrDoubleClamp,
2125 .
run({0, 0, 32, 32});
2132 TestCase(r,
"Transform -> crop -> regular color filter")
2133 .source({0, 0, 32, 32})
2135 .applyCrop({2, 2, 30, 30}, Expect::kDeferredImage)
2136 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
2137 .run({0, 0, 32, 32});
2139 TestCase(r,
"Transform -> regular color filter -> crop")
2140 .source({0, 0, 32, 32})
2142 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
2143 .applyCrop({2, 2, 30, 30}, Expect::kDeferredImage)
2144 .
run({0, 0, 32, 32});
2146 TestCase(r,
"Crop -> transform -> regular color filter")
2147 .source({0, 0, 32, 32})
2148 .applyCrop({2, 2, 30, 30}, Expect::kDeferredImage)
2150 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
2151 .run({0, 0, 32, 32});
2153 TestCase(r,
"Crop -> regular color filter -> transform")
2154 .source({0, 0, 32, 32})
2155 .applyCrop({2, 2, 30, 30}, Expect::kDeferredImage)
2156 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
2158 .run({0, 0, 32, 32});
2160 TestCase(r,
"Regular color filter -> transform -> crop")
2161 .source({0, 0, 32, 32})
2162 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
2164 .applyCrop({2, 2, 30, 30}, Expect::kDeferredImage)
2165 .
run({0, 0, 32, 32});
2167 TestCase(r,
"Regular color filter -> crop -> transform")
2168 .source({0, 0, 32, 32})
2169 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
2170 .applyCrop({2, 2, 30, 30}, Expect::kDeferredImage)
2172 .run({0, 0, 32, 32});
2180 TestCase(r,
"Transform -> transparency-affecting color filter -> crop")
2181 .source({0, 0, 32, 32})
2183 .applyColorFilter(affect_transparent(
SkColors::kGreen), Expect::kDeferredImage)
2184 .applyCrop({2, 2, 30, 30}, Expect::kDeferredImage)
2185 .
run({0, 0, 32, 32});
2187 TestCase(r,
"Crop -> transform -> transparency-affecting color filter")
2188 .source({0, 0, 32, 32})
2189 .applyCrop({2, 2, 30, 30}, Expect::kDeferredImage)
2191 .applyColorFilter(affect_transparent(
SkColors::kGreen), Expect::kDeferredImage)
2192 .run({0, 0, 32, 32});
2194 TestCase(r,
"Crop -> transparency-affecting color filter -> transform")
2195 .source({0, 0, 32, 32})
2196 .applyCrop({2, 2, 30, 30}, Expect::kDeferredImage)
2197 .applyColorFilter(affect_transparent(
SkColors::kGreen), Expect::kDeferredImage)
2199 .run({0, 0, 32, 32});
2201 TestCase(r,
"Transparency-affecting color filter -> transform -> crop")
2202 .source({0, 0, 32, 32})
2203 .applyColorFilter(affect_transparent(
SkColors::kGreen), Expect::kDeferredImage)
2205 .applyCrop({2, 2, 30, 30}, Expect::kDeferredImage)
2206 .
run({0, 0, 32, 32});
2211 TestCase(r,
"Transform -> crop -> transparency-affecting color filter")
2212 .source({0, 0, 32, 32})
2214 .applyCrop({2, 2, 30, 30}, Expect::kDeferredImage)
2216 .run({0, 0, 32, 32});
2218 TestCase(r,
"Transparency-affecting color filter -> crop -> transform")
2219 .source({0, 0, 32, 32})
2220 .applyColorFilter(affect_transparent(
SkColors::kGreen), Expect::kDeferredImage)
2221 .applyCrop({2, 2, 30, 30}, Expect::kDeferredImage)
2223 .run({0, 0, 32, 32});
2227 TestCase(r,
"Transform -> crop -> transparency-affecting color filter")
2228 .source({0, 0, 32, 32})
2230 .applyCrop({2, 2, 30, 30}, Expect::kDeferredImage)
2231 .applyColorFilter(affect_transparent(
SkColors::kGreen), Expect::kDeferredImage)
2232 .run({15, 15, 21, 21});
2234 TestCase(r,
"Transparency-affecting color filter -> crop -> transform")
2235 .source({0, 0, 32, 32})
2236 .applyColorFilter(affect_transparent(
SkColors::kGreen), Expect::kDeferredImage)
2237 .applyCrop({2, 2, 30, 30}, Expect::kDeferredImage)
2239 .run({15, 15, 21, 21});
2256 TestCase(r,
"Layer decal shouldn't be visible")
2257 .source({65, 0, 199, 200})
2260 -0.173648f, 0.984808f, 17.3648f,
2261 0.000000f, 0.000000f, 1.0000f),
2262 Expect::kDeferredImage)
2264 .
run({-15, -15, 115, 215});
2268static constexpr SkSize kNearlyIdentity = {0.999f, 0.999f};
2273 TestCase(r,
"Identity rescale is a no-op")
2274 .source({0, 0, 50, 50})
2275 .applyCrop({0, 0, 50, 50}, tm, Expect::kDeferredImage)
2276 .rescale({1.f, 1.f}, Expect::kDeferredImage)
2277 .
run({-5, -5, 55, 55});
2279 TestCase(r,
"Near identity rescale is a no-op",
2280 kDefaultMaxAllowedPercentImageDiff,
2283 .applyCrop({0, 0, 50, 50}, tm, Expect::kDeferredImage)
2284 .rescale(kNearlyIdentity, Expect::kDeferredImage)
2285 .run({-5, -5, 55, 55});
2300 TestCase(r,
"1-step rescale preserves tile mode",
2301 kDefaultMaxAllowedPercentImageDiff,
2303 .
source({16, 16, 64, 64})
2304 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2305 .rescale({0.5f, 0.5f}, Expect::kNewImage, expectedTileMode)
2306 .
run({0, 0, 80, 80});
2309 TestCase(r,
"2-step rescale preserves tile mode",
2311 : periodic ? 2.5f : 1.f,
2313 .
source({16, 16, 64, 64})
2314 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2315 .rescale({0.25f, 0.25f}, Expect::kNewImage, expectedTileMode)
2316 .
run({0, 0, 80, 80});
2318 TestCase(r,
"2-step rescale with near-identity elision",
2320 : periodic ? 73.85 : 52.2f,
2322 .
source({16, 16, 64, 64})
2323 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2324 .rescale({0.22f, 0.22f}, Expect::kNewImage, expectedTileMode)
2325 .
run({0, 0, 80, 80});
2327 TestCase(r,
"3-step rescale preserves tile mode",
2329 : periodic ? 90.5f : 71.f,
2331 .
source({16, 16, 64, 64})
2332 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2333 .rescale({0.155f, 0.155f}, Expect::kNewImage, expectedTileMode)
2334 .
run({0, 0, 80, 80});
2337 TestCase(r,
"Identity X axis, near-identity Y axis is a no-op",
2338 kDefaultMaxAllowedPercentImageDiff,
2340 .
source({16, 16, 64, 64})
2341 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2342 .rescale({1.f, kNearlyIdentity.
height()}, Expect::kDeferredImage)
2343 .
run({0, 0, 80, 80});
2344 TestCase(r,
"Near-identity X axis, identity Y axis is a no-op",
2345 kDefaultMaxAllowedPercentImageDiff,
2347 .
source({16, 16, 64, 64})
2348 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2349 .rescale({kNearlyIdentity.
width(), 1.f}, Expect::kDeferredImage)
2350 .
run({0, 0, 80, 80});
2352 TestCase(r,
"Identity X axis, 1-step Y axis preserves tile mode",
2355 .
source({16, 16, 64, 64})
2356 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2357 .rescale({1.f, 0.5f}, Expect::kNewImage, expectedTileMode)
2358 .
run({0, 0, 80, 80});
2359 TestCase(r,
"Near-identity X axis, 1-step Y axis preserves tile mode",
2362 .
source({16, 16, 64, 64})
2363 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2364 .rescale({kNearlyIdentity.
width(), 0.5f}, Expect::kNewImage, expectedTileMode)
2365 .
run({0, 0, 80, 80});
2366 TestCase(r,
"Identity X axis, 2-step Y axis preserves tile mode",
2369 .
source({16, 16, 64, 64})
2370 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2371 .rescale({1.f, 0.25f}, Expect::kNewImage, expectedTileMode)
2372 .
run({0, 0, 80, 80});
2373 TestCase(r,
"1-step X axis, 2-step Y axis preserves tile mode",
2375 : periodic ? 55.f : 29.5f,
2377 .
source({16, 16, 64, 64})
2378 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2379 .rescale({.55f, 0.27f}, Expect::kNewImage, expectedTileMode)
2380 .
run({0, 0, 80, 80});
2382 TestCase(r,
"1-step X axis, identity Y axis preserves tile mode",
2385 .
source({16, 16, 64, 64})
2386 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2387 .rescale({0.5f, 1.f}, Expect::kNewImage, expectedTileMode)
2388 .
run({0, 0, 80, 80});
2389 TestCase(r,
"1-step X axis, near-identity Y axis preserves tile mode",
2392 .
source({16, 16, 64, 64})
2393 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2394 .rescale({0.5f, kNearlyIdentity.
height()}, Expect::kNewImage, expectedTileMode)
2395 .
run({0, 0, 80, 80});
2396 TestCase(r,
"2-step X axis, identity Y axis preserves tile mode",
2399 .
source({16, 16, 64, 64})
2400 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2401 .rescale({0.25f, 1.f}, Expect::kNewImage, expectedTileMode)
2402 .
run({0, 0, 80, 80});
2403 TestCase(r,
"2-step X axis, 1-step Y axis preserves tile mode",
2405 : periodic ? 55.f : 29.5f,
2407 .
source({16, 16, 64, 64})
2408 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2409 .rescale({.27f, 0.55f}, Expect::kNewImage, expectedTileMode)
2410 .
run({0, 0, 80, 80});
2416 TestCase(r,
"Rescale applies layer bounds",
2417 kDefaultMaxAllowedPercentImageDiff,
2419 .source({16, 16, 64, 64})
2420 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2424 .
run({0, 0, 80, 80});
2431 TestCase(r,
"Identity rescale defers integer translation")
2432 .source({0, 0, 50, 50})
2433 .applyCrop({0, 0, 50, 50}, tm, Expect::kDeferredImage)
2435 .rescale({1.f, 1.f}, Expect::kDeferredImage)
2436 .
run({-15, -15, 45, 45});
2438 TestCase(r,
"Identity rescale applies complex transform")
2439 .source({16, 16, 64, 64})
2440 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2443 .
run({0, 0, 80, 80});
2445 TestCase(r,
"Near-identity rescale defers integer translation",
2446 kDefaultMaxAllowedPercentImageDiff,
2449 .applyCrop({0, 0, 50, 50}, tm, Expect::kDeferredImage)
2451 .rescale(kNearlyIdentity, Expect::kDeferredImage)
2452 .run({-15, -15, 45, 45});
2454 TestCase(r,
"Near-identity rescale applies complex transform")
2455 .source({0, 0, 50, 50})
2456 .applyCrop({0, 0, 50, 50}, tm, Expect::kDeferredImage)
2459 .run({-5, -5, 55, 55});
2461 TestCase(r,
"1-step rescale applies complex transform in first step",
2462 kDefaultMaxAllowedPercentImageDiff,
2464 .
source({16, 16, 64, 64})
2465 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2468 .
run({0, 0, 80, 80});
2471 TestCase(r,
"2-step rescale applies complex transform",
2472 periodic ? 6.80f : 1.61f,
2474 .
source({16, 16, 64, 64})
2475 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2478 .
run({0, 0, 80, 80});
2490 TestCase(r,
"Identity rescale applies color filter but defers tile mode")
2491 .source({0, 0, 50, 50})
2492 .applyCrop({0, 0, 50, 50}, tm, Expect::kDeferredImage)
2493 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
2494 .rescale({1.f, 1.f}, Expect::kNewImage, expectedTileMode)
2495 .
run({-5, -5, 55, 55});
2497 TestCase(r,
"Near-identity rescale applies color filter but defers tile mode",
2498 kDefaultMaxAllowedPercentImageDiff,
2501 .applyCrop({0, 0, 50, 50}, tm, Expect::kDeferredImage)
2502 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
2503 .rescale(kNearlyIdentity, Expect::kNewImage, expectedTileMode)
2504 .run({-5, -5, 55, 55});
2506 TestCase(r,
"Rescale applies color filter but defers tile mode",
2507 kDefaultMaxAllowedPercentImageDiff,
2509 .
source({16, 16, 64, 64})
2510 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2511 .applyColorFilter(alpha_modulate(0.75f), Expect::kDeferredImage)
2512 .rescale({0.5f, 0.5f}, Expect::kNewImage, expectedTileMode)
2513 .
run({0, 0, 80, 80});
2515 TestCase(r,
"Rescale applies transparency-affecting color filter but defers tile mode")
2516 .source({16, 16, 64, 64})
2517 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2518 .applyColorFilter(affect_transparent(
SkColors::kGreen), Expect::kDeferredImage)
2519 .rescale({0.5f, 0.5f}, Expect::kNewImage, expectedTileMode)
2520 .
run({0, 0, 80, 80});
2525 static constexpr SkISize kSrcSize = {128,128};
2526 static constexpr SkIRect kIdentitySrc = {0,0,128,128};
2527 static constexpr SkIRect kSubsetSrc = {16,16,112,112};
2528 static constexpr SkIRect kOverlappingSrc = {-64, 16, 192, 112};
2529 static constexpr SkIRect kContainingSrc = {-64,-64,192,192};
2530 static constexpr SkIRect kDisjointSrc = {0,-200,128,-1};
2534 static constexpr SkIRect kDstRect = kIdentitySrc;
2537 static constexpr SkIRect kDesiredOutput = {-400, -400, 400, 400};
2559 "Empty dst rect returns empty FilterResult");
2561 "Empty src rect returns empty FilterResult");
2563 "Disjoint src rect returns empty FilterREsult");
2567 const char* label) {
2568 auto result = makeImage(src, kDstRect);
2571 "Result subset is incorrect: %s", label);
2573 "Result layer bounds are incorrect: %s", label);
2576 testSuccess(kIdentitySrc, kIdentitySrc, kDstRect,
2577 "Identity src->dst preserves original image bounds");
2578 testSuccess(kSubsetSrc, kSubsetSrc, kDstRect,
2579 "Contained src rect is preserved, stretched to original dst bounds");
2580 testSuccess(kOverlappingSrc, {0,16,128,112}, {32,0,96,128},
2581 "Overlapping src rect is clipped and dst is scaled on clipped axis");
2582 testSuccess(kContainingSrc, kIdentitySrc, {32,32,96,96},
2583 "Containing src rect is clipped and dst is scaled on both axes");
#define DEF_TEST_SUITE(name, runner, ganeshCtsEnforcement, graphiteCtsEnforcement)
@ kTopLeft_GrSurfaceOrigin
#define SkAssertResult(cond)
static SkColorFilterBase * as_CFB(SkColorFilter *filter)
@ kRGBA_8888_SkColorType
pixel with 8 bits for red, green, blue, alpha; in 32-bit word
constexpr SkColor SK_ColorYELLOW
constexpr SkColor SK_ColorMAGENTA
constexpr SkColor SK_ColorCYAN
constexpr SkColor SK_ColorTRANSPARENT
constexpr SkColor SK_ColorBLUE
constexpr SkColor SK_ColorRED
constexpr SkColor SK_ColorBLACK
constexpr SkColor SK_ColorGREEN
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
#define sk_float_ceil2int(x)
static bool apply(Pass *pass, SkRecord *record)
sk_sp< T > sk_ref_sp(T *obj)
static bool SkScalarIsInt(SkScalar x)
SK_API SkString static SkString SkStringPrintf()
static constexpr bool SkToBool(const T &x)
#define REPORTER_ASSERT(r, cond,...)
static constexpr auto kColorType
Type::kYUV Type::kRGBA() int(0.7 *637)
static void Draw(const skif::Context &ctx, SkDevice *device, const skif::FilterResult &image, bool preserveDeviceState)
static void TrackStats(skif::Context *ctx, skif::Stats *stats)
static bool IsShaderTilingExpected(const skif::Context &ctx, const skif::FilterResult &image, bool rescaling)
static skif::FilterResult Rescale(const skif::Context &ctx, const skif::FilterResult &image, const skif::LayerSpace< SkSize > scale)
static bool IsIntegerTransform(const skif::FilterResult &image)
static sk_sp< SkShader > StrictShader(const skif::Context &ctx, const skif::FilterResult &image)
static sk_sp< SkShader > AsShader(const skif::Context &ctx, const skif::FilterResult &image, const skif::LayerSpace< SkIRect > &sampleBounds)
static bool IsShaderClampingExpected(const skif::Context &ctx, const skif::FilterResult &image, bool rescaling)
void allocPixels(const SkImageInfo &info, size_t rowBytes)
sk_sp< SkImage > asImage() const
const SkPixmap & pixmap() const
SkColor4f getColor4f(int x, int y) const
bool readPixels(const SkImageInfo &dstInfo, void *dstPixels, size_t dstRowBytes, int srcX, int srcY) const
void clear(SkColor color)
bool affectsTransparentBlack() const
static sk_sp< SkColorFilter > Compose(const sk_sp< SkColorFilter > &outer, sk_sp< SkColorFilter > inner)
static sk_sp< SkColorFilter > Blend(const SkColor4f &c, sk_sp< SkColorSpace >, SkBlendMode mode)
static sk_sp< SkColorSpace > MakeSRGB()
sk_sp< SkData > serialize(const SkSerialProcs *=nullptr) const
bool readPixels(GrDirectContext *context, const SkImageInfo &dstInfo, void *dstPixels, size_t dstRowBytes, int srcX, int srcY, CachingHint cachingHint=kAllow_CachingHint) const
static bool InverseMapRect(const SkMatrix &mx, SkRect *dst, const SkRect &src)
static SkMatrix Scale(SkScalar sx, SkScalar sy)
static SkMatrix RotateDeg(SkScalar deg)
static SkMatrix Translate(SkScalar dx, SkScalar dy)
static SkMatrix MakeAll(SkScalar scaleX, SkScalar skewX, SkScalar transX, SkScalar skewY, SkScalar scaleY, SkScalar transY, SkScalar pers0, SkScalar pers1, SkScalar pers2)
static const SkMatrix & I()
static SkIRect MakeILarge()
virtual sk_sp< SkImage > asImage() const =0
virtual bool isGaneshBacked() const
const SkIRect & subset() const
const SkColorInfo & colorInfo() const
SkISize dimensions() const
virtual bool isGraphiteBacked() const
const char * c_str() const
virtual sk_sp< SkDevice > makeDevice(SkISize size, sk_sp< SkColorSpace >, const SkSurfaceProps *props=nullptr) const =0
const Backend * backend() const
sk_sp< SkColorSpace > refColorSpace() const
const LayerSpace< SkIRect > & desiredOutput() const
Context withNewDesiredOutput(const LayerSpace< SkIRect > &desiredOutput) const
FilterResult applyColorFilter(const Context &ctx, sk_sp< SkColorFilter > colorFilter) const
FilterResult applyCrop(const Context &ctx, const LayerSpace< SkIRect > &crop, SkTileMode tileMode=SkTileMode::kDecal) const
static FilterResult MakeFromImage(const Context &ctx, sk_sp< SkImage > image, SkRect srcRect, ParameterSpace< SkRect > dstRect, const SkSamplingOptions &sampling)
static constexpr SkSamplingOptions kDefaultSampling
FilterResult applyTransform(const Context &ctx, const LayerSpace< SkMatrix > &transform, const SkSamplingOptions &sampling) const
LayerSpace< SkIPoint > topLeft() const
LayerSpace< SkISize > size() const
const uint8_t uint32_t uint32_t GError ** error
constexpr SkColor4f kGreen
constexpr SkColor4f kCyan
constexpr SkColor4f kTransparent
constexpr SkColor4f kBlue
PODArray< SkColor > colors
SkSamplingOptions sampling
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
TextureProxyView AsView(const SkImage *image)
sk_sp< Backend > MakeGaneshBackend(sk_sp< GrRecordingContext > context, GrSurfaceOrigin origin, const SkSurfaceProps &surfaceProps, SkColorType colorType)
sk_sp< Backend > MakeRasterBackend(const SkSurfaceProps &surfaceProps, SkColorType colorType)
SIN Vec< N, float > sqrt(const Vec< N, float > &x)
static SkColor4f transform(SkColor4f c, SkColorSpace *src, SkColorSpace *dst)
static constexpr SkCubicResampler CatmullRom()
static constexpr SkCubicResampler Mitchell()
constexpr int32_t y() const
constexpr int32_t x() const
SkIRect makeOutset(int32_t dx, int32_t dy) const
bool intersect(const SkIRect &r)
int32_t fBottom
larger y-axis bounds
int32_t fTop
smaller y-axis bounds
static constexpr SkIRect MakeSize(const SkISize &size)
static constexpr SkIRect MakeEmpty()
constexpr SkIRect makeOffset(int32_t dx, int32_t dy) const
static constexpr SkIRect MakeWH(int32_t w, int32_t h)
static constexpr SkIRect MakeXYWH(int32_t x, int32_t y, int32_t w, int32_t h)
int32_t fLeft
smaller x-axis bounds
void outset(int32_t dx, int32_t dy)
bool contains(int32_t x, int32_t y) const
int32_t fRight
larger x-axis bounds
constexpr int32_t width() const
constexpr int32_t height() const
SkImageInfo makeWH(int newWidth, int newHeight) const
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)
SkRGBA4f< kPremul_SkAlphaType > premul() const
SkRGBA4f< kUnpremul_SkAlphaType > unpremul() const
static SkRect Make(const SkISize &size)
SkScalar fBottom
larger y-axis bounds
SkScalar fLeft
smaller x-axis bounds
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
SkScalar fRight
larger x-axis bounds
SkScalar fTop
smaller y-axis bounds
static constexpr SkSamplingOptions Aniso(int maxAniso)