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>
This commit is contained in:
parent
ffb3d688b0
commit
120784394c
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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>);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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*);
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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]
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user