aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Robert Phillips <robertphillips@google.com>2018-05-17 11:17:39 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2018-05-17 17:06:24 +0000
commit120784394c160d009bc3aa88dd217c13c105a6ca (patch)
treec2f3406ff37360a511af362138938e754c057619
parentffb3d688b0e76ad7d1517657b00e4525cc603f40 (diff)
Fix srcBounds computation in SkMatrixConvolutionImageFilter
Note that this does change the behavior of the cropRect for the repeated case. The cropRect now only acts as a hard clip on the output. BUG= skia:7766 Change-Id: I1d66678bc797cd4835701cd20c36e68b22ac880a Reviewed-on: https://skia-review.googlesource.com/127338 Reviewed-by: Herb Derby <herb@google.com> Commit-Queue: Robert Phillips <robertphillips@google.com>
-rw-r--r--gm/matrixconvolution.cpp11
-rw-r--r--include/core/SkColorFilter.h2
-rw-r--r--include/core/SkImageFilter.h28
-rw-r--r--include/effects/SkComposeImageFilter.h3
-rw-r--r--include/effects/SkDisplacementMapEffect.h7
-rw-r--r--include/effects/SkDropShadowImageFilter.h3
-rw-r--r--include/effects/SkImageSource.h3
-rw-r--r--include/effects/SkMatrixConvolutionImageFilter.h7
-rw-r--r--include/effects/SkMorphologyImageFilter.h3
-rw-r--r--include/effects/SkOffsetImageFilter.h3
-rw-r--r--include/effects/SkTileImageFilter.h6
-rw-r--r--src/core/SkBlurImageFilter.cpp5
-rw-r--r--src/core/SkCanvas.cpp47
-rw-r--r--src/core/SkImageFilter.cpp46
-rw-r--r--src/core/SkLocalMatrixImageFilter.cpp6
-rw-r--r--src/core/SkLocalMatrixImageFilter.h3
-rw-r--r--src/core/SkMatrixImageFilter.cpp6
-rw-r--r--src/core/SkMatrixImageFilter.h3
-rw-r--r--src/effects/SkArithmeticImageFilter.cpp14
-rw-r--r--src/effects/SkComposeImageFilter.cpp7
-rw-r--r--src/effects/SkDisplacementMapEffect.cpp6
-rw-r--r--src/effects/SkDropShadowImageFilter.cpp4
-rw-r--r--src/effects/SkImageSource.cpp4
-rw-r--r--src/effects/SkMatrixConvolutionImageFilter.cpp73
-rw-r--r--src/effects/SkMorphologyImageFilter.cpp2
-rw-r--r--src/effects/SkOffsetImageFilter.cpp8
-rw-r--r--src/effects/SkTileImageFilter.cpp7
-rw-r--r--src/effects/SkXfermodeImageFilter.cpp15
-rw-r--r--tests/CanvasTest.cpp3
-rw-r--r--tests/ImageFilterTest.cpp144
30 files changed, 320 insertions, 159 deletions
diff --git a/gm/matrixconvolution.cpp b/gm/matrixconvolution.cpp
index 4af12e172d..adb7eceff0 100644
--- a/gm/matrixconvolution.cpp
+++ b/gm/matrixconvolution.cpp
@@ -92,9 +92,14 @@ protected:
cropRect));
canvas->save();
canvas->translate(SkIntToScalar(x), SkIntToScalar(y));
- canvas->clipRect(SkRect::MakeWH(SkIntToScalar(fBitmap.width()),
- SkIntToScalar(fBitmap.height())));
- canvas->drawBitmap(fBitmap, 0, 0, &paint);
+ const SkRect layerBounds = SkRect::MakeIWH(fBitmap.width(), fBitmap.height());
+ canvas->clipRect(layerBounds);
+ // This GM is, in part, intended to display the wrapping behavior of the
+ // matrix image filter. The only (rational) way to achieve that for repeat mode
+ // is to create a tight layer.
+ canvas->saveLayer(layerBounds, &paint);
+ canvas->drawBitmap(fBitmap, 0, 0, nullptr);
+ canvas->restore();
canvas->restore();
}
diff --git a/include/core/SkColorFilter.h b/include/core/SkColorFilter.h
index 97c8220c6b..e8b09bae9c 100644
--- a/include/core/SkColorFilter.h
+++ b/include/core/SkColorFilter.h
@@ -135,7 +135,7 @@ public:
#endif
bool affectsTransparentBlack() const {
- return this->filterColor(0) != 0;
+ return this->filterColor(SK_ColorTRANSPARENT) != SK_ColorTRANSPARENT;
}
virtual void toString(SkString* str) const = 0;
diff --git a/include/core/SkImageFilter.h b/include/core/SkImageFilter.h
index 2528009e24..70f5513843 100644
--- a/include/core/SkImageFilter.h
+++ b/include/core/SkImageFilter.h
@@ -155,8 +155,13 @@ public:
* be exact, but should never be smaller than the real answer. The default
* implementation recursively unions all input bounds, or returns the
* source rect if no inputs.
+ *
+ * In kReverse mode, 'inputRect' is the device-space bounds of the input pixels. In kForward
+ * mode it should always be null. If 'inputRect' is null in kReverse mode the resulting
+ * answer may be incorrect.
*/
- SkIRect filterBounds(const SkIRect& src, const SkMatrix& ctm, MapDirection) const;
+ SkIRect filterBounds(const SkIRect& src, const SkMatrix& ctm,
+ MapDirection, const SkIRect* inputRect = nullptr) const;
#if SK_SUPPORT_GPU
static sk_sp<SkSpecialImage> DrawWithFP(GrContext* context,
@@ -320,12 +325,16 @@ protected:
* and returns the union of those results. If a derived class has special
* recursion requirements (e.g., it has an input which does not participate
* in bounds computation), it can be overridden here.
+ * In kReverse mode, 'inputRect' is the device-space bounds of the input pixels. In kForward
+ * mode it should always be null. If 'inputRect' is null in kReverse mode the resulting
+ * answer may be incorrect.
*
* Note that this function is *not* responsible for mapping the rect for
* this node's filter bounds requirements (i.e., calling
* onFilterNodeBounds()); that is handled by filterBounds().
*/
- virtual SkIRect onFilterBounds(const SkIRect&, const SkMatrix&, MapDirection) const;
+ virtual SkIRect onFilterBounds(const SkIRect&, const SkMatrix& ctm,
+ MapDirection, const SkIRect* inputRect) const;
/**
* Performs a forwards or reverse mapping of the given rect to accommodate
@@ -339,8 +348,12 @@ protected:
* inverse of the other. For example, blurring expands the given rect
* in both forward and reverse directions. Unlike
* onFilterBounds(), this function is non-recursive.
+ * In kReverse mode, 'inputRect' will be the device space bounds of the input pixels. In
+ * kForward mode, 'inputRect' should always be null. If 'inputRect' is null in kReverse mode
+ * the resulting answer may be incorrect.
*/
- virtual SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix&, MapDirection) const;
+ virtual SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix& ctm,
+ MapDirection, const SkIRect* inputRect) const;
// Helper function which invokes filter processing on the input at the
// specified "index". If the input is null, it returns "src" and leaves
@@ -416,6 +429,15 @@ protected:
return sk_ref_sp(const_cast<SkImageFilter*>(this));
}
+ // If 'srcBounds' will sample outside the border of 'originalSrcBounds' (i.e., the sample
+ // will wrap around to the other side) we must preserve the far side of the src along that
+ // axis (e.g., if we will sample beyond the left edge of the src, the right side must be
+ // preserved for the repeat sampling to work).
+ static SkIRect DetermineRepeatedSrcBound(const SkIRect& srcBounds,
+ const SkIVector& filterOffset,
+ const SkISize& filterSize,
+ const SkIRect& originalSrcBounds);
+
private:
// For makeColorSpace().
friend class SkColorSpaceXformer;
diff --git a/include/effects/SkComposeImageFilter.h b/include/effects/SkComposeImageFilter.h
index dd44df5d9c..0bfb7d8c5c 100644
--- a/include/effects/SkComposeImageFilter.h
+++ b/include/effects/SkComposeImageFilter.h
@@ -27,7 +27,8 @@ protected:
sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&,
SkIPoint* offset) const override;
sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
- SkIRect onFilterBounds(const SkIRect&, const SkMatrix&, MapDirection) const override;
+ SkIRect onFilterBounds(const SkIRect&, const SkMatrix& ctm,
+ MapDirection, const SkIRect* inputRect) const override;
bool onCanHandleComplexCTM() const override { return true; }
private:
diff --git a/include/effects/SkDisplacementMapEffect.h b/include/effects/SkDisplacementMapEffect.h
index af4d6fd10e..daf03d742c 100644
--- a/include/effects/SkDisplacementMapEffect.h
+++ b/include/effects/SkDisplacementMapEffect.h
@@ -35,10 +35,11 @@ public:
SkRect computeFastBounds(const SkRect& src) const override;
- virtual SkIRect onFilterBounds(const SkIRect& src, const SkMatrix&,
- MapDirection) const override;
+ virtual SkIRect onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
+ MapDirection, const SkIRect* inputRect) const override;
sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
- SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix&, MapDirection) const override;
+ SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix& ctm,
+ MapDirection, const SkIRect* inputRect) const override;
void toString(SkString* str) const override;
diff --git a/include/effects/SkDropShadowImageFilter.h b/include/effects/SkDropShadowImageFilter.h
index f6b27db271..d1ee44c168 100644
--- a/include/effects/SkDropShadowImageFilter.h
+++ b/include/effects/SkDropShadowImageFilter.h
@@ -37,7 +37,8 @@ protected:
sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&,
SkIPoint* offset) const override;
sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
- SkIRect onFilterNodeBounds(const SkIRect& src, const SkMatrix&, MapDirection) const override;
+ SkIRect onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
+ MapDirection, const SkIRect* inputRect) const override;
private:
SkDropShadowImageFilter(SkScalar dx, SkScalar dy, SkScalar sigmaX, SkScalar sigmaY, SkColor,
diff --git a/include/effects/SkImageSource.h b/include/effects/SkImageSource.h
index 25b169c20d..6ef855ab93 100644
--- a/include/effects/SkImageSource.h
+++ b/include/effects/SkImageSource.h
@@ -31,7 +31,8 @@ protected:
SkIPoint* offset) const override;
sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
- SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix&, MapDirection) const override;
+ SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix& ctm,
+ MapDirection, const SkIRect* inputRect) const override;
private:
explicit SkImageSource(sk_sp<SkImage>);
diff --git a/include/effects/SkMatrixConvolutionImageFilter.h b/include/effects/SkMatrixConvolutionImageFilter.h
index 94ea716842..5361fef928 100644
--- a/include/effects/SkMatrixConvolutionImageFilter.h
+++ b/include/effects/SkMatrixConvolutionImageFilter.h
@@ -85,7 +85,8 @@ protected:
sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&,
SkIPoint* offset) const override;
sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
- SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix&, MapDirection) const override;
+ SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix& ctm,
+ MapDirection, const SkIRect* inputRect) const override;
bool affectsTransparentBlack() const override;
private:
@@ -100,19 +101,23 @@ private:
template <class PixelFetcher, bool convolveAlpha>
void filterPixels(const SkBitmap& src,
SkBitmap* result,
+ SkIVector& offset,
const SkIRect& rect,
const SkIRect& bounds) const;
template <class PixelFetcher>
void filterPixels(const SkBitmap& src,
SkBitmap* result,
+ SkIVector& offset,
const SkIRect& rect,
const SkIRect& bounds) const;
void filterInteriorPixels(const SkBitmap& src,
SkBitmap* result,
+ SkIVector& offset,
const SkIRect& rect,
const SkIRect& bounds) const;
void filterBorderPixels(const SkBitmap& src,
SkBitmap* result,
+ SkIVector& offset,
const SkIRect& rect,
const SkIRect& bounds) const;
diff --git a/include/effects/SkMorphologyImageFilter.h b/include/effects/SkMorphologyImageFilter.h
index b75fe26d8a..d88a31dc65 100644
--- a/include/effects/SkMorphologyImageFilter.h
+++ b/include/effects/SkMorphologyImageFilter.h
@@ -16,7 +16,8 @@
class SK_API SkMorphologyImageFilter : public SkImageFilter {
public:
SkRect computeFastBounds(const SkRect& src) const override;
- SkIRect onFilterNodeBounds(const SkIRect& src, const SkMatrix&, MapDirection) const override;
+ SkIRect onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
+ MapDirection, const SkIRect* inputRect) const override;
/**
* All morphology procs have the same signature: src is the source buffer, dst the
diff --git a/include/effects/SkOffsetImageFilter.h b/include/effects/SkOffsetImageFilter.h
index 9356c70fd2..2d7b14f652 100644
--- a/include/effects/SkOffsetImageFilter.h
+++ b/include/effects/SkOffsetImageFilter.h
@@ -27,7 +27,8 @@ protected:
sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&,
SkIPoint* offset) const override;
sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
- SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix&, MapDirection) const override;
+ SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix& ctm,
+ MapDirection, const SkIRect* inputRect) const override;
private:
SkOffsetImageFilter(SkScalar dx, SkScalar dy, sk_sp<SkImageFilter> input, const CropRect*);
diff --git a/include/effects/SkTileImageFilter.h b/include/effects/SkTileImageFilter.h
index f37982bbb9..f62d8a02d4 100644
--- a/include/effects/SkTileImageFilter.h
+++ b/include/effects/SkTileImageFilter.h
@@ -21,8 +21,10 @@ public:
const SkRect& dst,
sk_sp<SkImageFilter> input);
- SkIRect onFilterBounds(const SkIRect& src, const SkMatrix&, MapDirection) const override;
- SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix&, MapDirection) const override;
+ SkIRect onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
+ MapDirection, const SkIRect* inputRect) const override;
+ SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix& ctm,
+ MapDirection, const SkIRect* inputRect) const override;
SkRect computeFastBounds(const SkRect& src) const override;
void toString(SkString* str) const override;
diff --git a/src/core/SkBlurImageFilter.cpp b/src/core/SkBlurImageFilter.cpp
index aa566a49f6..a53d061b37 100644
--- a/src/core/SkBlurImageFilter.cpp
+++ b/src/core/SkBlurImageFilter.cpp
@@ -49,7 +49,8 @@ protected:
sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&,
SkIPoint* offset) const override;
sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
- SkIRect onFilterNodeBounds(const SkIRect& src, const SkMatrix&, MapDirection) const override;
+ SkIRect onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
+ MapDirection, const SkIRect* inputRect) const override;
private:
typedef SkImageFilter INHERITED;
@@ -692,7 +693,7 @@ SkRect SkBlurImageFilterImpl::computeFastBounds(const SkRect& src) const {
}
SkIRect SkBlurImageFilterImpl::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
- MapDirection) const {
+ MapDirection, const SkIRect* inputRect) const {
SkVector sigma = map_sigma(fSigma, ctm);
return src.makeOutset(SkScalarCeilToInt(sigma.x() * 3), SkScalarCeilToInt(sigma.y() * 3));
}
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index a467cac060..00bbdf418e 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -879,24 +879,42 @@ bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlag
const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
- if (imageFilter) {
- clipBounds = imageFilter->filterBounds(clipBounds, ctm,
- SkImageFilter::kReverse_MapDirection);
- if (bounds && !imageFilter->canComputeFastBounds()) {
- bounds = nullptr;
- }
+ if (imageFilter && bounds && !imageFilter->canComputeFastBounds()) {
+ // If the image filter DAG affects transparent black then we will need to render
+ // out to the clip bounds
+ bounds = nullptr;
}
- SkIRect ir;
+
+ SkIRect inputSaveLayerBounds;
if (bounds) {
SkRect r;
ctm.mapRect(&r, *bounds);
- r.roundOut(&ir);
+ r.roundOut(&inputSaveLayerBounds);
} else { // no user bounds, so just use the clip
- ir = clipBounds;
+ inputSaveLayerBounds = clipBounds;
+ }
+
+ if (imageFilter) {
+ // expand the clip bounds by the image filter DAG to include extra content that might
+ // be required by the image filters.
+ clipBounds = imageFilter->filterBounds(clipBounds, ctm,
+ SkImageFilter::kReverse_MapDirection,
+ &inputSaveLayerBounds);
+ }
+
+ SkIRect clippedSaveLayerBounds;
+ if (bounds) {
+ // For better or for worse, user bounds currently act as a hard clip on the layer's
+ // extent (i.e., they implement the CSS filter-effects 'filter region' feature).
+ clippedSaveLayerBounds = inputSaveLayerBounds;
+ } else {
+ // If there are no user bounds, we don't want to artificially restrict the resulting
+ // layer bounds, so allow the expanded clip bounds free reign.
+ clippedSaveLayerBounds = clipBounds;
}
// early exit if the layer's bounds are clipped out
- if (!ir.intersect(clipBounds)) {
+ if (!clippedSaveLayerBounds.intersect(clipBounds)) {
if (BoundsAffectsClip(saveLayerFlags)) {
fMCRec->fTopLayer->fDevice->clipRegion(SkRegion(), SkClipOp::kIntersect); // empty
fMCRec->fRasterClip.setEmpty();
@@ -904,17 +922,18 @@ bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlag
}
return false;
}
- SkASSERT(!ir.isEmpty());
+ SkASSERT(!clippedSaveLayerBounds.isEmpty());
if (BoundsAffectsClip(saveLayerFlags)) {
// Simplify the current clips since they will be applied properly during restore()
- fMCRec->fRasterClip.setRect(ir);
- fDeviceClipBounds = qr_clip_bounds(ir);
+ fMCRec->fRasterClip.setRect(clippedSaveLayerBounds);
+ fDeviceClipBounds = qr_clip_bounds(clippedSaveLayerBounds);
}
if (intersection) {
- *intersection = ir;
+ *intersection = clippedSaveLayerBounds;
}
+
return true;
}
diff --git a/src/core/SkImageFilter.cpp b/src/core/SkImageFilter.cpp
index a9447212d3..59715d99e9 100644
--- a/src/core/SkImageFilter.cpp
+++ b/src/core/SkImageFilter.cpp
@@ -223,13 +223,14 @@ sk_sp<SkSpecialImage> SkImageFilter::filterImage(SkSpecialImage* src, const Cont
}
SkIRect SkImageFilter::filterBounds(const SkIRect& src, const SkMatrix& ctm,
- MapDirection direction) const {
+ MapDirection direction, const SkIRect* inputRect) const {
if (kReverse_MapDirection == direction) {
- SkIRect bounds = this->onFilterNodeBounds(src, ctm, direction);
- return this->onFilterBounds(bounds, ctm, direction);
+ SkIRect bounds = this->onFilterNodeBounds(src, ctm, direction, inputRect);
+ return this->onFilterBounds(bounds, ctm, direction, &bounds);
} else {
- SkIRect bounds = this->onFilterBounds(src, ctm, direction);
- bounds = this->onFilterNodeBounds(bounds, ctm, direction);
+ SkASSERT(!inputRect);
+ SkIRect bounds = this->onFilterBounds(src, ctm, direction, nullptr);
+ bounds = this->onFilterNodeBounds(bounds, ctm, direction, nullptr);
SkIRect dst;
this->getCropRect().applyTo(bounds, ctm, this->affectsTransparentBlack(), &dst);
return dst;
@@ -327,7 +328,7 @@ bool SkImageFilter::canHandleComplexCTM() const {
bool SkImageFilter::applyCropRect(const Context& ctx, const SkIRect& srcBounds,
SkIRect* dstBounds) const {
- SkIRect tmpDst = this->onFilterNodeBounds(srcBounds, ctx.ctm(), kForward_MapDirection);
+ SkIRect tmpDst = this->onFilterNodeBounds(srcBounds, ctx.ctm(), kForward_MapDirection, nullptr);
fCropRect.applyTo(tmpDst, ctx.ctm(), this->affectsTransparentBlack(), dstBounds);
// Intersect against the clip bounds, in case the crop rect has
// grown the bounds beyond the original clip. This can happen for
@@ -431,7 +432,7 @@ sk_sp<SkSpecialImage> SkImageFilter::applyCropRectAndPad(const Context& ctx,
}
SkIRect SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
- MapDirection direction) const {
+ MapDirection dir, const SkIRect* inputRect) const {
if (this->countInputs() < 1) {
return src;
}
@@ -439,7 +440,7 @@ SkIRect SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
SkIRect totalBounds;
for (int i = 0; i < this->countInputs(); ++i) {
SkImageFilter* filter = this->getInput(i);
- SkIRect rect = filter ? filter->filterBounds(src, ctm, direction) : src;
+ SkIRect rect = filter ? filter->filterBounds(src, ctm, dir, inputRect) : src;
if (0 == i) {
totalBounds = rect;
} else {
@@ -450,14 +451,16 @@ SkIRect SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
return totalBounds;
}
-SkIRect SkImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix&, MapDirection) const {
+SkIRect SkImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix&,
+ MapDirection, const SkIRect*) const {
return src;
}
SkImageFilter::Context SkImageFilter::mapContext(const Context& ctx) const {
SkIRect clipBounds = this->onFilterNodeBounds(ctx.clipBounds(), ctx.ctm(),
- MapDirection::kReverse_MapDirection);
+ MapDirection::kReverse_MapDirection,
+ &ctx.clipBounds());
return Context(ctx.ctm(), clipBounds, ctx.cache(), ctx.outputProperties());
}
@@ -494,3 +497,26 @@ sk_sp<SkSpecialImage> SkImageFilter::filterInput(int index,
void SkImageFilter::PurgeCache() {
SkImageFilterCache::Get()->purge();
}
+
+// In repeat mode, when we are going to sample off one edge of the srcBounds we require the
+// opposite side be preserved.
+SkIRect SkImageFilter::DetermineRepeatedSrcBound(const SkIRect& srcBounds,
+ const SkIVector& filterOffset,
+ const SkISize& filterSize,
+ const SkIRect& originalSrcBounds) {
+ SkIRect tmp = srcBounds;
+ tmp.fRight = Sk32_sat_add(tmp.fRight, filterSize.fWidth);
+ tmp.fBottom = Sk32_sat_add(tmp.fBottom, filterSize.fHeight);
+ tmp.offset(-filterOffset.fX, -filterOffset.fY);
+
+ if (tmp.fLeft < originalSrcBounds.fLeft || tmp.fRight > originalSrcBounds.fRight) {
+ tmp.fLeft = originalSrcBounds.fLeft;
+ tmp.fRight = originalSrcBounds.fRight;
+ }
+ if (tmp.fTop < originalSrcBounds.fTop || tmp.fBottom > originalSrcBounds.fBottom) {
+ tmp.fTop = originalSrcBounds.fTop;
+ tmp.fBottom = originalSrcBounds.fBottom;
+ }
+
+ return tmp;
+}
diff --git a/src/core/SkLocalMatrixImageFilter.cpp b/src/core/SkLocalMatrixImageFilter.cpp
index a882849637..07db7aff77 100644
--- a/src/core/SkLocalMatrixImageFilter.cpp
+++ b/src/core/SkLocalMatrixImageFilter.cpp
@@ -52,9 +52,9 @@ sk_sp<SkSpecialImage> SkLocalMatrixImageFilter::onFilterImage(SkSpecialImage* so
return this->filterInput(0, source, localCtx, offset);
}
-SkIRect SkLocalMatrixImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& matrix,
- MapDirection direction) const {
- return this->getInput(0)->filterBounds(src, SkMatrix::Concat(matrix, fLocalM), direction);
+SkIRect SkLocalMatrixImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
+ MapDirection dir, const SkIRect* inputRect) const {
+ return this->getInput(0)->filterBounds(src, SkMatrix::Concat(ctm, fLocalM), dir, inputRect);
}
sk_sp<SkImageFilter> SkLocalMatrixImageFilter::onMakeColorSpace(SkColorSpaceXformer* xformer)
diff --git a/src/core/SkLocalMatrixImageFilter.h b/src/core/SkLocalMatrixImageFilter.h
index 951d157111..89a7e9d145 100644
--- a/src/core/SkLocalMatrixImageFilter.h
+++ b/src/core/SkLocalMatrixImageFilter.h
@@ -26,7 +26,8 @@ protected:
sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&,
SkIPoint* offset) const override;
sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
- SkIRect onFilterBounds(const SkIRect& src, const SkMatrix&, MapDirection) const override;
+ SkIRect onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
+ MapDirection, const SkIRect* inputRect) const override;
private:
SkLocalMatrixImageFilter(const SkMatrix& localM, sk_sp<SkImageFilter> input);
diff --git a/src/core/SkMatrixImageFilter.cpp b/src/core/SkMatrixImageFilter.cpp
index f9b53348b1..47b0d4245e 100644
--- a/src/core/SkMatrixImageFilter.cpp
+++ b/src/core/SkMatrixImageFilter.cpp
@@ -114,12 +114,12 @@ SkRect SkMatrixImageFilter::computeFastBounds(const SkRect& src) const {
}
SkIRect SkMatrixImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
- MapDirection direction) const {
+ MapDirection dir, const SkIRect* inputRect) const {
SkMatrix matrix;
if (!ctm.invert(&matrix)) {
return src;
}
- if (kForward_MapDirection == direction) {
+ if (kForward_MapDirection == dir) {
matrix.postConcat(fTransform);
} else {
SkMatrix transformInverse;
@@ -134,7 +134,7 @@ SkIRect SkMatrixImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatr
SkIRect result = floatBounds.roundOut();
#ifndef SK_IGNORE_MATRIX_IMAGE_FILTER_FIX
- if (kReverse_MapDirection == direction && kNone_SkFilterQuality != fFilterQuality) {
+ if (kReverse_MapDirection == dir && kNone_SkFilterQuality != fFilterQuality) {
// When filtering we might need some pixels in the source that might be otherwise
// clipped off.
result.outset(1, 1);
diff --git a/src/core/SkMatrixImageFilter.h b/src/core/SkMatrixImageFilter.h
index 445e739195..b43df9166d 100644
--- a/src/core/SkMatrixImageFilter.h
+++ b/src/core/SkMatrixImageFilter.h
@@ -43,7 +43,8 @@ protected:
sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&,
SkIPoint* offset) const override;
sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
- SkIRect onFilterNodeBounds(const SkIRect& src, const SkMatrix&, MapDirection) const override;
+ SkIRect onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
+ MapDirection, const SkIRect* inputRect) const override;
private:
SkMatrix fTransform;
diff --git a/src/effects/SkArithmeticImageFilter.cpp b/src/effects/SkArithmeticImageFilter.cpp
index 984ed80a19..c022ec955e 100644
--- a/src/effects/SkArithmeticImageFilter.cpp
+++ b/src/effects/SkArithmeticImageFilter.cpp
@@ -44,7 +44,8 @@ protected:
sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&,
SkIPoint* offset) const override;
- SkIRect onFilterBounds(const SkIRect&, const SkMatrix&, MapDirection) const override;
+ SkIRect onFilterBounds(const SkIRect&, const SkMatrix& ctm,
+ MapDirection, const SkIRect* inputRect) const override;
#if SK_SUPPORT_GPU
sk_sp<SkSpecialImage> filterImageGPU(SkSpecialImage* source,
@@ -214,17 +215,18 @@ sk_sp<SkSpecialImage> ArithmeticImageFilterImpl::onFilterImage(SkSpecialImage* s
SkIRect ArithmeticImageFilterImpl::onFilterBounds(const SkIRect& src,
const SkMatrix& ctm,
- MapDirection direction) const {
- if (kReverse_MapDirection == direction) {
- return SkImageFilter::onFilterBounds(src, ctm, direction);
+ MapDirection dir,
+ const SkIRect* inputRect) const {
+ if (kReverse_MapDirection == dir) {
+ return SkImageFilter::onFilterBounds(src, ctm, dir, inputRect);
}
SkASSERT(2 == this->countInputs());
// result(i1,i2) = k1*i1*i2 + k2*i1 + k3*i2 + k4
// Note that background (getInput(0)) is i2, and foreground (getInput(1)) is i1.
- auto i2 = this->getInput(0) ? this->getInput(0)->filterBounds(src, ctm, direction) : src;
- auto i1 = this->getInput(1) ? this->getInput(1)->filterBounds(src, ctm, direction) : src;
+ auto i2 = this->getInput(0) ? this->getInput(0)->filterBounds(src, ctm, dir, nullptr) : src;
+ auto i1 = this->getInput(1) ? this->getInput(1)->filterBounds(src, ctm, dir, nullptr) : src;
// Arithmetic with non-zero k4 may influence the complete filter primitive
// region. [k4 > 0 => result(0,0) = k4 => result(i1,i2) >= k4]
diff --git a/src/effects/SkComposeImageFilter.cpp b/src/effects/SkComposeImageFilter.cpp
index 7769f6d845..51cec9c671 100644
--- a/src/effects/SkComposeImageFilter.cpp
+++ b/src/effects/SkComposeImageFilter.cpp
@@ -39,7 +39,7 @@ sk_sp<SkSpecialImage> SkComposeImageFilter::onFilterImage(SkSpecialImage* source
// filter requires as input. This matters if the outer filter moves pixels.
SkIRect innerClipBounds;
innerClipBounds = this->getInput(0)->filterBounds(ctx.clipBounds(), ctx.ctm(),
- kReverse_MapDirection);
+ kReverse_MapDirection, &ctx.clipBounds());
Context innerContext(ctx.ctm(), innerClipBounds, ctx.cache(), ctx.outputProperties());
SkIPoint innerOffset = SkIPoint::Make(0, 0);
sk_sp<SkSpecialImage> inner(this->filterInput(1, source, innerContext, &innerOffset));
@@ -75,11 +75,12 @@ sk_sp<SkImageFilter> SkComposeImageFilter::onMakeColorSpace(SkColorSpaceXformer*
}
SkIRect SkComposeImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
- MapDirection direction) const {
+ MapDirection dir, const SkIRect* inputRect) const {
SkImageFilter* outer = this->getInput(0);
SkImageFilter* inner = this->getInput(1);
- return outer->filterBounds(inner->filterBounds(src, ctm, direction), ctm, direction);
+ const SkIRect innerRect = inner->filterBounds(src, ctm, dir, inputRect);
+ return outer->filterBounds(innerRect, ctm, dir, &innerRect);
}
sk_sp<SkFlattenable> SkComposeImageFilter::CreateProc(SkReadBuffer& buffer) {
diff --git a/src/effects/SkDisplacementMapEffect.cpp b/src/effects/SkDisplacementMapEffect.cpp
index fe425c3cc3..d40e521b0a 100644
--- a/src/effects/SkDisplacementMapEffect.cpp
+++ b/src/effects/SkDisplacementMapEffect.cpp
@@ -388,7 +388,7 @@ SkRect SkDisplacementMapEffect::computeFastBounds(const SkRect& src) const {
}
SkIRect SkDisplacementMapEffect::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
- MapDirection) const {
+ MapDirection, const SkIRect* inputRect) const {
SkVector scale = SkVector::Make(fScale, fScale);
ctm.mapVectors(&scale, 1);
return src.makeOutset(SkScalarCeilToInt(SkScalarAbs(scale.fX) * SK_ScalarHalf),
@@ -396,10 +396,10 @@ SkIRect SkDisplacementMapEffect::onFilterNodeBounds(const SkIRect& src, const Sk
}
SkIRect SkDisplacementMapEffect::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
- MapDirection direction) const {
+ MapDirection dir, const SkIRect* inputRect) const {
// Recurse only into color input.
if (this->getColorInput()) {
- return this->getColorInput()->filterBounds(src, ctm, direction);
+ return this->getColorInput()->filterBounds(src, ctm, dir, inputRect);
}
return src;
}
diff --git a/src/effects/SkDropShadowImageFilter.cpp b/src/effects/SkDropShadowImageFilter.cpp
index 9cdcace4c3..7532894bce 100644
--- a/src/effects/SkDropShadowImageFilter.cpp
+++ b/src/effects/SkDropShadowImageFilter.cpp
@@ -140,9 +140,9 @@ SkRect SkDropShadowImageFilter::computeFastBounds(const SkRect& src) const {
}
SkIRect SkDropShadowImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
- MapDirection direction) const {
+ MapDirection dir, const SkIRect* inputRect) const {
SkVector offsetVec = SkVector::Make(fDx, fDy);
- if (kReverse_MapDirection == direction) {
+ if (kReverse_MapDirection == dir) {
offsetVec.negate();
}
ctm.mapVectors(&offsetVec, 1);
diff --git a/src/effects/SkImageSource.cpp b/src/effects/SkImageSource.cpp
index 167ef4b71b..0f48fb0c97 100644
--- a/src/effects/SkImageSource.cpp
+++ b/src/effects/SkImageSource.cpp
@@ -147,9 +147,9 @@ SkRect SkImageSource::computeFastBounds(const SkRect& src) const {
}
SkIRect SkImageSource::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
- MapDirection direction) const {
+ MapDirection direction, const SkIRect* inputRect) const {
if (kReverse_MapDirection == direction) {
- return SkImageFilter::onFilterNodeBounds(src, ctm, direction);
+ return SkImageFilter::onFilterNodeBounds(src, ctm, direction, inputRect);
}
SkRect dstRect = fDstRect;
diff --git a/src/effects/SkMatrixConvolutionImageFilter.cpp b/src/effects/SkMatrixConvolutionImageFilter.cpp
index 9412b604ab..10f417bea4 100644
--- a/src/effects/SkMatrixConvolutionImageFilter.cpp
+++ b/src/effects/SkMatrixConvolutionImageFilter.cpp
@@ -169,6 +169,7 @@ public:
template<class PixelFetcher, bool convolveAlpha>
void SkMatrixConvolutionImageFilter::filterPixels(const SkBitmap& src,
SkBitmap* result,
+ SkIVector& offset,
const SkIRect& r,
const SkIRect& bounds) const {
SkIRect rect(r);
@@ -176,7 +177,7 @@ void SkMatrixConvolutionImageFilter::filterPixels(const SkBitmap& src,
return;
}
for (int y = rect.fTop; y < rect.fBottom; ++y) {
- SkPMColor* dptr = result->getAddr32(rect.fLeft - bounds.fLeft, y - bounds.fTop);
+ SkPMColor* dptr = result->getAddr32(rect.fLeft - offset.fX, y - offset.fY);
for (int x = rect.fLeft; x < rect.fRight; ++x) {
SkScalar sumA = 0, sumR = 0, sumG = 0, sumB = 0;
for (int cy = 0; cy < fKernelSize.fHeight; cy++) {
@@ -213,35 +214,38 @@ void SkMatrixConvolutionImageFilter::filterPixels(const SkBitmap& src,
template<class PixelFetcher>
void SkMatrixConvolutionImageFilter::filterPixels(const SkBitmap& src,
SkBitmap* result,
+ SkIVector& offset,
const SkIRect& rect,
const SkIRect& bounds) const {
if (fConvolveAlpha) {
- filterPixels<PixelFetcher, true>(src, result, rect, bounds);
+ filterPixels<PixelFetcher, true>(src, result, offset, rect, bounds);
} else {
- filterPixels<PixelFetcher, false>(src, result, rect, bounds);
+ filterPixels<PixelFetcher, false>(src, result, offset, rect, bounds);
}
}
void SkMatrixConvolutionImageFilter::filterInteriorPixels(const SkBitmap& src,
SkBitmap* result,
+ SkIVector& offset,
const SkIRect& rect,
const SkIRect& bounds) const {
- filterPixels<UncheckedPixelFetcher>(src, result, rect, bounds);
+ filterPixels<UncheckedPixelFetcher>(src, result, offset, rect, bounds);
}
void SkMatrixConvolutionImageFilter::filterBorderPixels(const SkBitmap& src,
SkBitmap* result,
+ SkIVector& offset,
const SkIRect& rect,
- const SkIRect& bounds) const {
+ const SkIRect& srcBounds) const {
switch (fTileMode) {
case kClamp_TileMode:
- filterPixels<ClampPixelFetcher>(src, result, rect, bounds);
+ filterPixels<ClampPixelFetcher>(src, result, offset, rect, srcBounds);
break;
case kRepeat_TileMode:
- filterPixels<RepeatPixelFetcher>(src, result, rect, bounds);
+ filterPixels<RepeatPixelFetcher>(src, result, offset, rect, srcBounds);
break;
case kClampToBlack_TileMode:
- filterPixels<ClampToBlackPixelFetcher>(src, result, rect, bounds);
+ filterPixels<ClampToBlackPixelFetcher>(src, result, offset, rect, srcBounds);
break;
}
}
@@ -301,6 +305,21 @@ sk_sp<SkSpecialImage> SkMatrixConvolutionImageFilter::onFilterImage(SkSpecialIma
return nullptr;
}
+ const SkIRect originalSrcBounds = SkIRect::MakeXYWH(inputOffset.fX, inputOffset.fY,
+ input->width(), input->height());
+
+ SkIRect srcBounds = this->onFilterNodeBounds(dstBounds, ctx.ctm(), kReverse_MapDirection,
+ &originalSrcBounds);
+
+ if (kRepeat_TileMode == fTileMode) {
+ srcBounds = DetermineRepeatedSrcBound(srcBounds, fKernelOffset,
+ fKernelSize, originalSrcBounds);
+ } else {
+ if (!srcBounds.intersect(dstBounds)) {
+ return nullptr;
+ }
+ }
+
#if SK_SUPPORT_GPU
// Note: if the kernel is too big, the GPU path falls back to SW
if (source->isTextureBacked() &&
@@ -319,9 +338,10 @@ sk_sp<SkSpecialImage> SkMatrixConvolutionImageFilter::onFilterImage(SkSpecialIma
offset->fX = dstBounds.left();
offset->fY = dstBounds.top();
dstBounds.offset(-inputOffset);
+ srcBounds.offset(-inputOffset);
auto fp = GrMatrixConvolutionEffect::Make(std::move(inputProxy),
- dstBounds,
+ srcBounds,
fKernelSize,
fKernel,
fGain,
@@ -366,6 +386,8 @@ sk_sp<SkSpecialImage> SkMatrixConvolutionImageFilter::onFilterImage(SkSpecialIma
offset->fX = dstBounds.fLeft;
offset->fY = dstBounds.fTop;
dstBounds.offset(-inputOffset);
+ srcBounds.offset(-inputOffset);
+
SkIRect interior = SkIRect::MakeXYWH(dstBounds.left() + fKernelOffset.fX,
dstBounds.top() + fKernelOffset.fY,
dstBounds.width() - fKernelSize.fWidth + 1,
@@ -378,11 +400,15 @@ sk_sp<SkSpecialImage> SkMatrixConvolutionImageFilter::onFilterImage(SkSpecialIma
interior.left(), interior.bottom());
SkIRect right = SkIRect::MakeLTRB(interior.right(), interior.top(),
dstBounds.right(), interior.bottom());
- this->filterBorderPixels(inputBM, &dst, top, dstBounds);
- this->filterBorderPixels(inputBM, &dst, left, dstBounds);
- this->filterInteriorPixels(inputBM, &dst, interior, dstBounds);
- this->filterBorderPixels(inputBM, &dst, right, dstBounds);
- this->filterBorderPixels(inputBM, &dst, bottom, dstBounds);
+
+ SkIVector dstContentOffset = { offset->fX - inputOffset.fX, offset->fY - inputOffset.fY };
+
+ this->filterBorderPixels(inputBM, &dst, dstContentOffset, top, srcBounds);
+ this->filterBorderPixels(inputBM, &dst, dstContentOffset, left, srcBounds);
+ this->filterInteriorPixels(inputBM, &dst, dstContentOffset, interior, srcBounds);
+ this->filterBorderPixels(inputBM, &dst, dstContentOffset, right, srcBounds);
+ this->filterBorderPixels(inputBM, &dst, dstContentOffset, bottom, srcBounds);
+
return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(dstBounds.width(), dstBounds.height()),
dst);
}
@@ -401,12 +427,18 @@ const {
}
SkIRect SkMatrixConvolutionImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
- MapDirection direction) const {
+ MapDirection dir,
+ const SkIRect* inputRect) const {
+ if (kReverse_MapDirection == dir && kRepeat_TileMode == fTileMode && inputRect) {
+ SkASSERT(inputRect);
+ return DetermineRepeatedSrcBound(src, fKernelOffset, fKernelSize, *inputRect);
+ }
+
SkIRect dst = src;
int w = fKernelSize.width() - 1, h = fKernelSize.height() - 1;
dst.fRight = Sk32_sat_add(dst.fRight, w);
dst.fBottom = Sk32_sat_add(dst.fBottom, h);
- if (kReverse_MapDirection == direction) {
+ if (kReverse_MapDirection == dir) {
dst.offset(-fKernelOffset);
} else {
dst.offset(fKernelOffset - SkIPoint::Make(w, h));
@@ -415,9 +447,14 @@ SkIRect SkMatrixConvolutionImageFilter::onFilterNodeBounds(const SkIRect& src, c
}
bool SkMatrixConvolutionImageFilter::affectsTransparentBlack() const {
- // Because the kernel is applied in device-space, we have no idea what
+ // It seems that the only rational way for repeat sample mode to work is if the caller
+ // explicitly restricts the input in which case the input range is explicitly known and
+ // specified.
+ // TODO: is seems that this should be true for clamp mode too.
+
+ // For the other modes, because the kernel is applied in device-space, we have no idea what
// pixels it will affect in object-space.
- return true;
+ return kRepeat_TileMode != fTileMode;
}
void SkMatrixConvolutionImageFilter::toString(SkString* str) const {
diff --git a/src/effects/SkMorphologyImageFilter.cpp b/src/effects/SkMorphologyImageFilter.cpp
index d985562c62..e421fdd28a 100644
--- a/src/effects/SkMorphologyImageFilter.cpp
+++ b/src/effects/SkMorphologyImageFilter.cpp
@@ -92,7 +92,7 @@ SkRect SkMorphologyImageFilter::computeFastBounds(const SkRect& src) const {
}
SkIRect SkMorphologyImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
- MapDirection) const {
+ MapDirection, const SkIRect* inputRect) const {
SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()),
SkIntToScalar(this->radius().height()));
ctm.mapVectors(&radius, 1);
diff --git a/src/effects/SkOffsetImageFilter.cpp b/src/effects/SkOffsetImageFilter.cpp
index 6d4d96ad7d..5b1533b371 100644
--- a/src/effects/SkOffsetImageFilter.cpp
+++ b/src/effects/SkOffsetImageFilter.cpp
@@ -49,8 +49,8 @@ sk_sp<SkSpecialImage> SkOffsetImageFilter::onFilterImage(SkSpecialImage* source,
return input;
} else {
SkIRect bounds;
- SkIRect srcBounds = SkIRect::MakeWH(input->width(), input->height());
- srcBounds.offset(srcOffset);
+ const SkIRect srcBounds = SkIRect::MakeXYWH(srcOffset.fX, srcOffset.fY,
+ input->width(), input->height());
if (!this->applyCropRect(ctx, srcBounds, &bounds)) {
return nullptr;
}
@@ -97,9 +97,9 @@ SkRect SkOffsetImageFilter::computeFastBounds(const SkRect& src) const {
}
SkIRect SkOffsetImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
- MapDirection direction) const {
+ MapDirection dir, const SkIRect* inputRect) const {
SkIPoint vec = map_offset_vector(ctm, fOffset);
- if (kReverse_MapDirection == direction) {
+ if (kReverse_MapDirection == dir) {
SkPointPriv::Negate(vec);
}
diff --git a/src/effects/SkTileImageFilter.cpp b/src/effects/SkTileImageFilter.cpp
index fdd66c4da0..1aac5814c5 100644
--- a/src/effects/SkTileImageFilter.cpp
+++ b/src/effects/SkTileImageFilter.cpp
@@ -128,13 +128,14 @@ sk_sp<SkImageFilter> SkTileImageFilter::onMakeColorSpace(SkColorSpaceXformer* xf
}
SkIRect SkTileImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
- MapDirection direction) const {
- SkRect rect = kReverse_MapDirection == direction ? fSrcRect : fDstRect;
+ MapDirection dir, const SkIRect* inputRect) const {
+ SkRect rect = kReverse_MapDirection == dir ? fSrcRect : fDstRect;
ctm.mapRect(&rect);
return rect.roundOut();
}
-SkIRect SkTileImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix&, MapDirection) const {
+SkIRect SkTileImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix&,
+ MapDirection, const SkIRect* inputRect) const {
// Don't recurse into inputs.
return src;
}
diff --git a/src/effects/SkXfermodeImageFilter.cpp b/src/effects/SkXfermodeImageFilter.cpp
index 587784a391..9123818c84 100644
--- a/src/effects/SkXfermodeImageFilter.cpp
+++ b/src/effects/SkXfermodeImageFilter.cpp
@@ -42,7 +42,8 @@ protected:
SkIPoint* offset) const override;
sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
- SkIRect onFilterBounds(const SkIRect&, const SkMatrix&, MapDirection) const override;
+ SkIRect onFilterBounds(const SkIRect&, const SkMatrix& ctm,
+ MapDirection, const SkIRect* inputRect) const override;
#if SK_SUPPORT_GPU
sk_sp<SkSpecialImage> filterImageGPU(SkSpecialImage* source,
@@ -179,17 +180,19 @@ sk_sp<SkSpecialImage> SkXfermodeImageFilter_Base::onFilterImage(SkSpecialImage*
SkIRect SkXfermodeImageFilter_Base::onFilterBounds(const SkIRect& src,
const SkMatrix& ctm,
- MapDirection direction) const {
- if (kReverse_MapDirection == direction) {
- return SkImageFilter::onFilterBounds(src, ctm, direction);
+ MapDirection dir,
+ const SkIRect* inputRect) const {
+ if (kReverse_MapDirection == dir) {
+ return SkImageFilter::onFilterBounds(src, ctm, dir, inputRect);
}
+ SkASSERT(!inputRect);
SkASSERT(2 == this->countInputs());
auto getBackground = [&]() {
- return this->getInput(0) ? this->getInput(0)->filterBounds(src, ctm, direction) : src;
+ return this->getInput(0) ? this->getInput(0)->filterBounds(src, ctm, dir, inputRect) : src;
};
auto getForeground = [&]() {
- return this->getInput(1) ? this->getInput(1)->filterBounds(src, ctm, direction) : src;
+ return this->getInput(1) ? this->getInput(1)->filterBounds(src, ctm, dir, inputRect) : src;
};
switch (fMode) {
case SkBlendMode::kClear:
diff --git a/tests/CanvasTest.cpp b/tests/CanvasTest.cpp
index d61d195982..aee1d405be 100644
--- a/tests/CanvasTest.cpp
+++ b/tests/CanvasTest.cpp
@@ -846,7 +846,8 @@ protected:
return nullptr;
}
sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override { return nullptr; }
- SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix&, MapDirection) const override {
+ SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix&,
+ MapDirection, const SkIRect* inputRect) const override {
return SkIRect::MakeEmpty();
}
diff --git a/tests/ImageFilterTest.cpp b/tests/ImageFilterTest.cpp
index 556c3f040d..8e46715fbb 100644
--- a/tests/ImageFilterTest.cpp
+++ b/tests/ImageFilterTest.cpp
@@ -150,7 +150,6 @@ public:
FilterList(sk_sp<SkImageFilter> input, const SkImageFilter::CropRect* cropRect = nullptr) {
SkPoint3 location = SkPoint3::Make(0, 0, SK_Scalar1);
const SkScalar five = SkIntToScalar(5);
-
{
sk_sp<SkColorFilter> cf(SkColorFilter::MakeModeFilter(SK_ColorRED,
SkBlendMode::kSrcIn));
@@ -158,7 +157,6 @@ public:
this->addFilter("color filter",
SkColorFilterImageFilter::Make(std::move(cf), input, cropRect));
}
-
{
sk_sp<SkImage> gradientImage(SkImage::MakeFromBitmap(make_gradient_circle(64, 64)));
sk_sp<SkImageFilter> gradientSource(SkImageSource::Make(std::move(gradientImage)));
@@ -169,7 +167,6 @@ public:
20.0f,
std::move(gradientSource), input, cropRect));
}
-
this->addFilter("blur", SkBlurImageFilter::Make(SK_Scalar1,
SK_Scalar1,
input,
@@ -193,13 +190,14 @@ public:
const SkISize kernelSize = SkISize::Make(3, 3);
const SkScalar gain = SK_Scalar1, bias = 0;
+ // This filter needs a saveLayer bc it is in repeat mode
this->addFilter("matrix convolution",
- SkMatrixConvolutionImageFilter::Make(
- kernelSize, kernel, gain, bias, SkIPoint::Make(1, 1),
- SkMatrixConvolutionImageFilter::kRepeat_TileMode, false,
- input, cropRect));
+ SkMatrixConvolutionImageFilter::Make(
+ kernelSize, kernel, gain, bias, SkIPoint::Make(1, 1),
+ SkMatrixConvolutionImageFilter::kRepeat_TileMode, false,
+ input, cropRect),
+ true);
}
-
this->addFilter("merge", SkMergeImageFilter::Make(input, input, cropRect));
{
@@ -273,18 +271,21 @@ public:
int count() const { return fFilters.count(); }
SkImageFilter* getFilter(int index) const { return fFilters[index].fFilter.get(); }
const char* getName(int index) const { return fFilters[index].fName; }
+ bool needsSaveLayer(int index) const { return fFilters[index].fNeedsSaveLayer; }
private:
struct Filter {
- Filter() : fName(nullptr) {}
- Filter(const char* name, sk_sp<SkImageFilter> filter)
+ Filter() : fName(nullptr), fNeedsSaveLayer(false) {}
+ Filter(const char* name, sk_sp<SkImageFilter> filter, bool needsSaveLayer)
: fName(name)
- , fFilter(std::move(filter)) {
+ , fFilter(std::move(filter))
+ , fNeedsSaveLayer(needsSaveLayer) {
}
const char* fName;
sk_sp<SkImageFilter> fFilter;
+ bool fNeedsSaveLayer;
};
- void addFilter(const char* name, sk_sp<SkImageFilter> filter) {
- fFilters.push_back(Filter(name, std::move(filter)));
+ void addFilter(const char* name, sk_sp<SkImageFilter> filter, bool needsSaveLayer = false) {
+ fFilters.push_back(Filter(name, std::move(filter), needsSaveLayer));
}
SkTArray<Filter> fFilters;
@@ -305,7 +306,8 @@ private:
}
sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override { return nullptr; }
- SkIRect onFilterBounds(const SkIRect&, const SkMatrix&, MapDirection) const override {
+ SkIRect onFilterBounds(const SkIRect&, const SkMatrix&,
+ MapDirection, const SkIRect*) const override {
return fBounds;
}
@@ -743,34 +745,51 @@ DEF_TEST(ImageFilterDrawTiled, reporter) {
tiledResult.allocN32Pixels(width, height);
SkCanvas tiledCanvas(tiledResult);
SkCanvas untiledCanvas(untiledResult);
- int tileSize = 8;
+ const int tileSize = 8;
+
+ SkPaint textPaint;
+ textPaint.setTextSize(SkIntToScalar(height));
+ textPaint.setColor(SK_ColorWHITE);
+
+ const char* text = "ABC";
+ const SkScalar yPos = SkIntToScalar(height);
for (int scale = 1; scale <= 2; ++scale) {
for (int i = 0; i < filters.count(); ++i) {
- tiledCanvas.clear(0);
- untiledCanvas.clear(0);
- SkPaint paint;
- paint.setImageFilter(sk_ref_sp(filters.getFilter(i)));
- paint.setTextSize(SkIntToScalar(height));
- paint.setColor(SK_ColorWHITE);
- SkString str;
- const char* text = "ABC";
- SkScalar ypos = SkIntToScalar(height);
+ SkPaint combinedPaint;
+ combinedPaint.setTextSize(SkIntToScalar(height));
+ combinedPaint.setColor(SK_ColorWHITE);
+ combinedPaint.setImageFilter(sk_ref_sp(filters.getFilter(i)));
+
+ untiledCanvas.clear(SK_ColorTRANSPARENT);
untiledCanvas.save();
untiledCanvas.scale(SkIntToScalar(scale), SkIntToScalar(scale));
- untiledCanvas.drawString(text, 0, ypos, paint);
+ untiledCanvas.drawString(text, 0, yPos, combinedPaint);
untiledCanvas.restore();
+ untiledCanvas.flush();
+
+ tiledCanvas.clear(SK_ColorTRANSPARENT);
for (int y = 0; y < height; y += tileSize) {
for (int x = 0; x < width; x += tileSize) {
tiledCanvas.save();
- tiledCanvas.clipRect(SkRect::Make(SkIRect::MakeXYWH(x, y, tileSize, tileSize)));
- tiledCanvas.scale(SkIntToScalar(scale), SkIntToScalar(scale));
- tiledCanvas.drawString(text, 0, ypos, paint);
+ const SkRect clipRect = SkRect::MakeXYWH(x, y, tileSize, tileSize);
+ tiledCanvas.clipRect(clipRect);
+ if (filters.needsSaveLayer(i)) {
+ const SkRect layerBounds = SkRect::MakeWH(width, height);
+ tiledCanvas.saveLayer(&layerBounds, &combinedPaint);
+ tiledCanvas.scale(SkIntToScalar(scale), SkIntToScalar(scale));
+ tiledCanvas.drawString(text, 0, yPos, textPaint);
+ tiledCanvas.restore();
+ } else {
+ tiledCanvas.scale(SkIntToScalar(scale), SkIntToScalar(scale));
+ tiledCanvas.drawString(text, 0, yPos, combinedPaint);
+ }
+
tiledCanvas.restore();
}
}
- untiledCanvas.flush();
tiledCanvas.flush();
+
if (!sk_tool_utils::equal_pixels(untiledResult, tiledResult, 1)) {
REPORTER_ASSERT(reporter, false, filters.getName(i));
break;
@@ -850,7 +869,8 @@ DEF_TEST(ImageFilterBlurThenShadowBounds, reporter) {
SkIRect bounds = SkIRect::MakeXYWH(0, 0, 100, 100);
SkIRect expectedBounds = SkIRect::MakeXYWH(-133, -133, 236, 236);
- bounds = filter2->filterBounds(bounds, SkMatrix::I(), SkImageFilter::kReverse_MapDirection);
+ bounds = filter2->filterBounds(bounds, SkMatrix::I(),
+ SkImageFilter::kReverse_MapDirection, &bounds);
REPORTER_ASSERT(reporter, bounds == expectedBounds);
}
@@ -861,7 +881,8 @@ DEF_TEST(ImageFilterShadowThenBlurBounds, reporter) {
SkIRect bounds = SkIRect::MakeXYWH(0, 0, 100, 100);
SkIRect expectedBounds = SkIRect::MakeXYWH(-133, -133, 236, 236);
- bounds = filter2->filterBounds(bounds, SkMatrix::I(), SkImageFilter::kReverse_MapDirection);
+ bounds = filter2->filterBounds(bounds, SkMatrix::I(),
+ SkImageFilter::kReverse_MapDirection, &bounds);
REPORTER_ASSERT(reporter, bounds == expectedBounds);
}
@@ -872,7 +893,8 @@ DEF_TEST(ImageFilterDilateThenBlurBounds, reporter) {
SkIRect bounds = SkIRect::MakeXYWH(0, 0, 100, 100);
SkIRect expectedBounds = SkIRect::MakeXYWH(-132, -132, 234, 234);
- bounds = filter2->filterBounds(bounds, SkMatrix::I(), SkImageFilter::kReverse_MapDirection);
+ bounds = filter2->filterBounds(bounds, SkMatrix::I(),
+ SkImageFilter::kReverse_MapDirection, &bounds);
REPORTER_ASSERT(reporter, bounds == expectedBounds);
}
@@ -891,20 +913,20 @@ DEF_TEST(ImageFilterScaledBlurRadius, reporter) {
SkIRect expectedBlurBounds = SkIRect::MakeLTRB(-6, -6, 206, 206);
SkIRect blurBounds = blur->filterBounds(
- bounds, scaleMatrix, SkImageFilter::kForward_MapDirection);
+ bounds, scaleMatrix, SkImageFilter::kForward_MapDirection, nullptr);
REPORTER_ASSERT(reporter, blurBounds == expectedBlurBounds);
SkIRect reverseBlurBounds = blur->filterBounds(
- bounds, scaleMatrix, SkImageFilter::kReverse_MapDirection);
+ bounds, scaleMatrix, SkImageFilter::kReverse_MapDirection, &bounds);
REPORTER_ASSERT(reporter, reverseBlurBounds == expectedBlurBounds);
SkIRect expectedShadowBounds = SkIRect::MakeLTRB(0, 0, 460, 460);
SkIRect shadowBounds = dropShadow->filterBounds(
- bounds, scaleMatrix, SkImageFilter::kForward_MapDirection);
+ bounds, scaleMatrix, SkImageFilter::kForward_MapDirection, nullptr);
REPORTER_ASSERT(reporter, shadowBounds == expectedShadowBounds);
SkIRect expectedReverseShadowBounds =
SkIRect::MakeLTRB(-260, -260, 200, 200);
SkIRect reverseShadowBounds = dropShadow->filterBounds(
- bounds, scaleMatrix, SkImageFilter::kReverse_MapDirection);
+ bounds, scaleMatrix, SkImageFilter::kReverse_MapDirection, &bounds);
REPORTER_ASSERT(reporter,
reverseShadowBounds == expectedReverseShadowBounds);
}
@@ -916,20 +938,20 @@ DEF_TEST(ImageFilterScaledBlurRadius, reporter) {
SkIRect expectedBlurBounds = SkIRect::MakeLTRB(-3, -103, 103, 3);
SkIRect blurBounds = blur->filterBounds(
- bounds, scaleMatrix, SkImageFilter::kForward_MapDirection);
+ bounds, scaleMatrix, SkImageFilter::kForward_MapDirection, nullptr);
REPORTER_ASSERT(reporter, blurBounds == expectedBlurBounds);
SkIRect reverseBlurBounds = blur->filterBounds(
- bounds, scaleMatrix, SkImageFilter::kReverse_MapDirection);
+ bounds, scaleMatrix, SkImageFilter::kReverse_MapDirection, &bounds);
REPORTER_ASSERT(reporter, reverseBlurBounds == expectedBlurBounds);
SkIRect expectedShadowBounds = SkIRect::MakeLTRB(0, -230, 230, 0);
SkIRect shadowBounds = dropShadow->filterBounds(
- bounds, scaleMatrix, SkImageFilter::kForward_MapDirection);
+ bounds, scaleMatrix, SkImageFilter::kForward_MapDirection, nullptr);
REPORTER_ASSERT(reporter, shadowBounds == expectedShadowBounds);
SkIRect expectedReverseShadowBounds =
SkIRect::MakeLTRB(-130, -100, 100, 130);
SkIRect reverseShadowBounds = dropShadow->filterBounds(
- bounds, scaleMatrix, SkImageFilter::kReverse_MapDirection);
+ bounds, scaleMatrix, SkImageFilter::kReverse_MapDirection, &bounds);
REPORTER_ASSERT(reporter,
reverseShadowBounds == expectedReverseShadowBounds);
}
@@ -1919,8 +1941,8 @@ DEF_TEST(XfermodeImageFilterBounds, reporter) {
for (int i = 0; i < kModeCount; ++i) {
sk_sp<SkImageFilter> xfermode(SkXfermodeImageFilter::Make(static_cast<SkBlendMode>(i),
background, foreground, nullptr));
- auto bounds =
- xfermode->filterBounds(src, SkMatrix::I(), SkImageFilter::kForward_MapDirection);
+ auto bounds = xfermode->filterBounds(src, SkMatrix::I(),
+ SkImageFilter::kForward_MapDirection, nullptr);
REPORTER_ASSERT(reporter, bounds == expectedBounds[i]);
}
@@ -1929,7 +1951,8 @@ DEF_TEST(XfermodeImageFilterBounds, reporter) {
sk_sp<SkImageFilter> foreground2(new FixedBoundsImageFilter(SkIRect::MakeXYWH(40, 40, 50, 50)));
sk_sp<SkImageFilter> xfermode(SkXfermodeImageFilter::Make(
SkBlendMode::kSrcIn, std::move(background2), std::move(foreground2), nullptr));
- auto bounds = xfermode->filterBounds(src, SkMatrix::I(), SkImageFilter::kForward_MapDirection);
+ auto bounds = xfermode->filterBounds(src, SkMatrix::I(),
+ SkImageFilter::kForward_MapDirection, nullptr);
REPORTER_ASSERT(reporter, bounds.isEmpty());
}
@@ -1939,12 +1962,12 @@ DEF_TEST(OffsetImageFilterBounds, reporter) {
SkIRect expectedForward = SkIRect::MakeXYWH(-50, -50, 100, 100);
SkIRect boundsForward = offset->filterBounds(src, SkMatrix::I(),
- SkImageFilter::kForward_MapDirection);
+ SkImageFilter::kForward_MapDirection, nullptr);
REPORTER_ASSERT(reporter, boundsForward == expectedForward);
SkIRect expectedReverse = SkIRect::MakeXYWH(50, 50, 100, 100);
SkIRect boundsReverse = offset->filterBounds(src, SkMatrix::I(),
- SkImageFilter::kReverse_MapDirection);
+ SkImageFilter::kReverse_MapDirection, &src);
REPORTER_ASSERT(reporter, boundsReverse == expectedReverse);
}
@@ -1956,7 +1979,7 @@ static void test_arithmetic_bounds(skiatest::Reporter* reporter, float k1, float
SkArithmeticImageFilter::Make(k1, k2, k3, k4, false, background, foreground, crop));
// The value of the input rect doesn't matter because we use inputs with fixed bounds.
SkIRect bounds = arithmetic->filterBounds(SkIRect::MakeXYWH(11, 22, 33, 44), SkMatrix::I(),
- SkImageFilter::kForward_MapDirection);
+ SkImageFilter::kForward_MapDirection, nullptr);
REPORTER_ASSERT(reporter, expected == bounds);
}
@@ -2027,18 +2050,20 @@ DEF_TEST(ImageSourceBounds, reporter) {
SkIRect input(SkIRect::MakeXYWH(10, 20, 30, 40));
REPORTER_ASSERT(reporter,
imageBounds == source1->filterBounds(input, SkMatrix::I(),
- SkImageFilter::kForward_MapDirection));
+ SkImageFilter::kForward_MapDirection,
+ nullptr));
REPORTER_ASSERT(reporter,
input == source1->filterBounds(input, SkMatrix::I(),
- SkImageFilter::kReverse_MapDirection));
+ SkImageFilter::kReverse_MapDirection, &input));
SkMatrix scale(SkMatrix::MakeScale(2));
SkIRect scaledBounds = SkIRect::MakeWH(128, 128);
REPORTER_ASSERT(reporter,
scaledBounds == source1->filterBounds(input, scale,
- SkImageFilter::kForward_MapDirection));
- REPORTER_ASSERT(
- reporter,
- input == source1->filterBounds(input, scale, SkImageFilter::kReverse_MapDirection));
+ SkImageFilter::kForward_MapDirection,
+ nullptr));
+ REPORTER_ASSERT(reporter, input == source1->filterBounds(input, scale,
+ SkImageFilter::kReverse_MapDirection,
+ &input));
// Specified src and dst rects.
SkRect src(SkRect::MakeXYWH(0.5, 0.5, 100.5, 100.5));
@@ -2046,16 +2071,19 @@ DEF_TEST(ImageSourceBounds, reporter) {
sk_sp<SkImageFilter> source2(SkImageSource::Make(image, src, dst, kMedium_SkFilterQuality));
REPORTER_ASSERT(reporter,
dst.roundOut() == source2->filterBounds(input, SkMatrix::I(),
- SkImageFilter::kForward_MapDirection));
+ SkImageFilter::kForward_MapDirection,
+ nullptr));
REPORTER_ASSERT(reporter,
input == source2->filterBounds(input, SkMatrix::I(),
- SkImageFilter::kReverse_MapDirection));
+ SkImageFilter::kReverse_MapDirection, &input));
scale.mapRect(&dst);
scale.mapRect(&src);
REPORTER_ASSERT(reporter,
dst.roundOut() == source2->filterBounds(input, scale,
- SkImageFilter::kForward_MapDirection));
- REPORTER_ASSERT(
- reporter,
- input == source2->filterBounds(input, scale, SkImageFilter::kReverse_MapDirection));
+ SkImageFilter::kForward_MapDirection,
+ nullptr));
+ REPORTER_ASSERT(reporter, input == source2->filterBounds(input, scale,
+ SkImageFilter::kReverse_MapDirection,
+ &input));
}
+