aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
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 /src
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>
Diffstat (limited to 'src')
-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
17 files changed, 174 insertions, 82 deletions
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: