Fix filter primitive bounds computations.
Make each filter responsible for expanding its destination bounds. Previously, we were using a union of all intermediate bounds sizes via join() calls in many image filters' computeFastBounds(), due to the fact that those filters could only produce bitmaps the same size as their inputs. Now, we compute optimal bounds for each filter as follows: 1) Pass the (unmodified) clip bounds to the root node of the DAG in the first recursive call to onFilterImage() as the Context's fClipBounds. 2) Reverse-map the clip: when recursing up the DAG in filterInput[GPU](), apply filter-specific expansion to the clip by calling calling onFilterNodeBounds(... kReverse). This allows upstream nodes to have a clip that respects the current node's requirements. This is done via helper function mapContext(). 3) Forward-map the source bitmap: just prior to applying the crop rect in applyCropRect(), we determine the filter's preferred bounds by mapping the source bitmap bounds forwards via onFilterNodeBounds(..., kForward). NOTE: GMs affected by this change: fast_slow_blurimagefilter: fast and slow paths now produce the same result spritebitmap: drawSprite() and drawBitmap() paths now produce the same result filterfastbounds: fast bounds are optimized; all drop-shadow results now appear apply-filter: snug and not-snug cases give same results dropshadowimagefilter: drawSprite() results now show shadows draw-with-filter: no artifacts on erode edges; blur edges no longer clipped displacement, imagefiltersbase, imagefiltersclipped, imagefilterscropexpand, imagefiltersscaled, matriximagefilter, resizeimagefilter, localmatriximagefilter, testimagefilters: fixed incorrect clipping imagefilterstransformed, morphology: no artifacts on erode edges BUG=skia:1062,skia:3194,skia:3939,skia:4337,skia:4526 Review URL: https://codereview.chromium.org/1308703007
This commit is contained in:
parent
02046c50b2
commit
db64af3b17
@ -346,6 +346,25 @@ protected:
|
||||
// implementation recursively unions all input bounds, or returns false if
|
||||
// no inputs.
|
||||
virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) const;
|
||||
enum MapDirection {
|
||||
kForward_MapDirection,
|
||||
kReverse_MapDirection
|
||||
};
|
||||
|
||||
/**
|
||||
* Performs a forwards or reverse mapping of the given rect to accommodate
|
||||
* this filter's margin requirements. kForward_MapDirection is used to
|
||||
* determine the destination pixels which would be touched by filtering
|
||||
* the given given source rect (e.g., given source bitmap bounds,
|
||||
* determine the optimal bounds of the filtered offscreen bitmap).
|
||||
* kReverse_MapDirection is used to determine which pixels of the
|
||||
* input(s) would be required to fill the given destination rect
|
||||
* (e.g., clip bounds). NOTE: these operations may not be the
|
||||
* inverse of the other. For example, blurring expands the given rect
|
||||
* in both forward and reverse directions. Unlike
|
||||
* onFilterBounds(), this function is non-recursive.
|
||||
*/
|
||||
virtual void onFilterNodeBounds(const SkIRect&, const SkMatrix&, SkIRect*, MapDirection) const;
|
||||
|
||||
// Helper function which invokes filter processing on the input at the
|
||||
// specified "index". If the input is null, it leaves "result" and
|
||||
@ -405,6 +424,14 @@ protected:
|
||||
virtual bool asFragmentProcessor(GrFragmentProcessor**, GrTexture*, const SkMatrix&,
|
||||
const SkIRect& bounds) const;
|
||||
|
||||
/**
|
||||
* Creates a modified Context for use when recursing up the image filter DAG.
|
||||
* The clip bounds are adjusted to accommodate any margins that this
|
||||
* filter requires by calling this node's
|
||||
* onFilterNodeBounds(..., kReverse_MapDirection).
|
||||
*/
|
||||
Context mapContext(const Context& ctx) const;
|
||||
|
||||
private:
|
||||
friend class SkGraphics;
|
||||
static void PurgeCache();
|
||||
|
@ -27,7 +27,8 @@ protected:
|
||||
void flatten(SkWriteBuffer&) const override;
|
||||
bool onFilterImage(Proxy*, const SkBitmap& src, const Context&, SkBitmap* result,
|
||||
SkIPoint* offset) const override;
|
||||
bool onFilterBounds(const SkIRect& src, const SkMatrix&, SkIRect* dst) const override;
|
||||
void onFilterNodeBounds(const SkIRect& src, const SkMatrix&,
|
||||
SkIRect* dst, MapDirection) const override;
|
||||
bool canFilterImageGPU() const override { return true; }
|
||||
bool filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx, SkBitmap* result,
|
||||
SkIPoint* offset) const override;
|
||||
|
@ -40,6 +40,7 @@ public:
|
||||
|
||||
virtual bool onFilterBounds(const SkIRect& src, const SkMatrix&,
|
||||
SkIRect* dst) const override;
|
||||
void onFilterNodeBounds(const SkIRect&, const SkMatrix&, SkIRect*, MapDirection) const override;
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
bool canFilterImageGPU() const override { return true; }
|
||||
|
@ -35,7 +35,8 @@ protected:
|
||||
void flatten(SkWriteBuffer&) const override;
|
||||
bool onFilterImage(Proxy*, const SkBitmap& source, const Context&, SkBitmap* result,
|
||||
SkIPoint* loc) const override;
|
||||
bool onFilterBounds(const SkIRect& src, const SkMatrix&, SkIRect* dst) const override;
|
||||
void onFilterNodeBounds(const SkIRect& src, const SkMatrix&,
|
||||
SkIRect* dst, MapDirection) const override;
|
||||
|
||||
private:
|
||||
SkDropShadowImageFilter(SkScalar dx, SkScalar dy, SkScalar sigmaX, SkScalar sigmaY, SkColor,
|
||||
|
@ -79,7 +79,7 @@ protected:
|
||||
|
||||
bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
|
||||
SkBitmap* result, SkIPoint* loc) const override;
|
||||
bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) const override;
|
||||
void onFilterNodeBounds(const SkIRect&, const SkMatrix&, SkIRect*, MapDirection) const override;
|
||||
bool canComputeFastBounds() const override;
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
|
@ -16,7 +16,8 @@
|
||||
class SK_API SkMorphologyImageFilter : public SkImageFilter {
|
||||
public:
|
||||
void computeFastBounds(const SkRect& src, SkRect* dst) const override;
|
||||
bool onFilterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst) const override;
|
||||
void onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
|
||||
SkIRect* dst, MapDirection) const override;
|
||||
|
||||
/**
|
||||
* All morphology procs have the same signature: src is the source buffer, dst the
|
||||
|
@ -30,7 +30,7 @@ protected:
|
||||
void flatten(SkWriteBuffer&) const override;
|
||||
bool onFilterImage(Proxy*, const SkBitmap& src, const Context&, SkBitmap* result,
|
||||
SkIPoint* loc) const override;
|
||||
bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) const override;
|
||||
void onFilterNodeBounds(const SkIRect&, const SkMatrix&, SkIRect*, MapDirection) const override;
|
||||
|
||||
private:
|
||||
SkOffsetImageFilter(SkScalar dx, SkScalar dy, SkImageFilter* input, const CropRect*);
|
||||
|
@ -25,6 +25,7 @@ public:
|
||||
SkBitmap* dst, SkIPoint* offset) const override;
|
||||
bool onFilterBounds(const SkIRect& src, const SkMatrix&,
|
||||
SkIRect* dst) const override;
|
||||
void onFilterNodeBounds(const SkIRect&, const SkMatrix&, SkIRect*, MapDirection) const override;
|
||||
void computeFastBounds(const SkRect& src, SkRect* dst) const override;
|
||||
|
||||
SK_TO_STRING_OVERRIDE()
|
||||
|
@ -40,8 +40,6 @@
|
||||
#include "GrRenderTarget.h"
|
||||
#endif
|
||||
|
||||
#define SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
|
||||
|
||||
/*
|
||||
* Return true if the drawing this rect would hit every pixels in the canvas.
|
||||
*
|
||||
@ -1084,6 +1082,10 @@ bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
|
||||
} else {
|
||||
bounds = nullptr;
|
||||
}
|
||||
#else
|
||||
if (bounds && !imageFilter->canComputeFastBounds()) {
|
||||
bounds = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
SkIRect ir;
|
||||
@ -1370,7 +1372,11 @@ void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
|
||||
const SkBitmap& src = srcDev->accessBitmap(false);
|
||||
SkMatrix matrix = *iter.fMatrix;
|
||||
matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
|
||||
#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
|
||||
SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
|
||||
#else
|
||||
SkIRect clipBounds = iter.fClip->getBounds().makeOffset(-pos.x(), -pos.y());
|
||||
#endif
|
||||
SkAutoTUnref<SkImageFilter::Cache> cache(dstDev->getImageFilterCache());
|
||||
SkImageFilter::Context ctx(matrix, clipBounds, cache.get(),
|
||||
SkImageFilter::kApprox_SizeConstraint);
|
||||
|
@ -411,7 +411,11 @@ void SkBaseDevice::drawBitmapAsSprite(const SkDraw& draw, const SkBitmap& bitmap
|
||||
SkIPoint offset = SkIPoint::Make(0, 0);
|
||||
SkMatrix matrix = *draw.fMatrix;
|
||||
matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
|
||||
#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
|
||||
const SkIRect clipBounds = bitmap.bounds();
|
||||
#else
|
||||
const SkIRect clipBounds = draw.fClip->getBounds().makeOffset(-x, -y);
|
||||
#endif
|
||||
SkAutoTUnref<SkImageFilter::Cache> cache(this->getImageFilterCache());
|
||||
SkImageFilter::Context ctx(matrix, clipBounds, cache.get(),
|
||||
SkImageFilter::kApprox_SizeConstraint);
|
||||
|
@ -277,7 +277,7 @@ bool SkImageFilter::filterInput(int index, Proxy* proxy, const SkBitmap& src,
|
||||
}
|
||||
Context ctx(origCtx.ctm(), origCtx.clipBounds(), origCtx.cache(), constraint);
|
||||
|
||||
return input->filterImage(proxy, src, ctx, result, offset);
|
||||
return input->filterImage(proxy, src, this->mapContext(ctx), result, offset);
|
||||
}
|
||||
|
||||
bool SkImageFilter::filterBounds(const SkIRect& src, const SkMatrix& ctm,
|
||||
@ -405,7 +405,12 @@ bool SkImageFilter::applyCropRect(const Context& ctx, const SkBitmap& src,
|
||||
}
|
||||
src.getBounds(srcBounds);
|
||||
srcBounds->offset(srcOffset);
|
||||
return fCropRect.applyTo(*srcBounds, ctx, dstBounds) && srcBounds->intersect(*dstBounds);
|
||||
#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
|
||||
return fCropRect.applyTo(*srcBounds, ctx, dstBounds);
|
||||
#else
|
||||
this->onFilterNodeBounds(*srcBounds, ctx.ctm(), dstBounds, kForward_MapDirection);
|
||||
return fCropRect.applyTo(*dstBounds, ctx, dstBounds);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool SkImageFilter::applyCropRect(const Context& ctx, Proxy* proxy, const SkBitmap& src,
|
||||
@ -413,7 +418,13 @@ bool SkImageFilter::applyCropRect(const Context& ctx, Proxy* proxy, const SkBitm
|
||||
SkIRect srcBounds;
|
||||
src.getBounds(&srcBounds);
|
||||
srcBounds.offset(*srcOffset);
|
||||
#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
|
||||
if (!fCropRect.applyTo(srcBounds, ctx, bounds)) {
|
||||
#else
|
||||
SkIRect dstBounds;
|
||||
this->onFilterNodeBounds(srcBounds, ctx.ctm(), &dstBounds, kForward_MapDirection);
|
||||
if (!fCropRect.applyTo(dstBounds, ctx, bounds)) {
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -441,26 +452,44 @@ bool SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
|
||||
return true;
|
||||
}
|
||||
|
||||
SkIRect bounds;
|
||||
SkIRect bounds, totalBounds;
|
||||
this->onFilterNodeBounds(src, ctm, &bounds, kReverse_MapDirection);
|
||||
for (int i = 0; i < fInputCount; ++i) {
|
||||
SkImageFilter* filter = this->getInput(i);
|
||||
SkIRect rect = src;
|
||||
if (filter && !filter->filterBounds(src, ctm, &rect)) {
|
||||
SkIRect rect = bounds;
|
||||
if (filter && !filter->filterBounds(bounds, ctm, &rect)) {
|
||||
return false;
|
||||
}
|
||||
if (0 == i) {
|
||||
bounds = rect;
|
||||
totalBounds = rect;
|
||||
} else {
|
||||
bounds.join(rect);
|
||||
totalBounds.join(rect);
|
||||
}
|
||||
}
|
||||
|
||||
// don't modify dst until now, so we don't accidentally change it in the
|
||||
// loop, but then return false on the next filter.
|
||||
*dst = bounds;
|
||||
*dst = totalBounds;
|
||||
return true;
|
||||
}
|
||||
|
||||
void SkImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix&,
|
||||
SkIRect* dst, MapDirection) const {
|
||||
*dst = src;
|
||||
}
|
||||
|
||||
|
||||
SkImageFilter::Context SkImageFilter::mapContext(const Context& ctx) const {
|
||||
#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
|
||||
return ctx;
|
||||
#else
|
||||
SkIRect clipBounds;
|
||||
this->onFilterNodeBounds(ctx.clipBounds(), ctx.ctm(), &clipBounds,
|
||||
MapDirection::kReverse_MapDirection);
|
||||
return Context(ctx.ctm(), clipBounds, ctx.cache(), ctx.sizeConstraint());
|
||||
#endif
|
||||
}
|
||||
|
||||
bool SkImageFilter::asFragmentProcessor(GrFragmentProcessor**, GrTexture*,
|
||||
const SkMatrix&, const SkIRect&) const {
|
||||
return false;
|
||||
@ -506,7 +535,7 @@ bool SkImageFilter::filterInputGPU(int index, SkImageFilter::Proxy* proxy,
|
||||
}
|
||||
Context ctx(origCtx.ctm(), origCtx.clipBounds(), origCtx.cache(), constraint);
|
||||
|
||||
if (input->filterImage(proxy, src, ctx, result, offset)) {
|
||||
if (input->filterImage(proxy, src, this->mapContext(ctx), result, offset)) {
|
||||
if (!result->getTexture()) {
|
||||
const SkImageInfo info = result->info();
|
||||
if (kUnknown_SkColorType == info.colorType()) {
|
||||
|
@ -97,30 +97,33 @@ void SkMatrixImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) cons
|
||||
getInput(0)->computeFastBounds(src, &bounds);
|
||||
}
|
||||
fTransform.mapRect(dst, bounds);
|
||||
#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
|
||||
dst->join(bounds); // Work around for skia:3194
|
||||
#endif
|
||||
}
|
||||
|
||||
bool SkMatrixImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
|
||||
SkIRect* dst) const {
|
||||
SkMatrix transformInverse;
|
||||
if (!fTransform.invert(&transformInverse)) {
|
||||
return false;
|
||||
}
|
||||
void SkMatrixImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
|
||||
SkIRect* dst, MapDirection direction) const {
|
||||
SkMatrix matrix;
|
||||
if (!ctm.invert(&matrix)) {
|
||||
return false;
|
||||
*dst = src;
|
||||
return;
|
||||
}
|
||||
if (kForward_MapDirection == direction) {
|
||||
matrix.postConcat(fTransform);
|
||||
} else {
|
||||
SkMatrix transformInverse;
|
||||
if (!fTransform.invert(&transformInverse)) {
|
||||
*dst = src;
|
||||
return;
|
||||
}
|
||||
matrix.postConcat(transformInverse);
|
||||
}
|
||||
matrix.postConcat(transformInverse);
|
||||
matrix.postConcat(ctm);
|
||||
SkRect floatBounds;
|
||||
matrix.mapRect(&floatBounds, SkRect::Make(src));
|
||||
SkIRect bounds = floatBounds.roundOut();
|
||||
if (getInput(0) && !getInput(0)->filterBounds(bounds, ctm, &bounds)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*dst = bounds;
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef SK_IGNORE_TO_STRING
|
||||
|
@ -46,8 +46,8 @@ protected:
|
||||
|
||||
virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
|
||||
SkBitmap* result, SkIPoint* loc) const override;
|
||||
virtual bool onFilterBounds(const SkIRect& src, const SkMatrix&,
|
||||
SkIRect* dst) const override;
|
||||
virtual void onFilterNodeBounds(const SkIRect& src, const SkMatrix&,
|
||||
SkIRect* dst, MapDirection) const override;
|
||||
|
||||
private:
|
||||
SkMatrix fTransform;
|
||||
|
@ -221,6 +221,7 @@ private:
|
||||
int controlOps; // Number of control ops in this Save block, including the Save.
|
||||
Bounds bounds; // Bounds of everything in the block.
|
||||
const SkPaint* paint; // Unowned. If set, adjusts the bounds of all ops in this block.
|
||||
SkMatrix ctm;
|
||||
};
|
||||
|
||||
// Only Restore, SetMatrix, and Concat change the CTM.
|
||||
@ -301,6 +302,7 @@ private:
|
||||
sb.bounds =
|
||||
PaintMayAffectTransparentBlack(paint) ? fCurrentClipBounds : Bounds::MakeEmpty();
|
||||
sb.paint = paint;
|
||||
sb.ctm = this->fCTM;
|
||||
|
||||
fSaveStack.push(sb);
|
||||
this->pushControl();
|
||||
@ -563,9 +565,15 @@ private:
|
||||
|
||||
bool adjustForSaveLayerPaints(SkRect* rect, int savesToIgnore = 0) const {
|
||||
for (int i = fSaveStack.count() - 1 - savesToIgnore; i >= 0; i--) {
|
||||
SkMatrix inverse;
|
||||
if (!fSaveStack[i].ctm.invert(&inverse)) {
|
||||
return false;
|
||||
}
|
||||
inverse.mapRect(rect);
|
||||
if (!AdjustForPaint(fSaveStack[i].paint, rect)) {
|
||||
return false;
|
||||
}
|
||||
fSaveStack[i].ctm.mapRect(rect);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -82,7 +82,10 @@ bool SkBlurImageFilter::onFilterImage(Proxy* proxy,
|
||||
}
|
||||
|
||||
SkIRect srcBounds, dstBounds;
|
||||
if (!this->applyCropRect(ctx, src, srcOffset, &dstBounds, &srcBounds)) {
|
||||
if (!this->applyCropRect(this->mapContext(ctx), src, srcOffset, &dstBounds, &srcBounds)) {
|
||||
return false;
|
||||
}
|
||||
if (!srcBounds.intersect(dstBounds)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -184,17 +187,12 @@ void SkBlurImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const
|
||||
SkScalarMul(fSigma.height(), SkIntToScalar(3)));
|
||||
}
|
||||
|
||||
bool SkBlurImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
|
||||
SkIRect* dst) const {
|
||||
SkIRect bounds = src;
|
||||
void SkBlurImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
|
||||
SkIRect* dst, MapDirection) const {
|
||||
*dst = src;
|
||||
SkVector sigma = mapSigma(fSigma, ctm);
|
||||
bounds.outset(SkScalarCeilToInt(SkScalarMul(sigma.x(), SkIntToScalar(3))),
|
||||
SkScalarCeilToInt(SkScalarMul(sigma.y(), SkIntToScalar(3))));
|
||||
if (this->getInput(0) && !this->getInput(0)->filterBounds(bounds, ctm, &bounds)) {
|
||||
return false;
|
||||
}
|
||||
*dst = bounds;
|
||||
return true;
|
||||
dst->outset(SkScalarCeilToInt(SkScalarMul(sigma.x(), SkIntToScalar(3))),
|
||||
SkScalarCeilToInt(SkScalarMul(sigma.y(), SkIntToScalar(3))));
|
||||
}
|
||||
|
||||
bool SkBlurImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx,
|
||||
@ -206,7 +204,10 @@ bool SkBlurImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const
|
||||
return false;
|
||||
}
|
||||
SkIRect srcBounds, dstBounds;
|
||||
if (!this->applyCropRect(ctx, input, srcOffset, &dstBounds, &srcBounds)) {
|
||||
if (!this->applyCropRect(this->mapContext(ctx), input, srcOffset, &dstBounds, &srcBounds)) {
|
||||
return false;
|
||||
}
|
||||
if (!srcBounds.intersect(dstBounds)) {
|
||||
return false;
|
||||
}
|
||||
GrTexture* source = input.getTexture();
|
||||
|
@ -35,7 +35,9 @@ bool SkComposeImageFilter::onFilterImage(Proxy* proxy,
|
||||
|
||||
SkMatrix outerMatrix(ctx.ctm());
|
||||
outerMatrix.postTranslate(SkIntToScalar(-innerOffset.x()), SkIntToScalar(-innerOffset.y()));
|
||||
Context outerContext(outerMatrix, ctx.clipBounds(), ctx.cache(), ctx.sizeConstraint());
|
||||
SkIRect clipBounds = ctx.clipBounds();
|
||||
clipBounds.offset(-innerOffset.x(), -innerOffset.y());
|
||||
Context outerContext(outerMatrix, clipBounds, ctx.cache(), ctx.sizeConstraint());
|
||||
if (!this->filterInput(0, proxy, tmp, outerContext, result, &outerOffset, false)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -271,13 +271,19 @@ void SkDisplacementMapEffect::computeFastBounds(const SkRect& src, SkRect* dst)
|
||||
dst->outset(fScale * SK_ScalarHalf, fScale * SK_ScalarHalf);
|
||||
}
|
||||
|
||||
bool SkDisplacementMapEffect::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
|
||||
SkIRect* dst) const {
|
||||
SkIRect bounds = src;
|
||||
void SkDisplacementMapEffect::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
|
||||
SkIRect* dst, MapDirection) const {
|
||||
*dst = src;
|
||||
SkVector scale = SkVector::Make(fScale, fScale);
|
||||
ctm.mapVectors(&scale, 1);
|
||||
bounds.outset(SkScalarCeilToInt(scale.fX * SK_ScalarHalf),
|
||||
SkScalarCeilToInt(scale.fY * SK_ScalarHalf));
|
||||
dst->outset(SkScalarCeilToInt(scale.fX * SK_ScalarHalf),
|
||||
SkScalarCeilToInt(scale.fY * SK_ScalarHalf));
|
||||
}
|
||||
|
||||
bool SkDisplacementMapEffect::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
|
||||
SkIRect* dst) const {
|
||||
SkIRect bounds;
|
||||
this->onFilterNodeBounds(src, ctm, &bounds, kReverse_MapDirection);
|
||||
if (this->getColorInput()) {
|
||||
return this->getColorInput()->filterBounds(bounds, ctm, dst);
|
||||
}
|
||||
|
@ -116,25 +116,23 @@ void SkDropShadowImageFilter::computeFastBounds(const SkRect& src, SkRect* dst)
|
||||
}
|
||||
}
|
||||
|
||||
bool SkDropShadowImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
|
||||
SkIRect* dst) const {
|
||||
SkIRect bounds = src;
|
||||
void SkDropShadowImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
|
||||
SkIRect* dst, MapDirection direction) const {
|
||||
*dst = src;
|
||||
SkVector offsetVec = SkVector::Make(fDx, fDy);
|
||||
if (kReverse_MapDirection == direction) {
|
||||
offsetVec.negate();
|
||||
}
|
||||
ctm.mapVectors(&offsetVec, 1);
|
||||
bounds.offset(-SkScalarCeilToInt(offsetVec.x()),
|
||||
-SkScalarCeilToInt(offsetVec.y()));
|
||||
dst->offset(SkScalarCeilToInt(offsetVec.x()),
|
||||
SkScalarCeilToInt(offsetVec.y()));
|
||||
SkVector sigma = SkVector::Make(fSigmaX, fSigmaY);
|
||||
ctm.mapVectors(&sigma, 1);
|
||||
bounds.outset(SkScalarCeilToInt(SkScalarMul(sigma.x(), SkIntToScalar(3))),
|
||||
SkScalarCeilToInt(SkScalarMul(sigma.y(), SkIntToScalar(3))));
|
||||
dst->outset(SkScalarCeilToInt(SkScalarMul(sigma.x(), SkIntToScalar(3))),
|
||||
SkScalarCeilToInt(SkScalarMul(sigma.y(), SkIntToScalar(3))));
|
||||
if (fShadowMode == kDrawShadowAndForeground_ShadowMode) {
|
||||
bounds.join(src);
|
||||
dst->join(src);
|
||||
}
|
||||
if (getInput(0) && !getInput(0)->filterBounds(bounds, ctm, &bounds)) {
|
||||
return false;
|
||||
}
|
||||
*dst = bounds;
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef SK_IGNORE_TO_STRING
|
||||
|
@ -280,7 +280,7 @@ bool SkMatrixConvolutionImageFilter::onFilterImage(Proxy* proxy,
|
||||
}
|
||||
|
||||
SkIRect bounds;
|
||||
if (!this->applyCropRect(ctx, proxy, src, &srcOffset, &bounds, &src)) {
|
||||
if (!this->applyCropRect(this->mapContext(ctx), proxy, src, &srcOffset, &bounds, &src)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -322,17 +322,17 @@ bool SkMatrixConvolutionImageFilter::onFilterImage(Proxy* proxy,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SkMatrixConvolutionImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
|
||||
SkIRect* dst) const {
|
||||
SkIRect bounds = src;
|
||||
bounds.fRight += fKernelSize.width() - 1;
|
||||
bounds.fBottom += fKernelSize.height() - 1;
|
||||
bounds.offset(-fKernelOffset);
|
||||
if (getInput(0) && !getInput(0)->filterBounds(bounds, ctm, &bounds)) {
|
||||
return false;
|
||||
void SkMatrixConvolutionImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
|
||||
SkIRect* dst, MapDirection direction) const {
|
||||
*dst = src;
|
||||
int w = fKernelSize.width() - 1, h = fKernelSize.height() - 1;
|
||||
dst->fRight += w;
|
||||
dst->fBottom += h;
|
||||
if (kReverse_MapDirection == direction) {
|
||||
dst->offset(-fKernelOffset);
|
||||
} else {
|
||||
dst->offset(fKernelOffset - SkIPoint::Make(w, h));
|
||||
}
|
||||
*dst = bounds;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SkMatrixConvolutionImageFilter::canComputeFastBounds() const {
|
||||
|
@ -70,7 +70,7 @@ bool SkMorphologyImageFilter::filterImageGeneric(SkMorphologyImageFilter::Proc p
|
||||
}
|
||||
|
||||
SkIRect bounds;
|
||||
if (!this->applyCropRect(ctx, proxy, src, &srcOffset, &bounds, &src)) {
|
||||
if (!this->applyCropRect(this->mapContext(ctx), proxy, src, &srcOffset, &bounds, &src)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -149,18 +149,13 @@ void SkMorphologyImageFilter::computeFastBounds(const SkRect& src, SkRect* dst)
|
||||
dst->outset(SkIntToScalar(fRadius.width()), SkIntToScalar(fRadius.height()));
|
||||
}
|
||||
|
||||
bool SkMorphologyImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
|
||||
SkIRect* dst) const {
|
||||
SkIRect bounds = src;
|
||||
void SkMorphologyImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
|
||||
SkIRect* dst, MapDirection) const {
|
||||
*dst = src;
|
||||
SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()),
|
||||
SkIntToScalar(this->radius().height()));
|
||||
ctm.mapVectors(&radius, 1);
|
||||
bounds.outset(SkScalarCeilToInt(radius.x()), SkScalarCeilToInt(radius.y()));
|
||||
if (this->getInput(0) && !this->getInput(0)->filterBounds(bounds, ctm, &bounds)) {
|
||||
return false;
|
||||
}
|
||||
*dst = bounds;
|
||||
return true;
|
||||
dst->outset(SkScalarCeilToInt(radius.x()), SkScalarCeilToInt(radius.y()));
|
||||
}
|
||||
|
||||
SkFlattenable* SkErodeImageFilter::CreateProc(SkReadBuffer& buffer) {
|
||||
@ -637,7 +632,7 @@ bool SkMorphologyImageFilter::filterImageGPUGeneric(bool dilate,
|
||||
return false;
|
||||
}
|
||||
SkIRect bounds;
|
||||
if (!this->applyCropRect(ctx, proxy, input, &srcOffset, &bounds, &input)) {
|
||||
if (!this->applyCropRect(this->mapContext(ctx), proxy, input, &srcOffset, &bounds, &input)) {
|
||||
return false;
|
||||
}
|
||||
SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()),
|
||||
|
@ -70,24 +70,28 @@ void SkOffsetImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) cons
|
||||
} else {
|
||||
*dst = src;
|
||||
}
|
||||
#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
|
||||
SkRect copy = *dst;
|
||||
#endif
|
||||
dst->offset(fOffset.fX, fOffset.fY);
|
||||
#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
|
||||
dst->join(copy);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool SkOffsetImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
|
||||
SkIRect* dst) const {
|
||||
void SkOffsetImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
|
||||
SkIRect* dst, MapDirection direction) const {
|
||||
SkVector vec;
|
||||
ctm.mapVectors(&vec, &fOffset, 1);
|
||||
|
||||
SkIRect bounds = src;
|
||||
bounds.offset(-SkScalarCeilToInt(vec.fX), -SkScalarCeilToInt(vec.fY));
|
||||
bounds.join(src);
|
||||
if (getInput(0)) {
|
||||
return getInput(0)->filterBounds(bounds, ctm, dst);
|
||||
if (kReverse_MapDirection == direction) {
|
||||
vec.negate();
|
||||
}
|
||||
*dst = bounds;
|
||||
return true;
|
||||
|
||||
*dst = src;
|
||||
dst->offset(SkScalarCeilToInt(vec.fX), SkScalarCeilToInt(vec.fY));
|
||||
#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
|
||||
dst->join(src);
|
||||
#endif
|
||||
}
|
||||
|
||||
SkFlattenable* SkOffsetImageFilter::CreateProc(SkReadBuffer& buffer) {
|
||||
|
@ -81,21 +81,30 @@ bool SkTileImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src,
|
||||
return true;
|
||||
}
|
||||
|
||||
void SkTileImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
|
||||
SkIRect* dst, MapDirection direction) const {
|
||||
SkRect rect = kReverse_MapDirection == direction ? fSrcRect : fDstRect;
|
||||
ctm.mapRect(&rect);
|
||||
rect.roundOut(dst);
|
||||
#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
|
||||
dst->join(src);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool SkTileImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
|
||||
SkIRect* dst) const {
|
||||
SkRect srcRect;
|
||||
ctm.mapRect(&srcRect, fSrcRect);
|
||||
SkIRect srcIRect;
|
||||
srcRect.roundOut(&srcIRect);
|
||||
srcIRect.join(src);
|
||||
*dst = srcIRect;
|
||||
this->onFilterNodeBounds(src, ctm, dst, kReverse_MapDirection);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SkTileImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const {
|
||||
#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
|
||||
// This is a workaround for skia:3194.
|
||||
*dst = src;
|
||||
dst->join(fDstRect);
|
||||
#else
|
||||
*dst = fDstRect;
|
||||
#endif
|
||||
}
|
||||
|
||||
SkFlattenable* SkTileImageFilter::CreateProc(SkReadBuffer& buffer) {
|
||||
|
@ -1164,7 +1164,11 @@ void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
|
||||
SkIPoint offset = SkIPoint::Make(0, 0);
|
||||
SkMatrix matrix(*draw.fMatrix);
|
||||
matrix.postTranslate(SkIntToScalar(-left), SkIntToScalar(-top));
|
||||
#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
|
||||
SkIRect clipBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
|
||||
#else
|
||||
SkIRect clipBounds = draw.fClip->getBounds().makeOffset(-left, -top);
|
||||
#endif
|
||||
SkAutoTUnref<SkImageFilter::Cache> cache(getImageFilterCache());
|
||||
// This cache is transient, and is freed (along with all its contained
|
||||
// textures) when it goes out of scope.
|
||||
@ -1327,7 +1331,11 @@ void SkGpuDevice::drawDevice(const SkDraw& draw, SkBaseDevice* device,
|
||||
SkIPoint offset = SkIPoint::Make(0, 0);
|
||||
SkMatrix matrix(*draw.fMatrix);
|
||||
matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
|
||||
#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
|
||||
SkIRect clipBounds = SkIRect::MakeWH(devTex->width(), devTex->height());
|
||||
#else
|
||||
SkIRect clipBounds = draw.fClip->getBounds().makeOffset(-x, -y);
|
||||
#endif
|
||||
// This cache is transient, and is freed (along with all its contained
|
||||
// textures) when it goes out of scope.
|
||||
SkAutoTUnref<SkImageFilter::Cache> cache(getImageFilterCache());
|
||||
|
@ -1152,8 +1152,10 @@ DEF_TEST(ComposedImageFilterOffset, reporter) {
|
||||
|
||||
SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(1, 0, 20, 20));
|
||||
SkAutoTUnref<SkImageFilter> offsetFilter(SkOffsetImageFilter::Create(0, 0, nullptr, &cropRect));
|
||||
SkAutoTUnref<SkImageFilter> blurFilter(makeBlur());
|
||||
SkAutoTUnref<SkImageFilter> composedFilter(SkComposeImageFilter::Create(blurFilter, offsetFilter.get()));
|
||||
SkAutoTUnref<SkImageFilter> blurFilter(SkBlurImageFilter::Create(SK_Scalar1, SK_Scalar1,
|
||||
nullptr, &cropRect));
|
||||
SkAutoTUnref<SkImageFilter> composedFilter(SkComposeImageFilter::Create(blurFilter,
|
||||
offsetFilter.get()));
|
||||
SkBitmap result;
|
||||
SkIPoint offset;
|
||||
SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeWH(100, 100), nullptr, SkImageFilter::kApprox_SizeConstraint);
|
||||
|
Loading…
Reference in New Issue
Block a user