From ba8275148ab8b10393b053e2586b852796c4afc2 Mon Sep 17 00:00:00 2001 From: Herb Derby Date: Mon, 14 Aug 2017 15:35:46 -0400 Subject: [PATCH] Add an average mode for sigma < 2 The original code had an optimization when sigma < 2 that averaged pixels instead of Gaussian bluring them. This CL adds that behavior back to the new implementat. BUG=chromium:745290 Change-Id: I35b7de2138a859d546439cc2053b4b599a94ebe1 Reviewed-on: https://skia-review.googlesource.com/34180 Commit-Queue: Herb Derby Reviewed-by: Mike Klein --- src/core/SkMaskBlurFilter.cpp | 103 ++++++++++++++++++++++++++++++++-- src/core/SkMaskBlurFilter.h | 13 +++++ 2 files changed, 112 insertions(+), 4 deletions(-) diff --git a/src/core/SkMaskBlurFilter.cpp b/src/core/SkMaskBlurFilter.cpp index 42837f79a9..7eba2dab8d 100644 --- a/src/core/SkMaskBlurFilter.cpp +++ b/src/core/SkMaskBlurFilter.cpp @@ -25,22 +25,36 @@ static uint64_t weight_from_diameter(uint32_t d) { } static uint32_t filter_window(double sigma) { - auto possibleWindow = static_cast(floor(sigma * 3 * sqrt(2*kPi)/4 + 0.5)); + if (sigma < 2) { + uint32_t radius = static_cast(ceil(1.5 * sigma - 0.5)); + return 2 * radius + 1; + } + auto possibleWindow = static_cast(floor(sigma * 3 * sqrt(2 * kPi) / 4 + 0.5)); return std::max(1u, possibleWindow); } SkMaskBlurFilter::FilterInfo::FilterInfo(double sigma) - : fFilterWindow{filter_window(sigma)} - , fScaledWeight{(static_cast(1) << 32) / weight_from_diameter(fFilterWindow)} {} + : fIsSmall{sigma < 2} + , fFilterWindow{filter_window(sigma)} + , fWeight{fIsSmall ? fFilterWindow : weight_from_diameter(fFilterWindow)} + , fScaledWeight{(static_cast(1) << 32) / fWeight} +{ + SkASSERT(sigma >= 0); +} uint64_t SkMaskBlurFilter::FilterInfo::weight() const { - return weight_from_diameter(fFilterWindow); + return fWeight; } uint32_t SkMaskBlurFilter::FilterInfo::borderSize() const { + if (this->isSmall()) { + return (fFilterWindow - 1) / 2; + } + if ((fFilterWindow&1) == 0) { return 3 * (fFilterWindow / 2) - 1; } + return 3 * (fFilterWindow / 2); } @@ -63,6 +77,10 @@ uint64_t SkMaskBlurFilter::FilterInfo::scaledWeight() const { return fScaledWeight; } +bool SkMaskBlurFilter::FilterInfo::isSmall() const { + return fIsSmall; +} + SkMaskBlurFilter::SkMaskBlurFilter(double sigmaW, double sigmaH) : fInfoW{sigmaW}, fInfoH{sigmaH} , fBuffer0{skstd::make_unique_default(bufferSize(0))} @@ -165,6 +183,83 @@ size_t SkMaskBlurFilter::bufferSize(uint8_t bufferPass) const { // Blur one horizontal scan into the dst. void SkMaskBlurFilter::blurOneScan( + FilterInfo info, + const uint8_t* src, size_t srcStride, const uint8_t* srcEnd, + uint8_t* dst, size_t dstStride, uint8_t* dstEnd) const { + // We don't think this is good for quality. It is good for compatibility + // with previous expectations... + if (info.isSmall()) { + this->blurOneScanBox(info, src, srcStride, srcEnd, dst, dstStride, dstEnd); + } else { + this->blurOneScanGauss(info, src, srcStride, srcEnd, dst, dstStride, dstEnd); + } + +} + +// Blur one horizontal scan into the dst. +void SkMaskBlurFilter::blurOneScanBox( + FilterInfo info, + const uint8_t* src, size_t srcStride, const uint8_t* srcEnd, + uint8_t* dst, size_t dstStride, uint8_t* dstEnd) const { + + auto buffer0Begin = &fBuffer0[0]; + auto buffer0Cursor = buffer0Begin; + auto buffer0End = &fBuffer0[0] + info.diameter(0) - 1; + std::memset(&fBuffer0[0], 0, (buffer0End - buffer0Begin) * sizeof(fBuffer0[0])); + uint32_t sum0 = 0; + const uint64_t half = static_cast(1) << 31; + + // Consume the source generating pixels. + for (auto srcCursor = src; srcCursor < srcEnd; dst += dstStride, srcCursor += srcStride) { + uint32_t s = *srcCursor; + sum0 += s; + + *dst = SkTo((info.scaledWeight() * sum0 + half) >> 32); + + + sum0 -= *buffer0Cursor; + *buffer0Cursor = s; + buffer0Cursor = (buffer0Cursor + 1) < buffer0End ? buffer0Cursor + 1 : &fBuffer0[0]; + } + + // This handles the case when both ends of the box are not between [src, srcEnd), and both + // are zero at that point. + for (auto i = 0; i < static_cast(2 * info.borderSize()) - (srcEnd - src); i++) { + uint32_t s = 0; + sum0 += s; + + *dst = SkTo((info.scaledWeight() * sum0 + half) >> 32); + + sum0 -= *buffer0Cursor; + *buffer0Cursor = s; + buffer0Cursor = (buffer0Cursor + 1) < buffer0End ? buffer0Cursor + 1 : &fBuffer0[0]; + dst += dstStride; + } + + // Starting from the right, fill in the rest of the buffer. + std::memset(&fBuffer0[0], 0, (buffer0End - &fBuffer0[0]) * sizeof(fBuffer0[0])); + + sum0 = 0; + + uint8_t* dstCursor = dstEnd; + const uint8_t* srcCursor = srcEnd; + do { + dstCursor -= dstStride; + srcCursor -= srcStride; + uint32_t s = *srcCursor; + sum0 += s; + + *dstCursor = SkTo((info.scaledWeight() * sum0 + half) >> 32); + + sum0 -= *buffer0Cursor; + *buffer0Cursor = s; + buffer0Cursor = (buffer0Cursor + 1) < buffer0End ? buffer0Cursor + 1 : &fBuffer0[0]; + } while (dstCursor > dst); + +} + +// Blur one horizontal scan into the dst. +void SkMaskBlurFilter::blurOneScanGauss( FilterInfo info, const uint8_t* src, size_t srcStride, const uint8_t* srcEnd, uint8_t* dst, size_t dstStride, uint8_t* dstEnd) const { diff --git a/src/core/SkMaskBlurFilter.h b/src/core/SkMaskBlurFilter.h index c530488efb..000d9493a0 100644 --- a/src/core/SkMaskBlurFilter.h +++ b/src/core/SkMaskBlurFilter.h @@ -37,8 +37,13 @@ public: // A factor used to simulate division using multiplication and shift. uint64_t scaledWeight() const; + // Returned when sigma < 2. + bool isSmall() const; + private: + const bool fIsSmall; const uint32_t fFilterWindow; + const uint64_t fWeight; const uint64_t fScaledWeight; }; @@ -59,6 +64,14 @@ private: const uint8_t* src, size_t srcStride, const uint8_t* srcEnd, uint8_t* dst, size_t dstStride, uint8_t* dstEnd) const; + void blurOneScanBox(FilterInfo gen, + const uint8_t* src, size_t srcStride, const uint8_t* srcEnd, + uint8_t* dst, size_t dstStride, uint8_t* dstEnd) const; + + void blurOneScanGauss(FilterInfo gen, + const uint8_t* src, size_t srcStride, const uint8_t* srcEnd, + uint8_t* dst, size_t dstStride, uint8_t* dstEnd) const; + const FilterInfo fInfoW, fInfoH;