47#include <initializer_list>
55#if defined(SK_GRAPHITE)
79 using BoundsAnalysis = FilterResult::BoundsAnalysis;
84 bool preserveDeviceState) {
85 image.draw(ctx,
device, preserveDeviceState,
nullptr);
91 return image.asShader(ctx, FilterResult::kDefaultSampling,
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() &&
132 float scaleFactors[2];
134 return {{scaleFactors[0], scaleFactors[1]}};
150 if (!(analysis & BoundsAnalysis::kHasLayerFillingEffect)) {
164 if (analysis & BoundsAnalysis::kHasLayerFillingEffect) {
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(); }
334 if (fExpectation != Expect::kNewImage) {
337 if (
auto*
s = std::get_if<RescaleParams>(&fAction)) {
338 float minScale =
std::min(
s->fScale.width(),
s->fScale.height());
339 if (minScale >= 1.f - 0.001f) {
344 if (deferredScale &&
std::get<0>(*deferredScale) <= 0.9f) {
351 }
while(minScale < 0.9f);
358 return {steps, steps + 1};
370 if (
auto* t = std::get_if<TransformParams>(&fAction)) {
374 return t->fMatrix.mapRect(inputBounds);
375 }
else if (
auto* c = std::get_if<CropParams>(&fAction)) {
376 if (c->fExpectedBounds) {
377 return *c->fExpectedBounds;
381 if (!intersection.
intersect(inputBounds)) {
393 }
else if (std::holds_alternative<RescaleParams>(fAction)) {
405 Expect effectiveExpectation = fExpectation;
409 effectiveExpectation = Expect::kEmptyImage;
415 canvas.translate(-desiredOutput.
left(), -desiredOutput.
top());
423 if (effectiveExpectation != Expect::kEmptyImage) {
425 paint.setAntiAlias(
true);
430 if (
auto* t = std::get_if<TransformParams>(&fAction)) {
434 if (!
m.isTranslate() ||
440 }
else if (
auto* c = std::get_if<CropParams>(&fAction)) {
447 source =
source->makeSubset({imageBounds.left() - origin.
x(),
448 imageBounds.top() - origin.
y(),
449 imageBounds.right() - origin.
x(),
450 imageBounds.bottom() - origin.
y()});
451 origin = imageBounds.topLeft();
457 clear_device(paddedDevice.get());
458 paddedDevice->drawSpecial(
source.get(),
460 origin.
y() - c->fRect.top()),
464 origin = c->fRect.topLeft();
466 tileMode = c->fTileMode;
468 paint.setColorFilter(*cf);
469 }
else if (
auto*
s = std::get_if<RescaleParams>(&fAction)) {
471 if (
s->fScale.width() != 1.f ||
s->fScale.height() != 1.f) {
484 clear_device(stepDevice.get());
485 stepDevice->drawSpecial(
source.get(),
495 canvas.translate(origin.
x(), origin.
y());
496 canvas.scale(origSrcWidth / (
float)
source->
width(),
505 canvas.drawPaint(
paint);
512 std::variant<TransformParams,
528class FilterResultImageResolver {
538 FilterResultImageResolver(Method method) : fMethod(method) {}
540 const char* methodName()
const {
542 case Method::kImageAndOffset:
return "imageAndOffset";
543 case Method::kDrawToCanvas:
return "drawToCanvas";
544 case Method::kShader:
return "asShader";
545 case Method::kClippedShader:
return "asShaderClipped";
546 case Method::kStrictShader:
return "strictShader";
553 if (fMethod == Method::kImageAndOffset) {
556 return {resolved, origin};
559 return {
nullptr, {}};
570 if (fMethod > Method::kDrawToCanvas) {
572 if (fMethod == Method::kShader) {
579 }
else if (fMethod == Method::kClippedShader) {
584 auto [pixels, origin] = this->resolve(
592 paint.setShader(std::move(shader));
593 canvas.drawPaint(
paint);
595 SkASSERT(fMethod == Method::kDrawToCanvas);
612 using ResolveMethod = FilterResultImageResolver::Method;
620#if defined(SK_GANESH)
623 , fDirectContext(context)
631#if defined(SK_GRAPHITE)
634 , fRecorder(recorder)
649 float allowedPercentImageDiff,
650 int transparentCheckBorderTolerance) {
653 SkBitmap expectedBM = this->readPixels(expectedImage);
657 return this->compareImages(ctx, expectedBM, expectedOrigin, actual,
658 ResolveMethod::kImageAndOffset,
659 allowedPercentImageDiff, transparentCheckBorderTolerance) &&
660 this->compareImages(ctx, expectedBM, expectedOrigin, actual,
661 ResolveMethod::kDrawToCanvas,
662 allowedPercentImageDiff, transparentCheckBorderTolerance) &&
663 this->compareImages(ctx, expectedBM, expectedOrigin, actual,
664 ResolveMethod::kShader,
665 allowedPercentImageDiff, transparentCheckBorderTolerance) &&
666 this->compareImages(ctx, expectedBM, expectedOrigin, actual,
667 ResolveMethod::kClippedShader,
668 allowedPercentImageDiff, transparentCheckBorderTolerance);
672 FilterResultImageResolver expectedResolver{ResolveMethod::kStrictShader};
673 auto [expectedImage, expectedOrigin] = expectedResolver.resolve(ctx, actual);
674 SkBitmap expectedBM = this->readPixels(expectedImage.get());
675 return this->compareImages(ctx, expectedBM, expectedOrigin, actual,
676 ResolveMethod::kImageAndOffset,
691 size.height() / 2.f});
695 canvas.concat(rotation);
702 float sz =
size.width() <= 16.f ||
size.height() <= 16.f ? 2.f : 8.f;
703 for (
float y = coverBounds.
fTop;
y < coverBounds.
fBottom;
y += sz) {
704 for (
float x = coverBounds.
fLeft;
x < coverBounds.
fRight;
x += sz) {
718 float allowedPercentImageDiff,
int transparentCheckBorderTolerance) {
719 FilterResultImageResolver resolver{method};
720 auto [actualImage, actualOrigin] = resolver.resolve(ctx, actual);
722 SkBitmap actualBM = this->readPixels(actualImage.get());
724 if (!this->compareBitmaps(expected, expectedOrigin, actualBM, actualOrigin,
725 allowedPercentImageDiff, transparentCheckBorderTolerance,
727 if (!fLoggedErrorImage) {
728 SkDebugf(
"FilterResult comparison failed for method %s\n", resolver.methodName());
729 this->logBitmaps(expected, actualBM, badPixels);
730 fLoggedErrorImage =
true;
733 }
else if (kLogAllBitmaps) {
734 this->logBitmaps(expected, actualBM, badPixels);
740 bool compareBitmaps(
const SkBitmap& expected,
744 float allowedPercentImageDiff,
745 int transparentCheckBorderTolerance,
747 SkIRect excludeTransparentCheck;
748 if (actual.
empty()) {
758 const bool contained = expectedBounds.
contains(actualBounds);
760 "actual image [%d %d %d %d] not contained within expected [%d %d %d %d]",
763 expectedBounds.
fLeft, expectedBounds.
fTop,
773 for (
int y = 0;
y < actual.
height(); ++
y) {
774 for (
int x = 0;
x < actual.
width(); ++
x) {
778 if (actualColor != expectedColor &&
779 !this->approxColor(this->boxFilter(actual,
x,
y),
780 this->boxFilter(expected, ep.
fX, ep.
fY))) {
787 const int totalCount = expected.
width() * expected.
height();
788 const float percentError = 100.f * errorCount / (
float) totalCount;
789 const bool approxMatch = percentError <= allowedPercentImageDiff;
792 "%d pixels were too different from %d total (%f %% vs. %f %%)",
793 errorCount, totalCount, percentError, allowedPercentImageDiff);
800 excludeTransparentCheck = actualBounds.
makeOffset(-expectedOrigin);
804 excludeTransparentCheck.
outset(transparentCheckBorderTolerance,
805 transparentCheckBorderTolerance);
808 int badTransparencyCount = 0;
809 for (
int y = 0;
y < expected.
height(); ++
y) {
810 for (
int x = 0;
x < expected.
width(); ++
x) {
811 if (!excludeTransparentCheck.
isEmpty() && excludeTransparentCheck.
contains(
x,
y)) {
819 const bool onEdge = !excludeTransparentCheck.
isEmpty() &&
822 onEdge ? kAATolerance : 0.f)) {
824 badTransparencyCount++;
829 REPORTER_ASSERT(fReporter, badTransparencyCount == 0,
"Unexpected non-transparent pixels");
830 return badTransparencyCount == 0;
835 float tolerance = kRGBTolerance)
const {
842 float r = (apm.
fR + bpm.
fR) / 2.f;
843 float dr = (apm.
fR - bpm.
fR);
844 float dg = (apm.
fG - bpm.
fG);
845 float db = (apm.
fB - bpm.
fB);
847 return delta <= tolerance;
851 static constexpr int kKernelOffset = kKernelSize / 2;
853 float netWeight = 0.f;
854 for (
int sy =
y - kKernelOffset; sy <=
y + kKernelOffset; ++sy) {
855 for (
int sx =
x - kKernelOffset; sx <=
x + kKernelOffset; ++sx) {
856 float weight = kFuzzyKernel[sy -
y + kKernelOffset][sx -
x + kKernelOffset];
858 if (sx < 0 || sx >= bm.
width() || sy < 0 || sy >= bm.
height()) {
875 return sum.
unpremul() * (1.f / netWeight);
883 [[maybe_unused]]
int srcX = specialImage->
subset().
fLeft;
884 [[maybe_unused]]
int srcY = specialImage->
subset().
fTop;
889#if defined(SK_GANESH)
890 if (fDirectContext) {
897#if defined(SK_GRAPHITE)
902 auto proxyII = ii.
makeWH(view.width(), view.height());
904 bm.
pixmap(), view.proxy(), proxyII, srcX, srcY));
915 void logBitmaps(
const SkBitmap& expected,
922 if (!actual.
empty()) {
927 SkDebugf(
"Actual: null (fully transparent)\n\n");
930 if (!badPixels.
empty()) {
934 for (
auto p : badPixels) {
944#if defined(SK_GANESH)
947#if defined(SK_GRAPHITE)
953 bool fLoggedErrorImage =
false;
960 float allowedPercentImageDiff=kDefaultMaxAllowedPercentImageDiff,
961 int transparentCheckBorderTolerance=0)
964 , fAllowedPercentImageDiff(allowedPercentImageDiff)
965 , fTransparentCheckBorderTolerance(transparentCheckBorderTolerance)
979 TestCase& applyCrop(
const SkIRect& crop,
982 std::optional<SkTileMode> expectedTileMode = {},
983 std::optional<SkIRect> expectedBounds = {}) {
985 if (!expectedTileMode) {
986 expectedTileMode = tileMode;
988 std::optional<LayerSpace<SkIRect>> expectedLayerBounds;
989 if (expectedBounds) {
992 fActions.emplace_back(crop, tileMode, expectedLayerBounds,
expectation,
1000 return this->applyTransform(
matrix, FilterResult::kDefaultSampling,
expectation);
1006 std::optional<SkSamplingOptions> expectedSampling = {}) {
1009 if (!expectedSampling.has_value()) {
1015 this->getDefaultExpectedColorFilter(
expectation));
1025 if (!expectedColorFilter.has_value()) {
1027 colorFilter, this->getDefaultExpectedColorFilter(
expectation));
1030 fActions.emplace_back(std::move(colorFilter),
expectation,
1032 this->getDefaultExpectedTileMode(
expectation, affectsTransparent),
1033 std::move(*expectedColorFilter));
1039 std::optional<SkTileMode> expectedTileMode = {}) {
1041 if (!expectedTileMode) {
1042 expectedTileMode = this->getDefaultExpectedTileMode(
expectation,
1048 this->getDefaultExpectedColorFilter(
expectation));
1052 void run(
const SkIRect& requestedOutput)
const {
1054 this->
run(requestedOutput,
true);
1055 this->
run(requestedOutput,
false);
1058 void run(
const SkIRect& requestedOutput,
bool backPropagateDesiredOutput)
const {
1062 fRunner,
SkStringPrintf(
"backpropagate output: %d", backPropagateDesiredOutput));
1065 std::vector<LayerSpace<SkIRect>> desiredOutputs;
1066 desiredOutputs.resize(fActions.size(), desiredOutput);
1067 if (!backPropagateDesiredOutput) {
1071 auto inputBounds = fSourceBounds;
1072 for (
int i = 0;
i < (
int) fActions.size() - 1; ++
i) {
1073 desiredOutputs[
i] = fActions[
i].expectedBounds(inputBounds);
1080 inputBounds = desiredOutputs[
i];
1085 for (
int i = (
int) fActions.size() - 2;
i >= 0; --
i) {
1086 if (backPropagateDesiredOutput ||
1088 desiredOutputs[
i] = fActions[
i+1].requiredInput(desiredOutputs[
i+1]);
1095 if (!fSourceBounds.isEmpty()) {
1098 fSourceBounds.topLeft());
1101 Context baseContext{fRunner.refBackend(),
1115 if (!expectedImage) {
1116 sk_sp<SkDevice> expectedSurface = fRunner.backend()->makeDevice({1, 1}, colorSpace);
1117 clear_device(expectedSurface.
get());
1124 for (
int i = 0;
i < (
int) fActions.size(); ++
i) {
1136 Expect correctedExpectation = fActions[
i].expectation();
1140 expectedBounds = desiredOutputs[
i];
1141 if (desiredOutputs[
i].isEmpty()) {
1142 correctedExpectation = Expect::kEmptyImage;
1144 }
else if (!expectedBounds.
intersect(desiredOutputs[
i])) {
1149 backPropagateDesiredOutput);
1151 correctedExpectation = Expect::kEmptyImage;
1154 std::vector<int> allowedOffscreenSurfaces =
1155 fActions[
i].expectedOffscreenSurfaces(
source);
1157 int actualShaderDraws =
stats.fNumShaderBasedTilingDraws +
stats.fNumShaderClampedDraws;
1158 int expectedShaderTiledDraws = 0;
1159 bool actualNewImage =
output.image() &&
1161 switch(correctedExpectation) {
1162 case Expect::kNewImage:
1167 expectedShaderTiledDraws =
std::min(1, allowedOffscreenSurfaces[0]);
1170 case Expect::kDeferredImage:
1173 case Expect::kEmptyImage:
1179 find(allowedOffscreenSurfaces.begin(),
1180 allowedOffscreenSurfaces.end(),
1181 stats.fNumOffscreenSurfaces) != allowedOffscreenSurfaces.end(),
1182 "expected %d or %d, got %d",
1183 allowedOffscreenSurfaces[0],
1184 allowedOffscreenSurfaces.size() > 1 ? allowedOffscreenSurfaces[1] : -1,
1185 stats.fNumOffscreenSurfaces);
1186 REPORTER_ASSERT(fRunner, actualShaderDraws <= expectedShaderTiledDraws,
1187 "expected %d+%d <= %d",
1188 stats.fNumShaderBasedTilingDraws,
stats.fNumShaderClampedDraws,
1189 expectedShaderTiledDraws);
1197 auto actualBounds =
output.layerBounds();
1203 if (correctedExpectation == Expect::kDeferredImage ||
1212 fActions[
i].expectedColorFilter()));
1213 if (actualShaderDraws < expectedShaderTiledDraws ||
1221 expectedImage = fActions[
i].renderExpectedImage(ctx,
1222 std::move(expectedImage),
1225 expectedOrigin = desiredOutputs[
i].topLeft();
1226 if (!fRunner.compareImages(ctx,
1227 expectedImage.
get(),
1230 fAllowedPercentImageDiff,
1231 fTransparentCheckBorderTolerance)) {
1244 if (
expectation != Expect::kDeferredImage || fActions.empty()) {
1245 return FilterResult::kDefaultSampling;
1247 return fActions[fActions.size() - 1].expectedSampling();
1253 if (
expectation == Expect::kNewImage && cfAffectsTransparency) {
1255 }
else if (
expectation != Expect::kDeferredImage || fActions.empty()) {
1258 return fActions[fActions.size() - 1].expectedTileMode();
1264 if (
expectation != Expect::kDeferredImage || fActions.empty()) {
1267 return sk_ref_sp(fActions[fActions.size() - 1].expectedColorFilter());
1273 float fAllowedPercentImageDiff;
1274 int fTransparentCheckBorderTolerance;
1282 std::vector<ApplyAction> fActions;
1305#if defined(SK_GANESH)
1306#define DEF_GANESH_TEST_SUITE(name, ctsEnforcement) \
1307 DEF_GANESH_TEST_FOR_CONTEXTS(FilterResult_ganesh_##name, \
1308 skgpu::IsNativeBackend, \
1313 TestRunner runner(r, ctxInfo.directContext()); \
1314 test_suite_##name(runner); \
1317#define DEF_GANESH_TEST_SUITE(name)
1320#if defined(SK_GRAPHITE)
1321#define DEF_GRAPHITE_TEST_SUITE(name, ctsEnforcement) \
1322 DEF_CONDITIONAL_GRAPHITE_TEST_FOR_ALL_CONTEXTS(FilterResult_graphite_##name, \
1323 skgpu::IsNativeBackend, \
1329 using namespace skgpu::graphite; \
1330 auto recorder = context->makeRecorder(); \
1331 TestRunner runner(r, recorder.get()); \
1332 test_suite_##name(runner); \
1333 std::unique_ptr<Recording> recording = recorder->snap(); \
1335 ERRORF(r, "Failed to make recording"); \
1338 InsertRecordingInfo insertInfo; \
1339 insertInfo.fRecording = recording.get(); \
1340 context->insertRecording(insertInfo); \
1341 testContext->syncedSubmit(context); \
1344#define DEF_GRAPHITE_TEST_SUITE(name)
1347#define DEF_TEST_SUITE(name, runner, ganeshCtsEnforcement, graphiteCtsEnforcement) \
1348 static void test_suite_##name(TestRunner&); \
1351 DEF_GANESH_TEST_SUITE(name, ganeshCtsEnforcement) \
1352 DEF_TEST(FilterResult_raster_##name, reporter) { \
1353 TestRunner runner(reporter); \
1354 test_suite_##name(runner); \
1356 void test_suite_##name(TestRunner& runner)
1366 TestCase(r,
"applyCrop() to empty source")
1368 .applyCrop({0, 0, 10, 10}, tm, Expect::kEmptyImage)
1369 .
run({0, 0, 20, 20});
1372 TestCase(r,
"applyTransform() to empty source")
1375 .run({10, 10, 20, 20});
1377 TestCase(r,
"applyColorFilter() to empty source")
1379 .applyColorFilter(alpha_modulate(0.5f), Expect::kEmptyImage)
1380 .run({0, 0, 10, 10});
1382 TestCase(r,
"Transparency-affecting color filter overrules empty source")
1384 .applyColorFilter(affect_transparent(
SkColors::kBlue), Expect::kNewImage,
1386 .run({0, 0, 10, 10});
1393 TestCase(r,
"applyCrop() + empty output becomes empty")
1394 .source({0, 0, 10, 10})
1395 .applyCrop({2, 2, 8, 8}, tm, Expect::kEmptyImage)
1399 TestCase(r,
"applyTransform() + empty output becomes empty")
1400 .source({0, 0, 10, 10})
1404 TestCase(r,
"applyColorFilter() + empty output becomes empty")
1405 .source({0, 0, 10, 10})
1406 .applyColorFilter(alpha_modulate(0.5f), Expect::kEmptyImage)
1409 TestCase(r,
"Transpency-affecting color filter + empty output is empty")
1410 .source({0, 0, 10, 10})
1411 .applyColorFilter(affect_transparent(
SkColors::kBlue), Expect::kEmptyImage)
1424 const Expect nonDecalExpectsNewImage = tm ==
SkTileMode::kDecal ? Expect::kDeferredImage
1425 : Expect::kNewImage;
1426 TestCase(r,
"applyCrop() contained in source and output")
1427 .source({0, 0, 20, 20})
1428 .applyCrop({8, 8, 12, 12}, tm, Expect::kDeferredImage)
1429 .
run({4, 4, 16, 16});
1431 TestCase(r,
"applyCrop() contained in source, intersects output")
1432 .source({0, 0, 20, 20})
1433 .applyCrop({4, 4, 12, 12}, tm, Expect::kDeferredImage)
1434 .
run({8, 8, 16, 16});
1436 TestCase(r,
"applyCrop() intersects source, contained in output")
1437 .source({10, 10, 20, 20})
1438 .applyCrop({4, 4, 16, 16}, tm, nonDecalExpectsNewImage)
1439 .
run({0, 0, 20, 20});
1441 TestCase(r,
"applyCrop() intersects source and output")
1442 .source({0, 0, 10, 10})
1443 .applyCrop({5, -5, 15, 5}, tm, nonDecalExpectsNewImage)
1444 .
run({7, -2, 12, 8});
1446 TestCase(r,
"applyCrop() contains source, intersects output")
1447 .source({4, 4, 16, 16})
1448 .applyCrop({0, 0, 20, 20}, tm, nonDecalExpectsNewImage)
1449 .
run({-5, -5, 18, 18});
1453 TestCase(r,
"applyCrop() intersects source, contains output")
1454 .source({0, 0, 20, 20})
1456 .
run({0, 5, 20, 15});
1458 TestCase(r,
"applyCrop() contains source and output")
1459 .source({0, 0, 10, 10})
1472 TestCase(r,
"applyCrop() disjoint from source, intersects output")
1473 .source({0, 0, 10, 10})
1474 .applyCrop({11, 11, 20, 20}, tm, Expect::kEmptyImage)
1475 .
run({0, 0, 15, 15});
1477 TestCase(r,
"applyCrop() disjoint from source, intersects output disjoint from source")
1478 .source({0, 0, 10, 10})
1479 .applyCrop({11, 11, 20, 20}, tm, Expect::kEmptyImage)
1480 .
run({12, 12, 18, 18});
1482 TestCase(r,
"applyCrop() disjoint from source and output")
1483 .source({0, 0, 10, 10})
1484 .applyCrop({12, 12, 18, 18}, tm, Expect::kEmptyImage)
1485 .
run({-1, -1, 11, 11});
1487 TestCase(r,
"applyCrop() disjoint from source and output disjoint from source")
1488 .source({0, 0, 10, 10})
1489 .applyCrop({-10, 10, -1, -1}, tm, Expect::kEmptyImage)
1490 .
run({11, 11, 20, 20});
1498 : Expect::kNewImage;
1499 TestCase(r,
"applyCrop() intersects source, disjoint from output disjoint from source")
1500 .source({0, 0, 10, 10})
1501 .applyCrop({-5, -5, 5, 5}, tm, nonDecalExpectsImage)
1502 .
run({12, 12, 18, 18});
1504 TestCase(r,
"applyCrop() intersects source, disjoint from output")
1505 .source({0, 0, 10, 10})
1506 .applyCrop({-5, -5, 5, 5}, tm, nonDecalExpectsImage)
1507 .
run({6, 6, 18, 18});
1513 TestCase(r,
"applyCrop() is empty")
1514 .source({0, 0, 10, 10})
1516 .run({0, 0, 10, 10});
1518 TestCase(r,
"applyCrop() emptiness propagates")
1519 .source({0, 0, 10, 10})
1520 .applyCrop({1, 1, 9, 9}, tm, Expect::kDeferredImage)
1522 .run({0, 0, 10, 10});
1528 TestCase(r,
"Disjoint applyCrop() after kDecal become empty")
1529 .source({0, 0, 10, 10})
1531 .applyCrop({6, 6, 10, 10}, tm, Expect::kEmptyImage)
1532 .
run({0, 0, 10, 10});
1535 TestCase(r,
"Disjoint tiling applyCrop() before kDecal is not empty and combines")
1536 .source({0, 0, 10, 10})
1537 .applyCrop({0, 0, 4, 4}, tm, Expect::kDeferredImage)
1539 .
run({0, 0, 10, 10});
1541 TestCase(r,
"Disjoint non-decal applyCrops() are not empty")
1542 .source({0, 0, 10, 10})
1543 .applyCrop({0, 0, 4, 4}, tm, Expect::kDeferredImage)
1545 : Expect::kNewImage)
1546 .
run({0, 0, 10, 10});
1553 TestCase(r,
"Decal applyCrop() always combines with any other crop")
1554 .source({0, 0, 20, 20})
1555 .applyCrop({5, 5, 15, 15}, tm, Expect::kDeferredImage)
1557 .
run({0, 0, 20, 20});
1560 TestCase(r,
"Decal applyCrop() before non-decal crop requires new image")
1561 .source({0, 0, 20, 20})
1563 .applyCrop({10, 10, 20, 20}, tm, Expect::kNewImage)
1564 .
run({0, 0, 20, 20});
1566 TestCase(r,
"Consecutive non-decal crops combine if both are clamp")
1567 .source({0, 0, 20, 20})
1568 .applyCrop({5, 5, 15, 15}, tm, Expect::kDeferredImage)
1569 .applyCrop({10, 10, 20, 20}, tm,
1571 : Expect::kNewImage)
1572 .
run({0, 0, 20, 20});
1581 TestCase(r,
"Periodic applyCrop() becomes a transform")
1582 .source({0, 0, 20, 20})
1583 .applyCrop({5, 5, 15, 15}, tm, Expect::kDeferredImage,
1585 .
run({25, 25, 35, 35});
1587 TestCase(r,
"Periodic applyCrop() with partial transparency still becomes a transform")
1588 .source({0, 0, 20, 20})
1589 .applyCrop({-5, -5, 15, 15}, tm, Expect::kDeferredImage,
1593 .
run({15, 15, 35, 35});
1595 TestCase(r,
"Periodic applyCrop() after complex transform can still simplify")
1596 .source({0, 0, 20, 20})
1598 .applyCrop({-5, -5, 25, 25}, tm, Expect::kDeferredImage,
1601 .
run({55,55,85,85});
1605 TestCase(r,
"Periodic applyCrop() with visible edge does not become a transform")
1606 .source({0, 0, 20, 20})
1607 .applyCrop({5, 5, 15, 15}, tm, Expect::kDeferredImage)
1608 .
run({10, 10, 20, 20});
1610 TestCase(r,
"Periodic applyCrop() with visible edge and transparency creates new image")
1611 .source({0, 0, 20, 20})
1612 .applyCrop({-5, -5, 15, 15}, tm, Expect::kNewImage)
1613 .
run({10, 10, 20, 20});
1615 TestCase(r,
"Periodic applyCropp() with visible edge and complex transform creates image")
1616 .source({0, 0, 20, 20})
1618 .applyCrop({-5, -5, 25, 25}, tm, Expect::kNewImage)
1619 .
run({20, 20, 50, 50});
1624 TestCase(r,
"Decal then clamp crop uses 1px buffer around intersection")
1625 .source({0, 0, 20, 20})
1627 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
1629 .
run({0, 0, 20, 20});
1631 TestCase(r,
"Decal then clamp crop uses 1px buffer around intersection, w/ alpha color filter")
1632 .source({0, 0, 20, 20})
1634 .applyColorFilter(affect_transparent(
SkColors::kCyan), Expect::kDeferredImage)
1636 .
run({0, 0, 20, 20});
1643 TestCase(r,
"applyTransform() integer translate")
1644 .source({0, 0, 10, 10})
1646 .run({0, 0, 10, 10});
1648 TestCase(r,
"applyTransform() fractional translate")
1649 .source({0, 0, 10, 10})
1651 .run({0, 0, 10, 10});
1653 TestCase(r,
"applyTransform() scale")
1654 .source({0, 0, 24, 24})
1656 .run({-16, -16, 96, 96});
1659 TestCase(r,
"applyTransform() with complex transform")
1660 .source({0, 0, 8, 8})
1662 .run({0, 0, 16, 16});
1667 TestCase(r,
"linear + linear combine")
1668 .source({0, 0, 8, 8})
1673 .run({0, 0, 16, 16});
1675 TestCase(r,
"equiv. bicubics combine")
1676 .source({0, 0, 8, 8})
1681 .run({0, 0, 16, 16});
1683 TestCase(r,
"linear + bicubic becomes bicubic")
1684 .source({0, 0, 8, 8})
1689 .run({0, 0, 16, 16});
1691 TestCase(r,
"bicubic + linear becomes bicubic")
1692 .source({0, 0, 8, 8})
1698 .run({0, 0, 16, 16});
1700 TestCase(r,
"aniso picks max level to combine")
1701 .source({0, 0, 8, 8})
1707 .run({0, 0, 16, 16});
1709 TestCase(r,
"aniso picks max level to combine (other direction)")
1710 .source({0, 0, 8, 8})
1715 .run({0, 0, 16, 16});
1717 TestCase(r,
"linear + aniso becomes aniso")
1718 .source({0, 0, 8, 8})
1723 .run({0, 0, 16, 16});
1725 TestCase(r,
"aniso + linear stays aniso")
1726 .source({0, 0, 8, 8})
1732 .run({0, 0, 16, 16});
1740 TestCase(r,
"different bicubics do not combine")
1741 .source({0, 0, 8, 8})
1746 .run({0, 0, 16, 16});
1748 TestCase(r,
"nearest + linear do not combine")
1749 .source({0, 0, 8, 8})
1754 .run({0, 0, 16, 16});
1756 TestCase(r,
"linear + nearest do not combine")
1757 .source({0, 0, 8, 8})
1762 .run({0, 0, 16, 16});
1764 TestCase(r,
"bicubic + aniso do not combine")
1765 .source({0, 0, 8, 8})
1770 .run({0, 0, 16, 16});
1772 TestCase(r,
"aniso + bicubic do not combine")
1773 .source({0, 0, 8, 8})
1778 .run({0, 0, 16, 16});
1780 TestCase(r,
"nearest + nearest do not combine")
1781 .source({0, 0, 8, 8})
1786 .run({0, 0, 16, 16});
1793 TestCase(r,
"integer translate+NN then bicubic combines")
1794 .source({0, 0, 8, 8})
1797 FilterResult::kDefaultSampling)
1800 .run({0, 0, 16, 16});
1802 TestCase(r,
"bicubic then integer translate+NN combines")
1803 .source({0, 0, 8, 8})
1809 .run({0, 0, 16, 16});
1817 TestCase(r,
"Transform moves src image outside of requested output")
1818 .source({0, 0, 8, 8})
1822 TestCase(r,
"Transform moves src image outside of crop")
1823 .source({0, 0, 8, 8})
1825 .applyCrop({2, 2, 6, 6}, Expect::kEmptyImage)
1826 .
run({0, 0, 20, 20});
1828 TestCase(r,
"Transform moves cropped image outside of requested output")
1829 .source({0, 0, 8, 8})
1830 .applyCrop({1, 1, 4, 4}, Expect::kDeferredImage)
1836 TestCase(r,
"Crop after transform can always apply")
1837 .source({0, 0, 16, 16})
1839 .applyCrop({2, 2, 15, 15}, Expect::kDeferredImage)
1840 .
run({0, 0, 16, 16});
1844 TestCase(r,
"Crop after translate is lifted to image subset")
1845 .source({0, 0, 32, 32})
1847 .applyCrop({16, 16, 24, 24}, Expect::kDeferredImage)
1849 .run({0, 0, 32, 32});
1851 TestCase(r,
"Transform after unlifted crop triggers new image")
1852 .source({0, 0, 16, 16})
1854 .applyCrop({1, 1, 15, 15}, Expect::kDeferredImage)
1856 .run({0, 0, 16, 16});
1858 TestCase(r,
"Transform after unlifted crop with interior output does not trigger new image")
1859 .source({0, 0, 16, 16})
1861 .applyCrop({1, 1, 15, 15}, Expect::kDeferredImage)
1863 .run({4, 4, 12, 12});
1865 TestCase(r,
"Translate after unlifted crop does not trigger new image")
1866 .source({0, 0, 16, 16})
1868 .applyCrop({2, 2, 14, 14}, Expect::kDeferredImage)
1870 .run({0, 0, 16, 16});
1872 TestCase(r,
"Transform after large no-op crop does not trigger new image")
1873 .source({0, 0, 64, 64})
1875 .applyCrop({-64, -64, 128, 128}, Expect::kDeferredImage)
1877 .run({0, 0, 64, 64});
1887 TestCase(r,
"Transform after tile mode does not trigger new image")
1888 .source({0, 0, 64, 64})
1889 .applyCrop({2, 2, 32, 32}, tm, Expect::kDeferredImage)
1891 .run({0, 0, 64, 64});
1893 TestCase(r,
"Integer transform before tile mode does not trigger new image")
1894 .source({0, 0, 32, 32})
1896 .applyCrop({20, 20, 40, 40}, tm, Expect::kDeferredImage)
1897 .
run({0, 0, 64, 64});
1899 TestCase(r,
"Non-integer transform before tile mode triggers new image")
1900 .source({0, 0, 50, 40})
1902 .applyCrop({10, 10, 30, 30}, tm, Expect::kNewImage)
1903 .
run({0, 0, 50, 50});
1905 TestCase(r,
"Non-integer transform before tiling defers image if edges are hidden")
1906 .source({0, 0, 64, 64})
1908 .applyCrop({10, 10, 50, 50}, tm, Expect::kDeferredImage,
1910 .
run({11, 11, 49, 49});
1918 TestCase(r,
"applyColorFilter() defers image")
1919 .source({0, 0, 24, 24})
1920 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
1921 .run({0, 0, 32, 32});
1923 TestCase(r,
"applyColorFilter() composes with other color filters")
1924 .source({0, 0, 24, 24})
1925 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
1926 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
1927 .run({0, 0, 32, 32});
1929 TestCase(r,
"Transparency-affecting color filter fills output")
1930 .source({0, 0, 24, 24})
1931 .applyColorFilter(affect_transparent(
SkColors::kBlue), Expect::kDeferredImage)
1932 .run({-8, -8, 32, 32});
1936 TestCase(r,
"Transparency-affecting composition fills output (ATBx2)")
1937 .source({0, 0, 24, 24})
1938 .applyColorFilter(affect_transparent(
SkColors::kBlue), Expect::kDeferredImage)
1939 .applyColorFilter(affect_transparent(
SkColors::kRed), Expect::kDeferredImage)
1940 .run({-8, -8, 32, 32});
1942 TestCase(r,
"Transparency-affecting composition fills output (ATB,reg)")
1943 .source({0, 0, 24, 24})
1944 .applyColorFilter(affect_transparent(
SkColors::kBlue), Expect::kDeferredImage)
1945 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
1946 .run({-8, -8, 32, 32});
1948 TestCase(r,
"Transparency-affecting composition fills output (reg,ATB)")
1949 .source({0, 0, 24, 24})
1950 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
1951 .applyColorFilter(affect_transparent(
SkColors::kBlue), Expect::kDeferredImage)
1952 .run({-8, -8, 32, 32});
1957 TestCase(r,
"Transform composes with regular CF")
1958 .source({0, 0, 24, 24})
1960 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
1961 .run({0, 0, 24, 24});
1963 TestCase(r,
"Regular CF composes with transform")
1964 .source({0, 0, 24, 24})
1965 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
1967 .run({0, 0, 24, 24});
1969 TestCase(r,
"Transform composes with transparency-affecting CF")
1970 .source({0, 0, 24, 24})
1972 .applyColorFilter(affect_transparent(
SkColors::kBlue), Expect::kDeferredImage)
1973 .run({0, 0, 24, 24});
1979 TestCase(r,
"Transparency-affecting CF composes with transform")
1980 .source({0, 0, 24, 24})
1981 .applyColorFilter(affect_transparent(
SkColors::kBlue), Expect::kDeferredImage)
1983 .run({-50, -50, 50, 50});
1989 TestCase(r,
"Transform between regular color filters")
1990 .source({0, 0, 24, 24})
1991 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
1993 .applyColorFilter(alpha_modulate(0.75f), Expect::kDeferredImage)
1994 .run({0, 0, 24, 24});
1996 TestCase(r,
"Transform between transparency-affecting color filters")
1997 .source({0, 0, 24, 24})
1998 .applyColorFilter(affect_transparent(
SkColors::kBlue), Expect::kDeferredImage)
2000 .applyColorFilter(affect_transparent(
SkColors::kGreen), Expect::kDeferredImage)
2001 .run({0, 0, 24, 24});
2003 TestCase(r,
"Transform between ATB and regular color filters")
2004 .source({0, 0, 24, 24})
2005 .applyColorFilter(affect_transparent(
SkColors::kBlue), Expect::kDeferredImage)
2007 .applyColorFilter(alpha_modulate(0.75f), Expect::kDeferredImage)
2008 .run({0, 0, 24, 24});
2010 TestCase(r,
"Transform between regular and ATB color filters")
2011 .source({0, 0, 24, 24})
2012 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
2014 .applyColorFilter(affect_transparent(
SkColors::kGreen), Expect::kDeferredImage)
2015 .run({0, 0, 24, 24});
2020 TestCase(r,
"Regular color filter between transforms")
2021 .source({0, 0, 24, 24})
2023 .applyColorFilter(alpha_modulate(0.8f), Expect::kDeferredImage)
2025 .run({0, 0, 24, 24});
2027 TestCase(r,
"Transparency-affecting color filter between transforms")
2028 .source({0, 0, 24, 24})
2030 .applyColorFilter(affect_transparent(
SkColors::kRed), Expect::kDeferredImage)
2032 .run({0, 0, 24, 24});
2037 TestCase(r,
"Regular color filter after empty crop stays empty")
2038 .source({0, 0, 16, 16})
2040 .applyColorFilter(alpha_modulate(0.2f), Expect::kEmptyImage)
2041 .run({0, 0, 16, 16});
2043 TestCase(r,
"Transparency-affecting color filter after empty crop creates new image")
2044 .source({0, 0, 16, 16})
2046 .applyColorFilter(affect_transparent(
SkColors::kRed), Expect::kNewImage,
2048 .run({0, 0, 16, 16});
2050 TestCase(r,
"Regular color filter composes with crop")
2051 .source({0, 0, 32, 32})
2052 .applyColorFilter(alpha_modulate(0.7f), Expect::kDeferredImage)
2053 .applyCrop({8, 8, 24, 24}, tm, Expect::kDeferredImage)
2054 .
run({0, 0, 32, 32});
2056 TestCase(r,
"Crop composes with regular color filter")
2057 .source({0, 0, 32, 32})
2058 .applyCrop({8, 8, 24, 24}, tm, Expect::kDeferredImage)
2059 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
2060 .run({0, 0, 32, 32});
2063 TestCase(r,
"Transparency-affecting color filter restricted by crop")
2064 .source({0, 0, 32, 32})
2065 .applyColorFilter(affect_transparent(
SkColors::kRed), Expect::kDeferredImage)
2066 .applyCrop({8, 8, 24, 24}, tm, Expect::kDeferredImage)
2067 .
run({0, 0, 32, 32});
2069 TestCase(r,
"Crop composes with transparency-affecting color filter")
2070 .source({0, 0, 32, 32})
2071 .applyCrop({8, 8, 24, 24}, tm, Expect::kDeferredImage)
2072 .applyColorFilter(affect_transparent(
SkColors::kRed), Expect::kDeferredImage)
2073 .run({0, 0, 32, 32});
2080 TestCase(r,
"Crop between regular color filters")
2081 .source({0, 0, 32, 32})
2082 .applyColorFilter(alpha_modulate(0.8f), Expect::kDeferredImage)
2083 .applyCrop({8, 8, 24, 24}, tm, Expect::kDeferredImage)
2084 .applyColorFilter(alpha_modulate(0.4f), Expect::kDeferredImage)
2085 .run({0, 0, 32, 32});
2088 TestCase(r,
"Crop between transparency-affecting color filters requires new image")
2089 .source({0, 0, 32, 32})
2090 .applyColorFilter(affect_transparent(
SkColors::kGreen), Expect::kDeferredImage)
2092 .applyColorFilter(affect_transparent(
SkColors::kRed), Expect::kNewImage)
2093 .run({0, 0, 32, 32});
2095 TestCase(r,
"Output-constrained crop between transparency-affecting filters does not")
2096 .source({0, 0, 32, 32})
2097 .applyColorFilter(affect_transparent(
SkColors::kGreen), Expect::kDeferredImage)
2099 .applyColorFilter(affect_transparent(
SkColors::kRed), Expect::kDeferredImage)
2100 .run({8, 8, 24, 24});
2102 TestCase(r,
"Tiling between transparency-affecting color filters defers image")
2103 .source({0, 0, 32, 32})
2104 .applyColorFilter(affect_transparent(
SkColors::kGreen), Expect::kDeferredImage)
2105 .applyCrop({8, 8, 24, 24}, tm, Expect::kDeferredImage)
2106 .applyColorFilter(affect_transparent(
SkColors::kRed), Expect::kDeferredImage)
2107 .run({0, 0, 32, 32});
2110 TestCase(r,
"Crop between regular and ATB color filters")
2111 .source({0, 0, 32, 32})
2112 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
2113 .applyCrop({8, 8, 24, 24}, tm, Expect::kDeferredImage)
2114 .applyColorFilter(affect_transparent(
SkColors::kRed), Expect::kDeferredImage)
2115 .run({0, 0, 32, 32});
2117 TestCase(r,
"Crop between ATB and regular color filters")
2118 .source({0, 0, 32, 32})
2119 .applyColorFilter(affect_transparent(
SkColors::kRed), Expect::kDeferredImage)
2120 .applyCrop({8, 8, 24, 24}, tm, Expect::kDeferredImage)
2121 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
2122 .run({0, 0, 32, 32});
2130 Expect newImageIfNotDecalOrDoubleClamp =
2133 Expect::kNewImage : Expect::kDeferredImage;
2135 TestCase(r,
"Regular color filter between crops")
2136 .source({0, 0, 32, 32})
2137 .applyCrop({4, 4, 24, 24}, firstTM, Expect::kDeferredImage)
2138 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
2139 .applyCrop({15, 15, 32, 32}, secondTM, newImageIfNotDecalOrDoubleClamp,
2141 .
run({0, 0, 32, 32});
2143 TestCase(r,
"Transparency-affecting color filter between crops")
2144 .source({0, 0, 32, 32})
2145 .applyCrop({4, 4, 24, 24}, firstTM, Expect::kDeferredImage)
2146 .applyColorFilter(affect_transparent(
SkColors::kGreen), Expect::kDeferredImage)
2147 .applyCrop({15, 15, 32, 32}, secondTM, newImageIfNotDecalOrDoubleClamp,
2149 .
run({0, 0, 32, 32});
2156 TestCase(r,
"Transform -> crop -> regular color filter")
2157 .source({0, 0, 32, 32})
2159 .applyCrop({2, 2, 30, 30}, Expect::kDeferredImage)
2160 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
2161 .run({0, 0, 32, 32});
2163 TestCase(r,
"Transform -> regular color filter -> crop")
2164 .source({0, 0, 32, 32})
2166 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
2167 .applyCrop({2, 2, 30, 30}, Expect::kDeferredImage)
2168 .
run({0, 0, 32, 32});
2170 TestCase(r,
"Crop -> transform -> regular color filter")
2171 .source({0, 0, 32, 32})
2172 .applyCrop({2, 2, 30, 30}, Expect::kDeferredImage)
2174 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
2175 .run({0, 0, 32, 32});
2177 TestCase(r,
"Crop -> regular color filter -> transform")
2178 .source({0, 0, 32, 32})
2179 .applyCrop({2, 2, 30, 30}, Expect::kDeferredImage)
2180 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
2182 .run({0, 0, 32, 32});
2184 TestCase(r,
"Regular color filter -> transform -> crop")
2185 .source({0, 0, 32, 32})
2186 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
2188 .applyCrop({2, 2, 30, 30}, Expect::kDeferredImage)
2189 .
run({0, 0, 32, 32});
2191 TestCase(r,
"Regular color filter -> crop -> transform")
2192 .source({0, 0, 32, 32})
2193 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
2194 .applyCrop({2, 2, 30, 30}, Expect::kDeferredImage)
2196 .run({0, 0, 32, 32});
2204 TestCase(r,
"Transform -> transparency-affecting color filter -> crop")
2205 .source({0, 0, 32, 32})
2207 .applyColorFilter(affect_transparent(
SkColors::kGreen), Expect::kDeferredImage)
2208 .applyCrop({2, 2, 30, 30}, Expect::kDeferredImage)
2209 .
run({0, 0, 32, 32});
2211 TestCase(r,
"Crop -> transform -> transparency-affecting color filter")
2212 .source({0, 0, 32, 32})
2213 .applyCrop({2, 2, 30, 30}, Expect::kDeferredImage)
2215 .applyColorFilter(affect_transparent(
SkColors::kGreen), Expect::kDeferredImage)
2216 .run({0, 0, 32, 32});
2218 TestCase(r,
"Crop -> transparency-affecting color filter -> transform")
2219 .source({0, 0, 32, 32})
2220 .applyCrop({2, 2, 30, 30}, Expect::kDeferredImage)
2221 .applyColorFilter(affect_transparent(
SkColors::kGreen), Expect::kDeferredImage)
2223 .run({0, 0, 32, 32});
2225 TestCase(r,
"Transparency-affecting color filter -> transform -> crop")
2226 .source({0, 0, 32, 32})
2227 .applyColorFilter(affect_transparent(
SkColors::kGreen), Expect::kDeferredImage)
2229 .applyCrop({2, 2, 30, 30}, Expect::kDeferredImage)
2230 .
run({0, 0, 32, 32});
2235 TestCase(r,
"Transform -> crop -> transparency-affecting color filter")
2236 .source({0, 0, 32, 32})
2238 .applyCrop({2, 2, 30, 30}, Expect::kDeferredImage)
2240 .run({0, 0, 32, 32});
2242 TestCase(r,
"Transparency-affecting color filter -> crop -> transform")
2243 .source({0, 0, 32, 32})
2244 .applyColorFilter(affect_transparent(
SkColors::kGreen), Expect::kDeferredImage)
2245 .applyCrop({2, 2, 30, 30}, Expect::kDeferredImage)
2247 .run({0, 0, 32, 32});
2251 TestCase(r,
"Transform -> crop -> transparency-affecting color filter")
2252 .source({0, 0, 32, 32})
2254 .applyCrop({2, 2, 30, 30}, Expect::kDeferredImage)
2255 .applyColorFilter(affect_transparent(
SkColors::kGreen), Expect::kDeferredImage)
2256 .run({15, 15, 21, 21});
2258 TestCase(r,
"Transparency-affecting color filter -> crop -> transform")
2259 .source({0, 0, 32, 32})
2260 .applyColorFilter(affect_transparent(
SkColors::kGreen), Expect::kDeferredImage)
2261 .applyCrop({2, 2, 30, 30}, Expect::kDeferredImage)
2263 .run({15, 15, 21, 21});
2280 TestCase(r,
"Layer decal shouldn't be visible")
2281 .source({65, 0, 199, 200})
2284 -0.173648f, 0.984808f, 17.3648f,
2285 0.000000f, 0.000000f, 1.0000f),
2286 Expect::kDeferredImage)
2288 .
run({-15, -15, 115, 215});
2292static constexpr SkSize kNearlyIdentity = {0.999f, 0.999f};
2297 TestCase(r,
"Identity rescale is a no-op")
2298 .source({0, 0, 50, 50})
2299 .applyCrop({0, 0, 50, 50}, tm, Expect::kDeferredImage)
2300 .rescale({1.f, 1.f}, Expect::kDeferredImage)
2301 .
run({-5, -5, 55, 55});
2303 TestCase(r,
"Near identity rescale is a no-op",
2304 kDefaultMaxAllowedPercentImageDiff,
2306 .source({0, 0, 50, 50})
2307 .applyCrop({0, 0, 50, 50}, tm, Expect::kDeferredImage)
2308 .rescale(kNearlyIdentity, Expect::kDeferredImage)
2309 .run({-5, -5, 55, 55});
2320 TestCase(r,
"1-step rescale preserves tile mode",
2321 kDefaultMaxAllowedPercentImageDiff,
2323 .source({16, 16, 64, 64})
2324 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2325 .rescale({0.5f, 0.5f}, Expect::kNewImage, tm)
2326 .
run({0, 0, 80, 80});
2329 TestCase(r,
"2-step rescale preserves tile mode",
2331 : periodic ? 2.5f : 1.f,
2333 .source({16, 16, 64, 64})
2334 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2335 .rescale({0.25f, 0.25f}, Expect::kNewImage, tm)
2336 .
run({0, 0, 80, 80});
2338 TestCase(r,
"2-step rescale with near-identity elision",
2339 periodic ? 17.75f : 41.52f,
2341 .source({16, 16, 64, 64})
2342 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2343 .rescale({0.23f, 0.23f}, Expect::kNewImage, tm)
2344 .
run({0, 0, 80, 80});
2346 TestCase(r,
"3-step rescale preserves tile mode",
2347 periodic ? 56.3f : 51.3f,
2349 .source({16, 16, 64, 64})
2350 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2351 .rescale({0.155f, 0.155f}, Expect::kNewImage, tm)
2352 .
run({0, 0, 80, 80});
2355 TestCase(r,
"Identity X axis, near-identity Y axis is a no-op",
2356 kDefaultMaxAllowedPercentImageDiff,
2358 .source({16, 16, 64, 64})
2359 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2360 .rescale({1.f, kNearlyIdentity.
height()}, Expect::kDeferredImage)
2361 .
run({0, 0, 80, 80});
2362 TestCase(r,
"Near-identity X axis, identity Y axis is a no-op",
2363 kDefaultMaxAllowedPercentImageDiff,
2365 .source({16, 16, 64, 64})
2366 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2367 .rescale({kNearlyIdentity.
width(), 1.f}, Expect::kDeferredImage)
2368 .
run({0, 0, 80, 80});
2370 TestCase(r,
"Identity X axis, 1-step Y axis preserves tile mode",
2373 .source({16, 16, 64, 64})
2374 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2375 .rescale({1.f, 0.5f}, Expect::kNewImage, tm)
2376 .
run({0, 0, 80, 80});
2377 TestCase(r,
"Near-identity X axis, 1-step Y axis preserves tile mode",
2380 .source({16, 16, 64, 64})
2381 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2382 .rescale({kNearlyIdentity.
width(), 0.5f}, Expect::kNewImage, tm)
2383 .
run({0, 0, 80, 80});
2384 TestCase(r,
"Identity X axis, 2-step Y axis preserves tile mode",
2387 .source({16, 16, 64, 64})
2388 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2389 .rescale({1.f, 0.25f}, Expect::kNewImage, tm)
2390 .
run({0, 0, 80, 80});
2391 TestCase(r,
"1-step X axis, 2-step Y axis preserves tile mode",
2392 periodic ? 23.1f : 17.22f,
2394 .source({16, 16, 64, 64})
2395 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2396 .rescale({.55f, 0.27f}, Expect::kNewImage, tm)
2397 .
run({0, 0, 80, 80});
2399 TestCase(r,
"1-step X axis, identity Y axis preserves tile mode",
2402 .source({16, 16, 64, 64})
2403 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2404 .rescale({0.5f, 1.f}, Expect::kNewImage, tm)
2405 .
run({0, 0, 80, 80});
2406 TestCase(r,
"1-step X axis, near-identity Y axis preserves tile mode",
2409 .source({16, 16, 64, 64})
2410 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2411 .rescale({0.5f, kNearlyIdentity.
height()}, Expect::kNewImage, tm)
2412 .
run({0, 0, 80, 80});
2413 TestCase(r,
"2-step X axis, identity Y axis preserves tile mode",
2416 .source({16, 16, 64, 64})
2417 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2418 .rescale({0.25f, 1.f}, Expect::kNewImage, tm)
2419 .
run({0, 0, 80, 80});
2420 TestCase(r,
"2-step X axis, 1-step Y axis preserves tile mode",
2421 periodic ? 14.9f : 13.61f,
2423 .source({16, 16, 64, 64})
2424 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2425 .rescale({.27f, 0.55f}, Expect::kNewImage, tm)
2426 .
run({0, 0, 80, 80});
2432 TestCase(r,
"Rescale applies layer bounds",
2433 kDefaultMaxAllowedPercentImageDiff,
2435 .source({16, 16, 64, 64})
2436 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2440 .
run({0, 0, 80, 80});
2447 TestCase(r,
"Identity rescale defers integer translation")
2448 .source({0, 0, 50, 50})
2449 .applyCrop({0, 0, 50, 50}, tm, Expect::kDeferredImage)
2451 .rescale({1.f, 1.f}, Expect::kDeferredImage)
2452 .
run({-15, -15, 45, 45});
2454 TestCase(r,
"Identity rescale applies complex transform")
2455 .source({16, 16, 64, 64})
2456 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2459 .
run({0, 0, 80, 80});
2461 TestCase(r,
"Near-identity rescale defers integer translation",
2462 kDefaultMaxAllowedPercentImageDiff,
2464 .source({0, 0, 50, 50})
2465 .applyCrop({0, 0, 50, 50}, tm, Expect::kDeferredImage)
2467 .rescale(kNearlyIdentity, Expect::kDeferredImage)
2468 .run({-15, -15, 45, 45});
2470 TestCase(r,
"Near-identity rescale applies complex transform")
2471 .source({0, 0, 50, 50})
2472 .applyCrop({0, 0, 50, 50}, tm, Expect::kDeferredImage)
2475 .run({-5, -5, 55, 55});
2477 TestCase(r,
"Identity rescale with deferred scale applies transform in first step")
2478 .source({0, 0, 50, 50})
2479 .applyCrop({0, 0, 50, 50}, tm, Expect::kDeferredImage)
2482 .
run({-10, -10, 30, 30});
2484 TestCase(r,
"Near-identity rescale with deferred scale applies transform in first step",
2485 kDefaultMaxAllowedPercentImageDiff,
2487 .source({0, 0, 50, 50})
2488 .applyCrop({0, 0, 50, 50}, tm, Expect::kDeferredImage)
2491 .run({-10, -10, 30, 30});
2495 TestCase(r,
"1-step rescale applies complex transform in first step",
2496 periodic ? 1.1f : kDefaultMaxAllowedPercentImageDiff,
2498 .source({16, 16, 64, 64})
2499 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2502 .
run({0, 0, 80, 80});
2504 TestCase(r,
"2-step rescale applies complex transform",
2505 periodic ? 10.05f: 3.7f,
2507 .source({16, 16, 64, 64})
2508 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2511 .
run({0, 0, 80, 80});
2515 TestCase(r,
"Rescale with deferred downscale applies transform before first step",
2516 kDefaultMaxAllowedPercentImageDiff,
2518 .source({16, 16, 64, 64})
2519 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2522 .
run({0, 0, 80, 80});
2525 TestCase(r,
"Rescale with deferred upscale applies transform with first step",
2528 .source({16, 16, 64, 64})
2529 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2532 .
run({0, 0, 80, 80});
2539 TestCase(r,
"Identity rescale applies color filter but defers tile mode")
2540 .source({0, 0, 50, 50})
2541 .applyCrop({0, 0, 50, 50}, tm, Expect::kDeferredImage)
2542 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
2543 .rescale({1.f, 1.f}, Expect::kNewImage, tm)
2544 .
run({-5, -5, 55, 55});
2546 TestCase(r,
"Near-identity rescale applies color filter but defers tile mode",
2547 kDefaultMaxAllowedPercentImageDiff,
2549 .source({0, 0, 50, 50})
2550 .applyCrop({0, 0, 50, 50}, tm, Expect::kDeferredImage)
2551 .applyColorFilter(alpha_modulate(0.5f), Expect::kDeferredImage)
2552 .rescale(kNearlyIdentity, Expect::kNewImage, tm)
2553 .run({-5, -5, 55, 55});
2555 TestCase(r,
"Rescale applies color filter but defers tile mode",
2556 kDefaultMaxAllowedPercentImageDiff,
2558 .source({16, 16, 64, 64})
2559 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2560 .applyColorFilter(alpha_modulate(0.75f), Expect::kDeferredImage)
2561 .rescale({0.5f, 0.5f}, Expect::kNewImage, tm)
2562 .
run({0, 0, 80, 80});
2568 TestCase(r,
"Rescale applies transparency-affecting color filter but defers tile mode")
2569 .source({16, 16, 64, 64})
2570 .applyCrop({16, 16, 64, 64}, tm, Expect::kDeferredImage)
2571 .applyColorFilter(affect_transparent(
SkColors::kGreen), Expect::kDeferredImage)
2572 .rescale({0.5f, 0.5f}, Expect::kNewImage, expectedTileMode)
2573 .
run({0, 0, 80, 80});
2578 static constexpr SkISize kSrcSize = {128,128};
2579 static constexpr SkIRect kIdentitySrc = {0,0,128,128};
2580 static constexpr SkIRect kSubsetSrc = {16,16,112,112};
2581 static constexpr SkIRect kOverlappingSrc = {-64, 16, 192, 112};
2582 static constexpr SkIRect kContainingSrc = {-64,-64,192,192};
2583 static constexpr SkIRect kDisjointSrc = {0,-200,128,-1};
2587 static constexpr SkIRect kDstRect = kIdentitySrc;
2590 static constexpr SkIRect kDesiredOutput = {-400, -400, 400, 400};
2607 return FilterResult::MakeFromImage(ctx, sourceImage,
SkRect::Make(
src), dstRect, {});
2612 "Empty dst rect returns empty FilterResult");
2614 "Empty src rect returns empty FilterResult");
2616 "Disjoint src rect returns empty FilterREsult");
2620 const char* label) {
2624 "Result subset is incorrect: %s", label);
2626 "Result layer bounds are incorrect: %s", label);
2629 testSuccess(kIdentitySrc, kIdentitySrc, kDstRect,
2630 "Identity src->dst preserves original image bounds");
2631 testSuccess(kSubsetSrc, kSubsetSrc, kDstRect,
2632 "Contained src rect is preserved, stretched to original dst bounds");
2633 testSuccess(kOverlappingSrc, {0,16,128,112}, {32,0,96,128},
2634 "Overlapping src rect is clipped and dst is scaled on clipped axis");
2635 testSuccess(kContainingSrc, kIdentitySrc, {32,32,96,96},
2636 "Containing src rect is clipped and dst is scaled on both axes");
#define DEF_TEST_SUITE(name, runner, ganeshCtsEnforcement, graphiteCtsEnforcement)
SkAssertResult(font.textToGlyphs("Hello", 5, SkTextEncoding::kUTF8, glyphs, std::size(glyphs))==count)
@ kTopLeft_GrSurfaceOrigin
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 SkStringPrintf(const char *format,...) SK_PRINTF_LIKE(1
Creates a new string and writes into it using a printf()-style format.
static constexpr bool SkToBool(const T &x)
#define REPORTER_ASSERT(r, cond,...)
static constexpr auto kColorType
int find(T *array, int N, T item)
static void Draw(const skif::Context &ctx, SkDevice *device, const skif::FilterResult &image, bool preserveDeviceState)
static std::optional< std::pair< float, float > > DeferredScaleFactors(const skif::FilterResult &image)
static void TrackStats(skif::Context *ctx, skif::Stats *stats)
static bool IsShaderTilingExpected(const skif::Context &ctx, const skif::FilterResult &image)
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 bool IsShaderClampingExpected(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)
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
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()
bool equals(const SkData *other) const
virtual sk_sp< SkSpecialImage > snapSpecial(const SkIRect &subset, bool forceCopy=false)
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)
bool getMinMaxScales(SkScalar scaleFactors[2]) const
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
FilterResult applyTransform(const Context &ctx, const LayerSpace< SkMatrix > &transform, const SkSamplingOptions &sampling) const
LayerSpace< SkIPoint > topLeft() const
void outset(const LayerSpace< SkISize > &delta)
bool intersect(const LayerSpace< SkIRect > &r)
LayerSpace< SkISize > size() const
const uint8_t uint32_t uint32_t GError ** error
static float max(float r, float g, float b)
static float min(float r, float g, float b)
constexpr SkColor4f kGreen
constexpr SkColor4f kCyan
constexpr SkColor4f kTransparent
constexpr SkColor4f kBlue
unsigned useCenter Optional< SkMatrix > matrix
Optional< SkRect > bounds
sk_sp< const SkImage > image
PODArray< SkColor > colors
SkSamplingOptions sampling
SK_API sk_sp< SkShader > Empty()
bool AsBitmap(const SkSpecialImage *img, SkBitmap *result)
DEF_SWITCHES_START aot vmservice shared library name
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
const myers::Point & get< 0 >(const myers::Segment &s)
SK_API sk_sp< PrecompileShader > ColorFilter(SkSpan< const sk_sp< PrecompileShader > > shaders, SkSpan< const sk_sp< PrecompileColorFilter > > colorFilters)
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)
sk_sp< Backend > MakeGraphiteBackend(skgpu::graphite::Recorder *recorder, const SkSurfaceProps &surfaceProps, SkColorType colorType)
skgpu::graphite::Transform Transform
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< 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)