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) {}
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) {
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 {
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);
498 (void)processValue(loadEdge(
src));
503 processValue(0u).store(
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 {
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);
736 (void)processValue(loadEdge(
src));
741 processValue(0u).store(
dst);
746 processValue(loadEdge(
src)).store(
dst);
753 fBuffer0Cursor = buffer0Cursor;
754 fBuffer1Cursor = buffer1Cursor;
787 static_assert(kMaxSigma <= 2183.0f);
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);
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()),
851 if (makerX->window() > 1) {
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(),
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(),
896 srcAddr,
src.rowBytesAsPixels(),
897 dstAddr,
dst.rowBytesAsPixels());
903 originalDstBounds.offset(-dstOrigin);
919 if (sigma.
width() == 0.f && sigma.
height() == 0.f) {
940 childOutput = childOutput.
applyCrop(inputCtx,
960 auto [resolvedChildOutput, origin] = childOutput.
imageAndOffset(inputCtx);
961 if (!resolvedChildOutput) {
966 resolvedChildOutput->width(),
967 resolvedChildOutput->height())};
970 srcBounds, maxOutput),
975 bool gpuBacked)
const {
991#
if defined(SK_GANESH) || defined(SK_GRAPHITE)
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);
sk_bzero(glyphs, sizeof(glyphs))
#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 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< SkIPoint > topLeft() const
void outset(const LayerSpace< SkISize > &delta)
bool intersect(const LayerSpace< SkIRect > &r)
LayerSpace< T > paramToLayer(const ParameterSpace< T > ¶mGeometry) const
static float max(float r, float g, float b)
static float min(float r, float g, float b)
SK_API sk_sp< SkDocument > Make(SkWStream *dst, const SkSerialProcs *=nullptr, std::function< void(const SkPicture *)> onEndPage=nullptr)
Optional< SkRect > bounds
bool AsBitmap(const SkSpecialImage *img, SkBitmap *result)
sk_sp< SkSpecialImage > MakeFromRaster(const SkIRect &subset, const SkBitmap &bm, const SkSurfaceProps &props)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
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