42#if defined(SK_GANESH) || defined(SK_GRAPHITE)
46#if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE1
47 #include <xmmintrin.h>
48 #define SK_PREFETCH(ptr) _mm_prefetch(reinterpret_cast<const char*>(ptr), _MM_HINT_T0)
49#elif defined(__GNUC__)
50 #define SK_PREFETCH(ptr) __builtin_prefetch(ptr)
52 #define SK_PREFETCH(ptr)
66 , fLegacyTileMode(legacyTileMode) {}
74 friend void ::SkRegisterBlurImageFilterFlattenable();
84 std::optional<skif::LayerSpace<SkIRect>> onGetOutputLayerBounds(
92 bool gpuBacked)
const {
110 if (!
SkIsFinite(sigmaX, sigmaY) || sigmaX < 0.f || sigmaY < 0.f) {
119 return sk_make_sp<SkBlurImageFilter>(
SkSize{sigmaX, sigmaY}, tileMode, std::move(input));
132 filter = sk_make_sp<SkBlurImageFilter>(
SkSize{sigmaX, sigmaY}, std::move(filter));
159 sigmaX, sigmaY, tileMode,
common.getInput(0),
common.cropRect());
167 buffer.writeInt(
static_cast<int>(fLegacyTileMode));
181int calculate_window(
double sigma) {
183 return std::max(1, possibleWindow);
191static constexpr SkScalar kMaxSigma = 532.f;
195 explicit Pass(
int border) : fBorder(border) {}
196 virtual ~Pass() =
default;
198 void blur(
int srcLeft,
int srcRight,
int dstRight,
199 const uint32_t* src,
int srcStride,
200 uint32_t* dst,
int dstStride) {
203 auto srcStart = srcLeft - fBorder,
204 srcEnd = srcRight - fBorder,
209 const uint32_t* srcCursor =
src;
210 uint32_t* dstCursor =
dst;
212 if (dstIdx < srcIdx) {
216 int commonEnd = std::min(srcIdx, dstEnd);
217 while (dstIdx < commonEnd) {
219 dstCursor += dstStride;
223 }
else if (srcIdx < dstIdx) {
226 if (
int commonEnd = std::min(dstIdx, srcEnd); srcIdx < commonEnd) {
228 int n = commonEnd - srcIdx;
229 this->blurSegment(n, srcCursor, srcStride,
nullptr, 0);
231 srcCursor += n * srcStride;
233 if (srcIdx < dstIdx) {
235 int n = dstIdx - srcIdx;
236 this->blurSegment(n,
nullptr, 0,
nullptr, 0);
241 if (
int commonEnd = std::min(dstEnd, srcEnd); dstIdx < commonEnd) {
246 int n = commonEnd - dstIdx;
247 this->blurSegment(n, srcCursor, srcStride, dstCursor, dstStride);
248 srcCursor += n * srcStride;
249 dstCursor += n * dstStride;
255 if (dstIdx < dstEnd) {
256 int n = dstEnd - dstIdx;
257 this->blurSegment(n,
nullptr, 0, dstCursor, dstStride);
262 virtual void startBlur() = 0;
263 virtual void blurSegment(
264 int n,
const uint32_t* src,
int srcStride, uint32_t* dst,
int dstStride) = 0;
273 virtual ~PassMaker() =
default;
275 virtual size_t bufferSizeBytes()
const = 0;
276 int window()
const {
return fWindow;}
284class GaussPass final :
public Pass {
298 static PassMaker* MakeMaker(
double sigma,
SkArenaAlloc* alloc) {
300 int window = calculate_window(sigma);
305 class Maker :
public PassMaker {
309 return GaussPass::Make(this->
window(), buffer, alloc);
312 size_t bufferSizeBytes()
const override {
314 size_t onePassSize =
window - 1;
322 size_t bufferCount = (
window & 1) == 1 ? 3 * onePassSize : 3 * onePassSize + 1;
332 int passSize =
window - 1;
388 int window3 = window2 *
window;
389 int divisor = (
window & 1) == 1 ? window3 : window3 + window2;
390 return alloc->
make<GaussPass>(buffer0, buffer1, buffer2, buffersEnd, border, divisor);
403 , fBuffersEnd{buffersEnd}
404 , fDivider(divisor) {}
407 void startBlur()
override {
411 auto half = fDivider.half();
415 fBuffer0Cursor = fBuffer0;
416 fBuffer1Cursor = fBuffer1;
417 fBuffer2Cursor = fBuffer2;
459 int n,
const uint32_t* src,
int srcStride, uint32_t* dst,
int dstStride)
override {
475 sum2 -= *buffer2Cursor;
476 *buffer2Cursor = sum1;
477 buffer2Cursor = (buffer2Cursor + 1) < fBuffersEnd ? buffer2Cursor + 1 : fBuffer2;
478 sum1 -= *buffer1Cursor;
479 *buffer1Cursor = sum0;
480 buffer1Cursor = (buffer1Cursor + 1) < fBuffer2 ? buffer1Cursor + 1 : fBuffer1;
481 sum0 -= *buffer0Cursor;
482 *buffer0Cursor = leadingEdge;
483 buffer0Cursor = (buffer0Cursor + 1) < fBuffer1 ? buffer0Cursor + 1 : fBuffer0;
485 return skvx::cast<uint8_t>(blurred);
488 auto loadEdge = [&](
const uint32_t* srcCursor) {
494 (void)processValue(0);
496 }
else if (src && !dst) {
498 (void)processValue(loadEdge(src));
501 }
else if (!src && dst) {
503 processValue(0u).store(dst);
506 }
else if (src && dst) {
508 processValue(loadEdge(src)).store(dst);
515 fBuffer0Cursor = buffer0Cursor;
516 fBuffer1Cursor = buffer1Cursor;
517 fBuffer2Cursor = buffer2Cursor;
541class TentPass final :
public Pass {
554 static PassMaker* MakeMaker(
double sigma,
SkArenaAlloc* alloc) {
556 int gaussianWindow = calculate_window(sigma);
562 int tentWindow = 3 * gaussianWindow / 2;
563 if (tentWindow >= 4104) {
567 class Maker :
public PassMaker {
571 return TentPass::Make(this->
window(), buffer, alloc);
574 size_t bufferSizeBytes()
const override {
575 size_t onePassSize = this->
window() - 1;
582 size_t bufferCount = 2 * onePassSize;
587 return alloc->
make<Maker>(tentWindow);
596 int passSize =
window - 1;
643 return alloc->
make<TentPass>(buffer0, buffer1, buffersEnd, border, divisor);
654 , fBuffersEnd{buffersEnd}
655 , fDivider(divisor) {}
658 void startBlur()
override {
660 auto half = fDivider.half();
664 fBuffer0Cursor = fBuffer0;
665 fBuffer1Cursor = fBuffer1;
703 int n,
const uint32_t* src,
int srcStride, uint32_t* dst,
int dstStride)
override {
716 sum1 -= *buffer1Cursor;
717 *buffer1Cursor = sum0;
718 buffer1Cursor = (buffer1Cursor + 1) < fBuffersEnd ? buffer1Cursor + 1 : fBuffer1;
719 sum0 -= *buffer0Cursor;
720 *buffer0Cursor = leadingEdge;
721 buffer0Cursor = (buffer0Cursor + 1) < fBuffer1 ? buffer0Cursor + 1 : fBuffer0;
723 return skvx::cast<uint8_t>(blurred);
726 auto loadEdge = [&](
const uint32_t* srcCursor) {
732 (void)processValue(0);
734 }
else if (src && !dst) {
736 (void)processValue(loadEdge(src));
739 }
else if (!src && dst) {
741 processValue(0u).store(dst);
744 }
else if (src && dst) {
746 processValue(loadEdge(src)).store(dst);
753 fBuffer0Cursor = buffer0Cursor;
754 fBuffer1Cursor = buffer1Cursor;
787 static_assert(kMaxSigma <= 2183.0f);
790 SkASSERT(input->width() == srcBounds.width() && input->height() == srcBounds.height());
793 auto makeMaker = [&](
double sigma) -> PassMaker* {
794 SkASSERT(0 <= sigma && sigma <= 2183);
795 if (PassMaker* maker = GaussPass::MakeMaker(sigma, &alloc)) {
798 if (PassMaker* maker = TentPass::MakeMaker(sigma, &alloc)) {
804 PassMaker* makerX = makeMaker(sigma.width());
805 PassMaker* makerY = makeMaker(sigma.height());
807 SkASSERT(makerX->window() > 1 || makerY->window() > 1);
810 if (!SkSpecialImages::AsBitmap(input.
get(), &src)) {
813 if (
src.colorType() != kN32_SkColorType) {
817 auto originalDstBounds = dstBounds;
818 if (makerX->window() > 1) {
826 dstBounds.outset(yPadding);
831 if (!
dst.tryAllocPixels(
src.info().makeWH(dstBounds.width(), dstBounds.height()))) {
837 makerY->bufferSizeBytes()),
847 int loopStart = std::max(srcBounds.left(), dstBounds.left());
848 int loopEnd = std::min(srcBounds.right(), dstBounds.right());
851 if (makerX->window() > 1) {
854 loopStart = std::max(srcBounds.top(), dstBounds.top());
855 loopEnd = std::min(srcBounds.bottom(), dstBounds.bottom());
857 auto srcAddr =
src.getAddr32(0, loopStart - srcBounds.top());
858 auto dstAddr =
dst.getAddr32(0, loopStart - dstBounds.top());
861 Pass* pass = makerX->makePass(
buffer, &alloc);
862 for (
int y = loopStart;
y < loopEnd; ++
y) {
863 pass->blur(srcBounds.left() - dstBounds.left(),
864 srcBounds.right() - dstBounds.left(),
868 srcAddr +=
src.rowBytesAsPixels();
869 dstAddr +=
dst.rowBytesAsPixels();
874 loopStart = originalDstBounds.left();
875 loopEnd = originalDstBounds.right();
879 dstYOffset = originalDstBounds.top() - dstBounds.top();
881 srcBounds = dstBounds;
882 dstBounds = originalDstBounds;
887 if (makerY->window() > 1) {
888 auto srcAddr =
src.getAddr32(loopStart - srcBounds.left(), 0);
889 auto dstAddr =
dst.getAddr32(loopStart - dstBounds.left(), dstYOffset);
891 Pass* pass = makerY->makePass(
buffer, &alloc);
892 for (
int x = loopStart;
x < loopEnd; ++
x) {
893 pass->blur(srcBounds.top() - dstBounds.top(),
894 srcBounds.bottom() - dstBounds.top(),
896 srcAddr,
src.rowBytesAsPixels(),
897 dstAddr,
dst.rowBytesAsPixels());
903 originalDstBounds.offset(-dstOrigin);
904 return SkSpecialImages::MakeFromRaster(
SkIRect(originalDstBounds),
919 if (sigma.width() == 0.f && sigma.height() == 0.f) {
924 SkASSERT(sigma.width() >= 0.f && sigma.width() <= kMaxSigma &&
925 sigma.height() >= 0.f && sigma.height() <= kMaxSigma);
940 childOutput = childOutput.
applyCrop(inputCtx,
960 auto [resolvedChildOutput, origin] = childOutput.
imageAndOffset(inputCtx);
961 if (!resolvedChildOutput) {
966 resolvedChildOutput->width(),
967 resolvedChildOutput->height())};
970 srcBounds, maxOutput),
971 maxOutput.topLeft()};
975 bool gpuBacked)
const {
979 std::min(sigma.height(), kMaxSigma)});
990 if (!
SkIsFinite(sigma.width()) || (!gpuBacked && calculate_window(sigma.width()) <= 1)
991#
if defined(SK_GANESH) || defined(SK_GRAPHITE)
998 if (!
SkIsFinite(sigma.height()) || (!gpuBacked && calculate_window(sigma.height()) <= 1)
999#
if defined(SK_GANESH) || defined(SK_GRAPHITE)
1016 this->kernelBounds(mapping, desiredOutput,
true);
1017 return this->getChildInputLayerBounds(0, mapping, requiredInput, contentBounds);
1020std::optional<skif::LayerSpace<SkIRect>> SkBlurImageFilter::onGetOutputLayerBounds(
1023 auto childOutput = this->getChildOutputLayerBounds(0, mapping, contentBounds);
1027 return this->kernelBounds(mapping, *childOutput,
true);
1033SkRect SkBlurImageFilter::computeFastBounds(
const SkRect& src)
const {
1034 SkRect bounds = this->getInput(0) ? this->getInput(0)->computeFastBounds(src) :
src;
#define SK_ABORT(message,...)
void SkRegisterBlurImageFilterFlattenable()
constexpr SkColor SK_ColorTRANSPARENT
#define SK_FLATTENABLE_HOOKS(type)
#define SK_REGISTER_FLATTENABLE(type)
constexpr double SK_DoublePI
static bool SkIsFinite(T x, Pack... values)
#define SK_IMAGEFILTER_UNFLATTEN_COMMON(localVar, expectedCount)
static std::unique_ptr< SkEncoder > Make(SkWStream *dst, const SkPixmap *src, const SkYUVAPixmaps *srcYUVA, const SkColorSpace *srcYUVAColorSpace, const SkJpegEncoder::Options &options)
static void sk_bzero(void *buffer, size_t size)
static constexpr bool SkToBool(const T &x)
void * makeBytesAlignedTo(size_t size, size_t align)
auto make(Ctor &&ctor) -> decltype(ctor(nullptr))
static void Register(const char name[], Factory)
void flatten(SkWriteBuffer &) const override
static sk_sp< SkImageFilter > Blur(SkScalar sigmaX, SkScalar sigmaY, SkTileMode tileMode, sk_sp< SkImageFilter > input, const CropRect &cropRect={})
static sk_sp< SkImageFilter > Crop(const SkRect &rect, SkTileMode tileMode, sk_sp< SkImageFilter > input)
const SkSurfaceProps & surfaceProps() const
virtual const SkBlurEngine * getBlurEngine() const =0
const Backend * backend() const
const LayerSpace< SkIRect > & desiredOutput() const
Context withNewDesiredOutput(const LayerSpace< SkIRect > &desiredOutput) const
const Mapping & mapping() const
FilterResult applyCrop(const Context &ctx, const LayerSpace< SkIRect > &crop, SkTileMode tileMode=SkTileMode::kDecal) const
sk_sp< SkSpecialImage > imageAndOffset(const Context &ctx, SkIPoint *offset) const
LayerSpace< SkIRect > layerBounds() const
LayerSpace< T > paramToLayer(const ParameterSpace< T > ¶mGeometry) const
static const uint8_t buffer[]
Optional< SkRect > bounds
constexpr bool BlurIsEffectivelyIdentity(float sigma)
SIN Vec< N, float > sqrt(const Vec< N, float > &x)
SIN Vec< N, float > floor(const Vec< N, float > &x)
SIN Vec< N, float > ceil(const Vec< N, float > &x)
static constexpr SkIRect MakeXYWH(int32_t x, int32_t y, int32_t w, int32_t h)
static SKVX_ALWAYS_INLINE Vec Load(const void *ptr)
SKVX_ALWAYS_INLINE void store(void *ptr) const