Support complex matrices in Image image filter
Instead of using drawImageRect from fSrcRect to a dst rect already mapped by the CTM, this concats the CTM to the intermediate canvas and then does a drawImageRect from fSrcRect to fDstRect. It also updates the passthrough optimization to require positive scale factors so that mirrors aren't accidentally ignored. Bug: skia:11994, skia:12015 Change-Id: I6f3ee7afd842818dc9b8146beb866ac8b8f9a990 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/410098 Commit-Queue: Michael Ludwig <michaelludwig@google.com> Reviewed-by: Brian Salomon <bsalomon@google.com>
This commit is contained in:
parent
c6260f9742
commit
ee8f277198
@ -38,6 +38,8 @@ protected:
|
|||||||
SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix& ctm,
|
SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix& ctm,
|
||||||
MapDirection, const SkIRect* inputRect) const override;
|
MapDirection, const SkIRect* inputRect) const override;
|
||||||
|
|
||||||
|
bool onCanHandleComplexCTM() const override { return true; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend void ::SkRegisterImageImageFilterFlattenable();
|
friend void ::SkRegisterImageImageFilterFlattenable();
|
||||||
SK_FLATTENABLE_HOOKS(SkImageImageFilter)
|
SK_FLATTENABLE_HOOKS(SkImageImageFilter)
|
||||||
@ -101,57 +103,56 @@ void SkImageImageFilter::flatten(SkWriteBuffer& buffer) const {
|
|||||||
|
|
||||||
sk_sp<SkSpecialImage> SkImageImageFilter::onFilterImage(const Context& ctx,
|
sk_sp<SkSpecialImage> SkImageImageFilter::onFilterImage(const Context& ctx,
|
||||||
SkIPoint* offset) const {
|
SkIPoint* offset) const {
|
||||||
SkRect dstRect;
|
const SkRect dstBounds = ctx.ctm().mapRect(fDstRect);
|
||||||
ctx.ctm().mapRect(&dstRect, fDstRect);
|
const SkIRect dstIBounds = dstBounds.roundOut();
|
||||||
|
|
||||||
SkRect bounds = SkRect::MakeIWH(fImage->width(), fImage->height());
|
// Quick check to see if we can return the image directly, which can be done if the transform
|
||||||
if (fSrcRect == bounds) {
|
// ends up being an integer translate and sampling would have no effect on the output.
|
||||||
int iLeft = dstRect.fLeft;
|
// TODO: This currently means cubic sampling can be skipped, even though it would change results
|
||||||
int iTop = dstRect.fTop;
|
// for integer translation draws.
|
||||||
// TODO: this seems to be a very noise-prone way to determine this (esp. the floating-point
|
// TODO: This is prone to false negatives due to the floating point math; we could probably
|
||||||
// widths & heights).
|
// get away with dimensions and translates being epsilon close to integers.
|
||||||
if (dstRect.width() == bounds.width() && dstRect.height() == bounds.height() &&
|
const bool passthroughTransform = ctx.ctm().isScaleTranslate() &&
|
||||||
iLeft == dstRect.fLeft && iTop == dstRect.fTop) {
|
ctx.ctm().getScaleX() > 0.f &&
|
||||||
// The dest is just an un-scaled integer translation of the entire image; return it
|
ctx.ctm().getScaleY() > 0.f;
|
||||||
offset->fX = iLeft;
|
const bool passthroughSrcOffsets = SkScalarIsInt(fSrcRect.fLeft) &&
|
||||||
offset->fY = iTop;
|
SkScalarIsInt(fSrcRect.fTop);
|
||||||
|
const bool passthroughDstOffsets = SkScalarIsInt(dstBounds.fLeft) &&
|
||||||
|
SkScalarIsInt(dstBounds.fTop);
|
||||||
|
const bool passthroughDims =
|
||||||
|
SkScalarIsInt(fSrcRect.width()) && fSrcRect.width() == dstBounds.width() &&
|
||||||
|
SkScalarIsInt(fSrcRect.height()) && fSrcRect.height() == dstBounds.height();
|
||||||
|
|
||||||
return SkSpecialImage::MakeFromImage(ctx.getContext(),
|
if (passthroughTransform && passthroughSrcOffsets && passthroughDstOffsets && passthroughDims) {
|
||||||
SkIRect::MakeWH(fImage->width(), fImage->height()),
|
// Can pass through fImage directly, applying the dst's location to 'offset'. If fSrcRect
|
||||||
fImage, ctx.surfaceProps());
|
// extends outside of the image, we adjust dst to match since those areas would have been
|
||||||
|
// transparent black anyways.
|
||||||
|
SkIRect srcIBounds = fSrcRect.roundOut();
|
||||||
|
SkIPoint srcOffset = srcIBounds.topLeft();
|
||||||
|
if (!srcIBounds.intersect(SkIRect::MakeWH(fImage->width(), fImage->height()))) {
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*offset = dstIBounds.topLeft() + srcIBounds.topLeft() - srcOffset;
|
||||||
|
return SkSpecialImage::MakeFromImage(ctx.getContext(), srcIBounds, fImage,
|
||||||
|
ctx.surfaceProps());
|
||||||
}
|
}
|
||||||
|
|
||||||
const SkIRect dstIRect = dstRect.roundOut();
|
sk_sp<SkSpecialSurface> surf(ctx.makeSurface(dstIBounds.size()));
|
||||||
|
|
||||||
sk_sp<SkSpecialSurface> surf(ctx.makeSurface(dstIRect.size()));
|
|
||||||
if (!surf) {
|
if (!surf) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
SkCanvas* canvas = surf->getCanvas();
|
SkCanvas* canvas = surf->getCanvas();
|
||||||
SkASSERT(canvas);
|
|
||||||
|
|
||||||
// TODO: it seems like this clear shouldn't be necessary (see skbug.com/5075)
|
|
||||||
canvas->clear(0x0);
|
|
||||||
|
|
||||||
SkPaint paint;
|
|
||||||
|
|
||||||
// Subtract off the integer component of the translation (will be applied in offset, below).
|
// Subtract off the integer component of the translation (will be applied in offset, below).
|
||||||
dstRect.offset(-SkIntToScalar(dstIRect.fLeft), -SkIntToScalar(dstIRect.fTop));
|
canvas->translate(-dstIBounds.fLeft, -dstIBounds.fTop);
|
||||||
paint.setBlendMode(SkBlendMode::kSrc);
|
canvas->concat(ctx.ctm());
|
||||||
|
// TODO(skbug.com/5075): Canvases from GPU special surfaces come with unitialized content
|
||||||
// FIXME: this probably shouldn't be necessary, but drawImageRect asserts
|
canvas->clear(SK_ColorTRANSPARENT);
|
||||||
SkSamplingOptions sampling = fSampling;
|
canvas->drawImageRect(fImage.get(), fSrcRect, fDstRect, fSampling, nullptr,
|
||||||
// None filtering when it's translate-only (even for cubicresampling? <reed>)
|
|
||||||
if (fSrcRect.width() == dstRect.width() && fSrcRect.height() == dstRect.height()) {
|
|
||||||
sampling = SkSamplingOptions();
|
|
||||||
}
|
|
||||||
canvas->drawImageRect(fImage.get(), fSrcRect, dstRect, sampling, &paint,
|
|
||||||
SkCanvas::kStrict_SrcRectConstraint);
|
SkCanvas::kStrict_SrcRectConstraint);
|
||||||
|
|
||||||
offset->fX = dstIRect.fLeft;
|
*offset = dstIBounds.topLeft();
|
||||||
offset->fY = dstIRect.fTop;
|
|
||||||
return surf->makeImageSnapshot();
|
return surf->makeImageSnapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user