64static constexpr int kMaxKernelSize = 256;
67static constexpr int kMaxUniformKernelSize = 28;
69SkBitmap create_kernel_bitmap(
const SkISize& kernelSize,
const float* kernel,
70 float* innerGain,
float* innerBias);
74 SkMatrixConvolutionImageFilter(
const SkISize& kernelSize,
const SkScalar* kernel,
78 , fKernel(kernel, kernelSize.
width() * kernelSize.
height())
79 , fKernelSize(kernelSize)
80 , fKernelOffset({kernelOffset.
fX, kernelOffset.
fY})
83 , fConvolveAlpha(convolveAlpha) {
86 SkASSERT(kernelSize.fWidth >= 1 && kernelSize.fHeight >= 1);
87 SkASSERT(kernelOffset.fX >= 0 && kernelOffset.fX < kernelSize.fWidth);
88 SkASSERT(kernelOffset.fY >= 0 && kernelOffset.fY < kernelSize.fHeight);
91 fKernelBitmap = create_kernel_bitmap(kernelSize, kernel, &fInnerGain, &fInnerBias);
100 friend void ::SkRegisterMatrixConvolutionImageFilterFlattenable();
160 int dl,
int dt,
int dr,
int db) {
166SkBitmap create_kernel_bitmap(
const SkISize& kernelSize,
const float* kernel,
167 float* innerGain,
float* innerBias) {
169 if (
length <= kMaxUniformKernelSize) {
188 float min = kernel[0];
189 float max = kernel[0];
190 for (
int i = 1; i <
length; ++i) {
191 if (kernel[i] <
min) {
194 if (kernel[i] >
max) {
214 for (
int y = 0;
y < kernelSize.
fHeight; ++
y) {
215 for (
int x = 0;
x < kernelSize.
fWidth; ++
x) {
236 if (kernelSize.
width() < 1 || kernelSize.
height() < 1) {
245 if ((kernelOffset.
fX < 0) || (kernelOffset.
fX >= kernelSize.
fWidth) ||
246 (kernelOffset.
fY < 0) || (kernelOffset.
fY >= kernelSize.
fHeight)) {
260 kernelSize, kernel, gain, bias, kernelOffset, convolveAlpha, &filter));
272 SkMatrixConvolutionImageFilter::CreateProc);
291 if (!
buffer.readScalarArray(kernel.get(),
count)) {
305 bool convolveAlpha =
buffer.readBool();
314 kernelSize, kernel.get(), gain, bias, kernelOffset, tileMode,
320 buffer.writeInt(fKernelSize.width());
321 buffer.writeInt(fKernelSize.height());
322 buffer.writeScalarArray(fKernel.data(), fKernel.size());
323 buffer.writeScalar(fGain);
324 buffer.writeScalar(fBias);
325 buffer.writeInt(fKernelOffset.x());
326 buffer.writeInt(fKernelOffset.y());
327 buffer.writeBool(fConvolveAlpha);
334 return adjust(bounds,
337 fKernelSize.width() - fKernelOffset.x() - 1,
338 fKernelSize.height() - fKernelOffset.y() - 1);
343 return adjust(bounds,
344 fKernelOffset.x() - fKernelSize.width() + 1,
345 fKernelOffset.y() - fKernelSize.height() + 1,
358 static const char* kHeaderSkSL =
360 "uniform int2 offset;"
361 "uniform half2 gainAndBias;"
362 "uniform int convolveAlpha;"
364 "uniform shader child;"
366 "half4 main(float2 coord) {"
367 "half4 sum = half4(0);"
368 "half origAlpha = 0;";
371 static const char* kAccumulateSkSL =
372 "half4 c = child.eval(coord + half2(kernelPos) - half2(offset));"
373 "if (convolveAlpha == 0) {"
376 "if (kernelPos == offset) {"
384 static const char* kFooterSkSL =
385 "half4 color = sum*gainAndBias.x + gainAndBias.y;"
386 "if (convolveAlpha == 0) {"
388 "color = half4(color.rgb*origAlpha, origAlpha);"
391 "color.a = saturate(color.a);"
394 "color.rgb = clamp(color.rgb, 0, color.a);"
400 static_assert(kMaxUniformKernelSize % 4 == 0,
"Must be a multiple of 4");
403 "uniform half4 kernel[kMaxUniformKernelSize];"
405 "int2 kernelPos = int2(0);"
406 "for (int i = 0; i < kMaxUniformKernelSize; ++i) {"
407 "if (kernelPos.y >= size.y) { break; }"
409 "half4 k4 = kernel[i];"
410 "for (int j = 0; j < 4; ++j) {"
411 "if (kernelPos.y >= size.y) { break; }"
418 "if (kernelPos.x >= size.x) {"
425 kMaxUniformKernelSize, kHeaderSkSL, kAccumulateSkSL, kFooterSkSL).c_str());
429 static SkMutex cacheLock;
432 static const auto makeTextureEffect = [](
SkISize maxKernelSize) {
435 "const int kMaxKernelHeight = %d;"
436 "uniform shader kernel;"
437 "uniform half2 innerGainAndBias;"
439 "for (int y = 0; y < kMaxKernelHeight; ++y) {"
440 "if (y >= size.y) { break; }"
441 "for (int x = 0; x < kMaxKernelWidth; ++x) {"
442 "if (x >= size.x) { break; }"
444 "int2 kernelPos = int2(x,y);"
445 "half k = kernel.eval(half2(kernelPos) + 0.5).a;"
446 "k = k * innerGainAndBias.x + innerGainAndBias.y;"
451 maxKernelSize.fWidth, maxKernelSize.fHeight,
452 kHeaderSkSL, kAccumulateSkSL, kFooterSkSL).
c_str());
456 if (texWidth == 0 && texHeight == 0) {
459 static_assert(kMaxKernelSize % 4 == 0,
"Must be a multiple of 4");
469 effect = textureShaderCache.insert(
key, std::move(newEffect));
478 const int kernelLength = fKernelSize.width() * fKernelSize.height();
479 const bool useTextureShader = kernelLength > kMaxUniformKernelSize;
480 if (useTextureShader && fKernelBitmap.empty()) {
485 useTextureShader ? fKernelSize.
height() : 0);
487 builder.child(
"child") = std::move(input);
489 if (useTextureShader) {
495 builder.uniform(
"innerGainAndBias") =
SkV2{fInnerGain, fInnerBias};
497 float paddedKernel[kMaxUniformKernelSize];
498 memcpy(paddedKernel, fKernel.data(), kernelLength*
sizeof(
float));
499 memset(paddedKernel+kernelLength, 0, (kMaxUniformKernelSize - kernelLength)*
sizeof(
float));
501 builder.uniform(
"kernel").set(paddedKernel, kMaxUniformKernelSize);
507 builder.uniform(
"gainAndBias") =
SkV2{fGain, fBias / 255.f};
508 builder.uniform(
"convolveAlpha") = fConvolveAlpha ? 1 : 0;
522 if (fConvolveAlpha && fBias != 0.f) {
528 outputBounds = this->boundsAffectedByKernel(childOutput.
layerBounds());
536 this->boundsSampledByKernel(outputBounds),
537 ShaderFlags::kSampledRepeatedly);
539 return this->createShader(context, inputs[0]);
550 return this->getChildInputLayerBounds(0, mapping, requiredInput, contentBounds);
553std::optional<skif::LayerSpace<SkIRect>> SkMatrixConvolutionImageFilter::onGetOutputLayerBounds(
556 if (fConvolveAlpha && fBias != 0.f) {
566 auto outputBounds = this->getChildOutputLayerBounds(0, mapping, contentBounds);
568 return this->boundsAffectedByKernel(*outputBounds);
574SkRect SkMatrixConvolutionImageFilter::computeFastBounds(
const SkRect& bounds)
const {
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
@ kAlpha_8_SkColorType
pixel with alpha in 8-bit byte
#define SK_FLATTENABLE_HOOKS(type)
#define SK_REGISTER_FLATTENABLE(type)
#define SK_IMAGEFILTER_UNFLATTEN_COMMON(localVar, expectedCount)
static int SkNextPow2(int value)
static int64_t sk_64_mul(int64_t a, int64_t b)
void SkRegisterMatrixConvolutionImageFilterFlattenable()
static sk_sp< SkRuntimeEffect > get_runtime_effect(int texWidth, int texHeight)
sk_sp< T > sk_ref_sp(T *obj)
SkRuntimeEffect * SkMakeRuntimeEffect(SkRuntimeEffect::Result(*make)(SkString, const SkRuntimeEffect::Options &), const char *sksl, SkRuntimeEffect::Options options=SkRuntimeEffect::Options{})
static bool SkScalarNearlyZero(SkScalar x, SkScalar tolerance=SK_ScalarNearlyZero)
#define SkScalarRoundToInt(x)
SK_API SkString static SkString SkStringPrintf()
uint8_t * getAddr8(int x, int y) const
bool tryAllocPixels(const SkImageInfo &info, size_t rowBytes)
static void Register(const char name[], Factory)
virtual skif::LayerSpace< SkIRect > onGetInputLayerBounds(const skif::Mapping &mapping, const skif::LayerSpace< SkIRect > &desiredOutput, std::optional< skif::LayerSpace< SkIRect > > contentBounds) const =0
virtual bool onAffectsTransparentBlack() const
void flatten(SkWriteBuffer &) const override
virtual std::optional< skif::LayerSpace< SkIRect > > onGetOutputLayerBounds(const skif::Mapping &mapping, std::optional< skif::LayerSpace< SkIRect > > contentBounds) const =0
virtual skif::FilterResult onFilterImage(const skif::Context &context) const =0
virtual SkRect computeFastBounds(const SkRect &bounds) const
static sk_sp< SkImageFilter > MatrixConvolution(const SkISize &kernelSize, const SkScalar kernel[], SkScalar gain, SkScalar bias, const SkIPoint &kernelOffset, SkTileMode tileMode, bool convolveAlpha, sk_sp< SkImageFilter > input, const CropRect &cropRect={})
static sk_sp< SkImageFilter > Crop(const SkRect &rect, SkTileMode tileMode, sk_sp< SkImageFilter > input)
@ kConvolutionImageFilterTilingUpdate
static SkRect MakeLargeS32()
static Result MakeForShader(SkString sksl, const Options &)
static size_t Mul(size_t x, size_t y)
const char * c_str() const
virtual sk_sp< SkImage > getCachedBitmap(const SkBitmap &data) const =0
const Backend * backend() const
const LayerSpace< SkIRect > & desiredOutput() const
Context withNewDesiredOutput(const LayerSpace< SkIRect > &desiredOutput) const
LayerSpace< SkIRect > layerBounds() const
static const uint8_t buffer[]
static float max(float r, float g, float b)
static float min(float r, float g, float b)
void adjust(int32_t dL, int32_t dT, int32_t dR, int32_t dB)
constexpr int32_t width() const
constexpr int32_t height() const
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)