Use SkTileMode for blur and matrix convolution filters
Bug: skia:9280 Change-Id: I6d40b83093d8a8e9e84bbe44d917d02dfde85877 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/230124 Commit-Queue: Michael Ludwig <michaelludwig@google.com> Reviewed-by: Robert Phillips <robertphillips@google.com>
This commit is contained in:
parent
f9ad5ba8f5
commit
607c057de9
@ -10,9 +10,12 @@
|
||||
|
||||
#include "include/core/SkImageFilter.h"
|
||||
|
||||
enum class SkTileMode;
|
||||
|
||||
class SK_API SkBlurImageFilter {
|
||||
public:
|
||||
/*! \enum TileMode */
|
||||
/*! \enum TileMode
|
||||
* DEPRECATED: Use SkTileMode instead. */
|
||||
enum TileMode {
|
||||
kClamp_TileMode = 0, /*!< Clamp to the image's edge pixels. */
|
||||
/*!< This re-weights the filter so samples outside have no effect */
|
||||
@ -28,6 +31,10 @@ public:
|
||||
sk_sp<SkImageFilter> input,
|
||||
const SkImageFilter::CropRect* cropRect = nullptr,
|
||||
TileMode tileMode = TileMode::kClampToBlack_TileMode);
|
||||
// EXPERIMENTAL: kMirror is not yet supported
|
||||
static sk_sp<SkImageFilter> Make(SkScalar sigmaX, SkScalar sigmaY, SkTileMode tileMode,
|
||||
sk_sp<SkImageFilter> input,
|
||||
const SkImageFilter::CropRect* cropRect = nullptr);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "include/core/SkSize.h"
|
||||
|
||||
class SkBitmap;
|
||||
enum class SkTileMode;
|
||||
|
||||
/*! \class SkMatrixConvolutionImageFilter
|
||||
Matrix convolution image filter. This filter applies an NxM image
|
||||
@ -24,7 +25,8 @@ class SkBitmap;
|
||||
|
||||
class SK_API SkMatrixConvolutionImageFilter : public SkImageFilter {
|
||||
public:
|
||||
/*! \enum TileMode */
|
||||
/*! \enum TileMode
|
||||
* DEPRECATED: Use SkTileMode instead. */
|
||||
enum TileMode {
|
||||
kClamp_TileMode = 0, /*!< Clamp to the image's edge pixels. */
|
||||
kRepeat_TileMode, /*!< Wrap around to the image's opposite edge. */
|
||||
@ -37,6 +39,16 @@ public:
|
||||
|
||||
~SkMatrixConvolutionImageFilter() override;
|
||||
|
||||
static sk_sp<SkImageFilter> Make(const SkISize& kernelSize,
|
||||
const SkScalar* kernel,
|
||||
SkScalar gain,
|
||||
SkScalar bias,
|
||||
const SkIPoint& kernelOffset,
|
||||
TileMode tileMode,
|
||||
bool convolveAlpha,
|
||||
sk_sp<SkImageFilter> input,
|
||||
const CropRect* cropRect = nullptr);
|
||||
|
||||
/** Construct a matrix convolution image filter.
|
||||
@param kernelSize The kernel size in pixels, in each dimension (N by M).
|
||||
@param kernel The image processing kernel. Must contain N * M
|
||||
@ -50,7 +62,7 @@ public:
|
||||
over the image (e.g., a 3x3 kernel should have an
|
||||
offset of {1, 1}).
|
||||
@param tileMode How accesses outside the image are treated. (@see
|
||||
TileMode).
|
||||
TileMode). EXPERIMENTAL: kMirror not supported yet.
|
||||
@param convolveAlpha If true, all channels are convolved. If false,
|
||||
only the RGB channels are convolved, and
|
||||
alpha is copied from the source image.
|
||||
@ -63,7 +75,7 @@ public:
|
||||
SkScalar gain,
|
||||
SkScalar bias,
|
||||
const SkIPoint& kernelOffset,
|
||||
TileMode tileMode,
|
||||
SkTileMode tileMode,
|
||||
bool convolveAlpha,
|
||||
sk_sp<SkImageFilter> input,
|
||||
const CropRect* cropRect = nullptr);
|
||||
@ -74,7 +86,7 @@ protected:
|
||||
SkScalar gain,
|
||||
SkScalar bias,
|
||||
const SkIPoint& kernelOffset,
|
||||
TileMode tileMode,
|
||||
SkTileMode tileMode,
|
||||
bool convolveAlpha,
|
||||
sk_sp<SkImageFilter> input,
|
||||
const CropRect* cropRect);
|
||||
@ -89,13 +101,13 @@ protected:
|
||||
private:
|
||||
SK_FLATTENABLE_HOOKS(SkMatrixConvolutionImageFilter)
|
||||
|
||||
SkISize fKernelSize;
|
||||
SkScalar* fKernel;
|
||||
SkScalar fGain;
|
||||
SkScalar fBias;
|
||||
SkIPoint fKernelOffset;
|
||||
TileMode fTileMode;
|
||||
bool fConvolveAlpha;
|
||||
SkISize fKernelSize;
|
||||
SkScalar* fKernel;
|
||||
SkScalar fGain;
|
||||
SkScalar fBias;
|
||||
SkIPoint fKernelOffset;
|
||||
SkTileMode fTileMode;
|
||||
bool fConvolveAlpha;
|
||||
|
||||
template <class PixelFetcher, bool convolveAlpha>
|
||||
void filterPixels(const SkBitmap& src,
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <algorithm>
|
||||
|
||||
#include "include/core/SkBitmap.h"
|
||||
#include "include/core/SkTileMode.h"
|
||||
#include "include/private/SkColorData.h"
|
||||
#include "include/private/SkNx.h"
|
||||
#include "include/private/SkTFitsIn.h"
|
||||
@ -34,9 +35,9 @@ class SkBlurImageFilterImpl final : public SkImageFilter {
|
||||
public:
|
||||
SkBlurImageFilterImpl(SkScalar sigmaX,
|
||||
SkScalar sigmaY,
|
||||
SkTileMode tileMode,
|
||||
sk_sp<SkImageFilter> input,
|
||||
const CropRect* cropRect,
|
||||
SkBlurImageFilter::TileMode tileMode);
|
||||
const CropRect* cropRect);
|
||||
|
||||
SkRect computeFastBounds(const SkRect&) const override;
|
||||
|
||||
@ -60,8 +61,8 @@ private:
|
||||
const OutputProperties& outProps, SkIPoint* offset) const;
|
||||
#endif
|
||||
|
||||
SkSize fSigma;
|
||||
SkBlurImageFilter::TileMode fTileMode;
|
||||
SkSize fSigma;
|
||||
SkTileMode fTileMode;
|
||||
};
|
||||
|
||||
void SkImageFilter::RegisterFlattenables() { SK_REGISTER_FLATTENABLE(SkBlurImageFilterImpl); }
|
||||
@ -72,11 +73,31 @@ sk_sp<SkImageFilter> SkBlurImageFilter::Make(SkScalar sigmaX, SkScalar sigmaY,
|
||||
sk_sp<SkImageFilter> input,
|
||||
const SkImageFilter::CropRect* cropRect,
|
||||
TileMode tileMode) {
|
||||
SkTileMode skTileMode;
|
||||
switch(tileMode) {
|
||||
case kClamp_TileMode:
|
||||
skTileMode = SkTileMode::kClamp;
|
||||
break;
|
||||
case kRepeat_TileMode:
|
||||
skTileMode = SkTileMode::kRepeat;
|
||||
break;
|
||||
case kClampToBlack_TileMode:
|
||||
// Fall through
|
||||
default:
|
||||
skTileMode = SkTileMode::kDecal;
|
||||
break;
|
||||
}
|
||||
return Make(sigmaX, sigmaY, skTileMode, std::move(input), cropRect);
|
||||
}
|
||||
|
||||
sk_sp<SkImageFilter> SkBlurImageFilter::Make(SkScalar sigmaX, SkScalar sigmaY, SkTileMode tileMode,
|
||||
sk_sp<SkImageFilter> input,
|
||||
const SkImageFilter::CropRect* cropRect) {
|
||||
if (sigmaX < SK_ScalarNearlyZero && sigmaY < SK_ScalarNearlyZero && !cropRect) {
|
||||
return input;
|
||||
}
|
||||
return sk_sp<SkImageFilter>(
|
||||
new SkBlurImageFilterImpl(sigmaX, sigmaY, input, cropRect, tileMode));
|
||||
new SkBlurImageFilterImpl(sigmaX, sigmaY, tileMode, input, cropRect));
|
||||
}
|
||||
|
||||
// This rather arbitrary-looking value results in a maximum box blur kernel size
|
||||
@ -96,9 +117,9 @@ static SkVector map_sigma(const SkSize& localSigma, const SkMatrix& ctm) {
|
||||
|
||||
SkBlurImageFilterImpl::SkBlurImageFilterImpl(SkScalar sigmaX,
|
||||
SkScalar sigmaY,
|
||||
SkTileMode tileMode,
|
||||
sk_sp<SkImageFilter> input,
|
||||
const CropRect* cropRect,
|
||||
SkBlurImageFilter::TileMode tileMode)
|
||||
const CropRect* cropRect)
|
||||
: INHERITED(&input, 1, cropRect), fSigma{sigmaX, sigmaY}, fTileMode(tileMode) {}
|
||||
|
||||
sk_sp<SkFlattenable> SkBlurImageFilterImpl::CreateProc(SkReadBuffer& buffer) {
|
||||
@ -123,24 +144,46 @@ void SkBlurImageFilterImpl::flatten(SkWriteBuffer& buffer) const {
|
||||
buffer.writeScalar(fSigma.fWidth);
|
||||
buffer.writeScalar(fSigma.fHeight);
|
||||
|
||||
static_assert(SkBlurImageFilter::kLast_TileMode == 2, "flatten");
|
||||
SkASSERT(fTileMode <= SkBlurImageFilter::kLast_TileMode);
|
||||
// CreateProc only knows how to deserialize the old TileMode enum, so temporarily convert
|
||||
// SkTileMode back to the old one, treating kMirror as kRepeat.
|
||||
// TODO (michaelludwig) - Remove once CreateProc can deserialize SkTileMode directly, which will
|
||||
// require a new SkPicture version number.
|
||||
SkBlurImageFilter::TileMode backwardsCompatibleMode;
|
||||
switch(fTileMode) {
|
||||
case SkTileMode::kClamp:
|
||||
backwardsCompatibleMode = SkBlurImageFilter::kClamp_TileMode;
|
||||
break;
|
||||
case SkTileMode::kMirror:
|
||||
// Treat as repeat for now
|
||||
case SkTileMode::kRepeat:
|
||||
backwardsCompatibleMode = SkBlurImageFilter::kRepeat_TileMode;
|
||||
break;
|
||||
case SkTileMode::kDecal:
|
||||
default:
|
||||
backwardsCompatibleMode = SkBlurImageFilter::kClampToBlack_TileMode;
|
||||
break;
|
||||
}
|
||||
|
||||
buffer.writeInt(static_cast<int>(fTileMode));
|
||||
static_assert(SkBlurImageFilter::kLast_TileMode == 2, "flatten");
|
||||
SkASSERT(backwardsCompatibleMode <= SkBlurImageFilter::kLast_TileMode);
|
||||
|
||||
buffer.writeInt(static_cast<int>(backwardsCompatibleMode));
|
||||
}
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
static GrTextureDomain::Mode to_texture_domain_mode(SkBlurImageFilter::TileMode tileMode) {
|
||||
static GrTextureDomain::Mode to_texture_domain_mode(SkTileMode tileMode) {
|
||||
switch (tileMode) {
|
||||
case SkBlurImageFilter::TileMode::kClamp_TileMode:
|
||||
return GrTextureDomain::kClamp_Mode;
|
||||
case SkBlurImageFilter::TileMode::kClampToBlack_TileMode:
|
||||
return GrTextureDomain::kDecal_Mode;
|
||||
case SkBlurImageFilter::TileMode::kRepeat_TileMode:
|
||||
return GrTextureDomain::kRepeat_Mode;
|
||||
default:
|
||||
SK_ABORT("Unsupported tile mode.");
|
||||
return GrTextureDomain::kDecal_Mode;
|
||||
case SkTileMode::kClamp:
|
||||
return GrTextureDomain::kClamp_Mode;
|
||||
case SkTileMode::kDecal:
|
||||
return GrTextureDomain::kDecal_Mode;
|
||||
case SkTileMode::kMirror:
|
||||
// TODO (michaelludwig) - Support mirror mode, treat as repeat for now
|
||||
case SkTileMode::kRepeat:
|
||||
return GrTextureDomain::kRepeat_Mode;
|
||||
default:
|
||||
SK_ABORT("Unsupported tile mode.");
|
||||
return GrTextureDomain::kDecal_Mode;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include "include/core/SkBitmap.h"
|
||||
#include "include/core/SkRect.h"
|
||||
#include "include/core/SkTileMode.h"
|
||||
#include "include/core/SkUnPreMultiply.h"
|
||||
#include "include/effects/SkMatrixConvolutionImageFilter.h"
|
||||
#include "include/private/SkColorData.h"
|
||||
@ -30,7 +31,7 @@ SkMatrixConvolutionImageFilter::SkMatrixConvolutionImageFilter(const SkISize& ke
|
||||
SkScalar gain,
|
||||
SkScalar bias,
|
||||
const SkIPoint& kernelOffset,
|
||||
TileMode tileMode,
|
||||
SkTileMode tileMode,
|
||||
bool convolveAlpha,
|
||||
sk_sp<SkImageFilter> input,
|
||||
const CropRect* cropRect)
|
||||
@ -58,6 +59,33 @@ sk_sp<SkImageFilter> SkMatrixConvolutionImageFilter::Make(const SkISize& kernelS
|
||||
bool convolveAlpha,
|
||||
sk_sp<SkImageFilter> input,
|
||||
const CropRect* cropRect) {
|
||||
SkTileMode skTileMode;
|
||||
switch(tileMode) {
|
||||
case kClamp_TileMode:
|
||||
skTileMode = SkTileMode::kClamp;
|
||||
break;
|
||||
case kRepeat_TileMode:
|
||||
skTileMode = SkTileMode::kRepeat;
|
||||
break;
|
||||
case kClampToBlack_TileMode:
|
||||
// Fall through
|
||||
default:
|
||||
skTileMode = SkTileMode::kDecal;
|
||||
break;
|
||||
}
|
||||
return Make(kernelSize, kernel, gain, bias, kernelOffset, skTileMode, convolveAlpha,
|
||||
std::move(input), cropRect);
|
||||
}
|
||||
|
||||
sk_sp<SkImageFilter> SkMatrixConvolutionImageFilter::Make(const SkISize& kernelSize,
|
||||
const SkScalar* kernel,
|
||||
SkScalar gain,
|
||||
SkScalar bias,
|
||||
const SkIPoint& kernelOffset,
|
||||
SkTileMode tileMode,
|
||||
bool convolveAlpha,
|
||||
sk_sp<SkImageFilter> input,
|
||||
const CropRect* cropRect) {
|
||||
if (kernelSize.width() < 1 || kernelSize.height() < 1) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -121,7 +149,26 @@ void SkMatrixConvolutionImageFilter::flatten(SkWriteBuffer& buffer) const {
|
||||
buffer.writeScalar(fBias);
|
||||
buffer.writeInt(fKernelOffset.fX);
|
||||
buffer.writeInt(fKernelOffset.fY);
|
||||
buffer.writeInt((int) fTileMode);
|
||||
// CreateProc only knows how to deserialize the old TileMode enum, so temporarily convert
|
||||
// SkTileMode back to the old one, treating kMirror as kRepeat.
|
||||
// TODO (michaelludwig) - Remove once CreateProc can deserialize SkTileMode directly, which will
|
||||
// require a new SkPicture version number.
|
||||
TileMode backwardsCompatibleMode;
|
||||
switch(fTileMode) {
|
||||
case SkTileMode::kClamp:
|
||||
backwardsCompatibleMode = kClamp_TileMode;
|
||||
break;
|
||||
case SkTileMode::kMirror:
|
||||
// Treat as repeat for now
|
||||
case SkTileMode::kRepeat:
|
||||
backwardsCompatibleMode = kRepeat_TileMode;
|
||||
break;
|
||||
case SkTileMode::kDecal:
|
||||
default:
|
||||
backwardsCompatibleMode = kClampToBlack_TileMode;
|
||||
break;
|
||||
}
|
||||
buffer.writeInt((int) backwardsCompatibleMode);
|
||||
buffer.writeBool(fConvolveAlpha);
|
||||
}
|
||||
|
||||
@ -235,12 +282,15 @@ void SkMatrixConvolutionImageFilter::filterInteriorPixels(const SkBitmap& src,
|
||||
const SkIRect& rect,
|
||||
const SkIRect& bounds) const {
|
||||
switch (fTileMode) {
|
||||
case kRepeat_TileMode:
|
||||
case SkTileMode::kMirror:
|
||||
// TODO (michaelludwig) - Implement mirror tiling, treat as repeat for now.
|
||||
case SkTileMode::kRepeat:
|
||||
// In repeat mode, we still need to wrap the samples around the src
|
||||
filterPixels<RepeatPixelFetcher>(src, result, offset, rect, bounds);
|
||||
break;
|
||||
case kClamp_TileMode:
|
||||
case kClampToBlack_TileMode:
|
||||
case SkTileMode::kClamp:
|
||||
// Fall through
|
||||
case SkTileMode::kDecal:
|
||||
filterPixels<UncheckedPixelFetcher>(src, result, offset, rect, bounds);
|
||||
break;
|
||||
}
|
||||
@ -252,13 +302,15 @@ void SkMatrixConvolutionImageFilter::filterBorderPixels(const SkBitmap& src,
|
||||
const SkIRect& rect,
|
||||
const SkIRect& srcBounds) const {
|
||||
switch (fTileMode) {
|
||||
case kClamp_TileMode:
|
||||
case SkTileMode::kClamp:
|
||||
filterPixels<ClampPixelFetcher>(src, result, offset, rect, srcBounds);
|
||||
break;
|
||||
case kRepeat_TileMode:
|
||||
case SkTileMode::kMirror:
|
||||
// TODO (michaelludwig) - Implement mirror tiling, treat as repeat for now.
|
||||
case SkTileMode::kRepeat:
|
||||
filterPixels<RepeatPixelFetcher>(src, result, offset, rect, srcBounds);
|
||||
break;
|
||||
case kClampToBlack_TileMode:
|
||||
case SkTileMode::kDecal:
|
||||
filterPixels<ClampToBlackPixelFetcher>(src, result, offset, rect, srcBounds);
|
||||
break;
|
||||
}
|
||||
@ -289,13 +341,15 @@ static SkBitmap unpremultiply_bitmap(const SkBitmap& src) {
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
|
||||
static GrTextureDomain::Mode convert_tilemodes(SkMatrixConvolutionImageFilter::TileMode tileMode) {
|
||||
static GrTextureDomain::Mode convert_tilemodes(SkTileMode tileMode) {
|
||||
switch (tileMode) {
|
||||
case SkMatrixConvolutionImageFilter::kClamp_TileMode:
|
||||
case SkTileMode::kClamp:
|
||||
return GrTextureDomain::kClamp_Mode;
|
||||
case SkMatrixConvolutionImageFilter::kRepeat_TileMode:
|
||||
case SkTileMode::kMirror:
|
||||
// TODO (michaelludwig) - Implement mirror tiling, treat as repeat for now.
|
||||
case SkTileMode::kRepeat:
|
||||
return GrTextureDomain::kRepeat_Mode;
|
||||
case SkMatrixConvolutionImageFilter::kClampToBlack_TileMode:
|
||||
case SkTileMode::kDecal:
|
||||
return GrTextureDomain::kDecal_Mode;
|
||||
default:
|
||||
SkASSERT(false);
|
||||
@ -325,7 +379,7 @@ sk_sp<SkSpecialImage> SkMatrixConvolutionImageFilter::onFilterImage(SkSpecialIma
|
||||
SkIRect srcBounds = this->onFilterNodeBounds(dstBounds, ctx.ctm(), kReverse_MapDirection,
|
||||
&originalSrcBounds);
|
||||
|
||||
if (kRepeat_TileMode == fTileMode) {
|
||||
if (SkTileMode::kRepeat == fTileMode || SkTileMode::kMirror == fTileMode) {
|
||||
srcBounds = DetermineRepeatedSrcBound(srcBounds, fKernelOffset,
|
||||
fKernelSize, originalSrcBounds);
|
||||
} else {
|
||||
@ -407,7 +461,7 @@ sk_sp<SkSpecialImage> SkMatrixConvolutionImageFilter::onFilterImage(SkSpecialIma
|
||||
srcBounds.offset(-inputOffset);
|
||||
|
||||
SkIRect interior;
|
||||
if (kRepeat_TileMode == fTileMode) {
|
||||
if (SkTileMode::kRepeat == fTileMode || SkTileMode::kMirror == fTileMode) {
|
||||
// In repeat mode, the filterPixels calls will wrap around
|
||||
// so we just need to render 'dstBounds'
|
||||
interior = dstBounds;
|
||||
@ -442,7 +496,8 @@ sk_sp<SkSpecialImage> SkMatrixConvolutionImageFilter::onFilterImage(SkSpecialIma
|
||||
SkIRect SkMatrixConvolutionImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
|
||||
MapDirection dir,
|
||||
const SkIRect* inputRect) const {
|
||||
if (kReverse_MapDirection == dir && kRepeat_TileMode == fTileMode && inputRect) {
|
||||
if (kReverse_MapDirection == dir && inputRect &&
|
||||
(SkTileMode::kRepeat == fTileMode || SkTileMode::kMirror == fTileMode)) {
|
||||
SkASSERT(inputRect);
|
||||
return DetermineRepeatedSrcBound(src, fKernelOffset, fKernelSize, *inputRect);
|
||||
}
|
||||
@ -467,5 +522,5 @@ bool SkMatrixConvolutionImageFilter::affectsTransparentBlack() const {
|
||||
|
||||
// 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 kRepeat_TileMode != fTileMode;
|
||||
return SkTileMode::kRepeat != fTileMode && SkTileMode::kMirror != fTileMode;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user