Fix reverse bounds calculation for SkImageFilters::Compose
Since reverse bounds calculation is more-or-less the inverse operation of forward bounds calculation, it needs to be computed from the outer filter and then the inner filter. Previously bounds were always computed from inner and then outer, which is only valid for forward bounds calculations. Bug: skia:10888 Change-Id: I94a2170617ed01c8ec3066f3518c6baa06da952d Reviewed-on: https://skia-review.googlesource.com/c/skia/+/337401 Reviewed-by: Florin Malita <fmalita@chromium.org> Commit-Queue: Michael Ludwig <michaelludwig@google.com>
This commit is contained in:
parent
b432754c00
commit
cbbe0b0df6
@ -216,3 +216,101 @@ private:
|
||||
};
|
||||
|
||||
DEF_GM(return new ImageFilterMatrixWLocalMatrix();)
|
||||
|
||||
class ImageFilterComposedTransform : public skiagm::GM {
|
||||
public:
|
||||
|
||||
// Start at 70 degrees since that highlighted the issue in skbug.com/10888
|
||||
ImageFilterComposedTransform() : fDegrees(70.f) {}
|
||||
|
||||
protected:
|
||||
SkString onShortName() override {
|
||||
return SkString("imagefilter_composed_transform");
|
||||
}
|
||||
|
||||
SkISize onISize() override {
|
||||
return SkISize::Make(512, 512);
|
||||
}
|
||||
|
||||
bool onAnimate(double nanos) override {
|
||||
// Animate the rotation angle to test a variety of transformations
|
||||
fDegrees = TimeUtils::Scaled(1e-9f * nanos, 360.f);
|
||||
return true;
|
||||
}
|
||||
|
||||
void onOnceBeforeDraw() override {
|
||||
fImage = GetResourceAsImage("images/mandrill_256.png");
|
||||
}
|
||||
|
||||
void onDraw(SkCanvas* canvas) override {
|
||||
SkMatrix matrix = SkMatrix::RotateDeg(fDegrees);
|
||||
// All four quadrants should render the same
|
||||
this->drawFilter(canvas, 0.f, 0.f, this->makeDirectFilter(matrix));
|
||||
this->drawFilter(canvas, 256.f, 0.f, this->makeEarlyComposeFilter(matrix));
|
||||
this->drawFilter(canvas, 0.f, 256.f, this->makeLateComposeFilter(matrix));
|
||||
this->drawFilter(canvas, 256.f, 256.f, this->makeFullComposeFilter(matrix));
|
||||
}
|
||||
|
||||
private:
|
||||
SkScalar fDegrees;
|
||||
sk_sp<SkImage> fImage;
|
||||
|
||||
void drawFilter(SkCanvas* canvas, SkScalar tx, SkScalar ty, sk_sp<SkImageFilter> filter) const {
|
||||
SkPaint p;
|
||||
p.setImageFilter(std::move(filter));
|
||||
|
||||
canvas->save();
|
||||
canvas->translate(tx, ty);
|
||||
canvas->clipRect(SkRect::MakeIWH(256, 256));
|
||||
canvas->scale(0.5f, 0.5f);
|
||||
canvas->translate(128, 128);
|
||||
canvas->drawImage(fImage, 0, 0, &p);
|
||||
canvas->restore();
|
||||
}
|
||||
|
||||
// offset(matrix(offset))
|
||||
sk_sp<SkImageFilter> makeDirectFilter(const SkMatrix& matrix) const {
|
||||
SkPoint v = {fImage->width() / 2.f, fImage->height() / 2.f};
|
||||
sk_sp<SkImageFilter> filter = SkImageFilters::Offset(-v.fX, -v.fY, nullptr);
|
||||
filter = SkImageFilters::MatrixTransform(matrix, SkFilterQuality::kLow_SkFilterQuality,
|
||||
std::move(filter));
|
||||
filter = SkImageFilters::Offset(v.fX, v.fY, std::move(filter));
|
||||
return filter;
|
||||
}
|
||||
|
||||
// offset(compose(matrix, offset))
|
||||
sk_sp<SkImageFilter> makeEarlyComposeFilter(const SkMatrix& matrix) const {
|
||||
SkPoint v = {fImage->width() / 2.f, fImage->height() / 2.f};
|
||||
sk_sp<SkImageFilter> offset = SkImageFilters::Offset(-v.fX, -v.fY, nullptr);
|
||||
sk_sp<SkImageFilter> filter = SkImageFilters::MatrixTransform(
|
||||
matrix, SkFilterQuality::kLow_SkFilterQuality, nullptr);
|
||||
filter = SkImageFilters::Compose(std::move(filter), std::move(offset));
|
||||
filter = SkImageFilters::Offset(v.fX, v.fY, std::move(filter));
|
||||
return filter;
|
||||
}
|
||||
|
||||
// compose(offset, matrix(offset))
|
||||
sk_sp<SkImageFilter> makeLateComposeFilter(const SkMatrix& matrix) const {
|
||||
SkPoint v = {fImage->width() / 2.f, fImage->height() / 2.f};
|
||||
sk_sp<SkImageFilter> filter = SkImageFilters::Offset(-v.fX, -v.fY, nullptr);
|
||||
filter = SkImageFilters::MatrixTransform(matrix, SkFilterQuality::kLow_SkFilterQuality,
|
||||
std::move(filter));
|
||||
sk_sp<SkImageFilter> offset = SkImageFilters::Offset(v.fX, v.fY, nullptr);
|
||||
filter = SkImageFilters::Compose(std::move(offset), std::move(filter));
|
||||
return filter;
|
||||
}
|
||||
|
||||
// compose(offset, compose(matrix, offset))
|
||||
sk_sp<SkImageFilter> makeFullComposeFilter(const SkMatrix& matrix) const {
|
||||
SkPoint v = {fImage->width() / 2.f, fImage->height() / 2.f};
|
||||
sk_sp<SkImageFilter> offset = SkImageFilters::Offset(-v.fX, -v.fY, nullptr);
|
||||
sk_sp<SkImageFilter> filter = SkImageFilters::MatrixTransform(
|
||||
matrix, SkFilterQuality::kLow_SkFilterQuality, nullptr);
|
||||
filter = SkImageFilters::Compose(std::move(filter), std::move(offset));
|
||||
offset = SkImageFilters::Offset(v.fX, v.fY, nullptr);
|
||||
filter = SkImageFilters::Compose(std::move(offset), std::move(filter));
|
||||
return filter;
|
||||
}
|
||||
};
|
||||
|
||||
DEF_GM(return new ImageFilterComposedTransform();)
|
||||
|
@ -116,7 +116,16 @@ SkIRect SkComposeImageFilterImpl::onFilterBounds(const SkIRect& src, const SkMat
|
||||
const SkImageFilter* outer = this->getInput(0);
|
||||
const SkImageFilter* inner = this->getInput(1);
|
||||
|
||||
const SkIRect innerRect = inner->filterBounds(src, ctm, dir, inputRect);
|
||||
return outer->filterBounds(innerRect, ctm, dir,
|
||||
kReverse_MapDirection == dir ? &innerRect : nullptr);
|
||||
if (dir == kReverse_MapDirection) {
|
||||
// The output 'src' is processed by the outer filter, producing its required input bounds,
|
||||
// which is then the output bounds required of the inner filter. We pass the inputRect to
|
||||
// outer and not inner to match the default recursion logic of onGetInputLayerBounds
|
||||
const SkIRect outerRect = outer->filterBounds(src, ctm, dir, inputRect);
|
||||
return inner->filterBounds(outerRect, ctm, dir);
|
||||
} else {
|
||||
// The input 'src' is processed by the inner filter, producing the input bounds for the
|
||||
// outer filter of the composition, which then produces the final forward output bounds
|
||||
const SkIRect innerRect = inner->filterBounds(src, ctm, dir);
|
||||
return outer->filterBounds(innerRect, ctm, dir);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user