aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/SkMaskBlurFilter.cpp
diff options
context:
space:
mode:
authorGravatar Ben Wagner <bungeman@google.com>2018-05-07 17:58:25 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2018-05-08 19:15:34 +0000
commit2700779c531fac93eb24484bd4437a77557d4c68 (patch)
treee60f671faf0d07105d6f809faf03e080f1eac519 /src/core/SkMaskBlurFilter.cpp
parentefb84e6734074defa325a27273c8c98903955f52 (diff)
SkBlurMask to blur all the things.
SkBlurMask used to handle only A8 as input, and was recently extended to ARGB as input. This adds LCD16 and A1 as inputs as well, allowing SkScalerContext to no longer force all requests with a mask filter to A8. This also fixes the shadows for LCD16 and A1 glyphs, which at small sigma were quite obviously wrong since they were generated from an A8 version. Change-Id: I151cdbf5556d18c2938da990ecee5bcd8ee31be8 Reviewed-on: https://skia-review.googlesource.com/126615 Reviewed-by: Herb Derby <herb@google.com> Reviewed-by: Jim Van Verth <jvanverth@google.com> Commit-Queue: Ben Wagner <bungeman@google.com>
Diffstat (limited to 'src/core/SkMaskBlurFilter.cpp')
-rw-r--r--src/core/SkMaskBlurFilter.cpp286
1 files changed, 213 insertions, 73 deletions
diff --git a/src/core/SkMaskBlurFilter.cpp b/src/core/SkMaskBlurFilter.cpp
index acfa955a70..594760a35c 100644
--- a/src/core/SkMaskBlurFilter.cpp
+++ b/src/core/SkMaskBlurFilter.cpp
@@ -6,6 +6,7 @@
*/
#include "SkArenaAlloc.h"
+#include "SkColorPriv.h"
#include "SkGaussFilter.h"
#include "SkMalloc.h"
#include "SkMaskBlurFilter.h"
@@ -14,6 +15,7 @@
#include <cmath>
#include <climits>
+namespace {
static const double kPi = 3.14159265358979323846264338327950288;
class PlanGauss final {
@@ -101,8 +103,8 @@ public:
, fBuffer2End{buffer2End}
{ }
- void blur(const uint8_t* src, int srcStride, const uint8_t* srcEnd,
- uint8_t* dst, int dstStride, uint8_t* dstEnd) const {
+ template <typename AlphaIter> void blur(const AlphaIter srcBegin, const AlphaIter srcEnd,
+ uint8_t* dst, int dstStride, uint8_t* dstEnd) const {
auto buffer0Cursor = fBuffer0;
auto buffer1Cursor = fBuffer1;
auto buffer2Cursor = fBuffer2;
@@ -114,9 +116,8 @@ public:
uint32_t sum2 = 0;
// Consume the source generating pixels.
- for (auto srcCursor = src;
- srcCursor < srcEnd; dst += dstStride, srcCursor += srcStride) {
- uint32_t leadingEdge = *srcCursor;
+ for (AlphaIter src = srcBegin; src < srcEnd; ++src, dst += dstStride) {
+ uint32_t leadingEdge = *src;
sum0 += leadingEdge;
sum1 += sum0;
sum2 += sum1;
@@ -166,11 +167,10 @@ public:
sum0 = sum1 = sum2 = 0;
uint8_t* dstCursor = dstEnd;
- const uint8_t* srcCursor = srcEnd;
+ AlphaIter src = srcEnd;
while (dstCursor > dst) {
dstCursor -= dstStride;
- srcCursor -= srcStride;
- uint32_t leadingEdge = *srcCursor;
+ uint32_t leadingEdge = *(--src);
sum0 += leadingEdge;
sum1 += sum0;
sum2 += sum1;
@@ -231,6 +231,8 @@ public:
int fPass2Size;
};
+} // namespace
+
// NB 136 is the largest sigma that will not cause a buffer full of 255 mask values to overflow
// using the Gauss filter. It also limits the size of buffers used hold intermediate values.
// Explanation of maximums:
@@ -255,22 +257,71 @@ bool SkMaskBlurFilter::hasNoBlur() const {
return (3 * fSigmaW <= 1) && (3 * fSigmaH <= 1);
}
-static constexpr uint16_t _____ = 0u;
-static constexpr uint16_t kHalf = 0x80u;
-
-static SK_ALWAYS_INLINE Sk8h load(const uint8_t* from, int width, int stride) {
- SkASSERT(0 < width && width <= 8);
- uint8_t buffer[8];
- if (width < 8 || stride != 1) {
+namespace {
+struct LoadBW {
+ static constexpr int StrideOf8 = 1;
+ static SK_ALWAYS_INLINE Sk8h load(const uint8_t* from, int width) {
+ SkASSERT(0 < width && width <= 8);
+ uint8_t buffer[8];
sk_bzero(buffer, sizeof(buffer));
- for (int i = 0; i < width; i++) {
- buffer[i] = from[i * stride];
+ uint8_t value = *from;
+ for (int i = 0; i < width; ++i) {
+ buffer[i] = (value >> (7 - i)) & 1 ? 0xFF : 0;
}
- from = buffer;
+ auto v = SkNx_cast<uint16_t>(Sk8b::Load(buffer));
+ // Convert from 0-255 to 8.8 encoding.
+ return v << 8;
+ }
+};
+struct LoadA8 {
+ static constexpr int StrideOf8 = 8 * sizeof(uint8_t);
+ static SK_ALWAYS_INLINE Sk8h load(const uint8_t* from, int width) {
+ SkASSERT(0 < width && width <= 8);
+ uint8_t buffer[8];
+ if (width < 8) {
+ sk_bzero(buffer, sizeof(buffer));
+ for (int i = 0; i < width; ++i) {
+ buffer[i] = from[i];
+ }
+ from = buffer;
+ }
+ auto v = SkNx_cast<uint16_t>(Sk8b::Load(from));
+ // Convert from 0-255 to 8.8 encoding.
+ return v << 8;
+ }
+};
+struct LoadARGB {
+ static constexpr int StrideOf8 = 8 * sizeof(uint32_t);
+ static SK_ALWAYS_INLINE Sk8h load(const uint8_t* from, int width) {
+ SkASSERT(0 < width && width <= 8);
+ uint8_t buffer[8];
+ sk_bzero(buffer, sizeof(buffer));
+ for (int i = 0; i < width; ++i) {
+ uint32_t packed = reinterpret_cast<const uint32_t*>(from)[i];
+ buffer[i] = SkGetPackedA32(packed);
+ }
+ auto v = SkNx_cast<uint16_t>(Sk8b::Load(buffer));
+ // Convert from 0-255 to 8.8 encoding.
+ return v << 8;
+ }
+};
+struct LoadLCD {
+ static constexpr int StrideOf8 = 8 * sizeof(uint16_t);
+ static SK_ALWAYS_INLINE Sk8h load(const uint8_t* from, int width) {
+ SkASSERT(0 < width && width <= 8);
+ uint8_t buffer[8];
+ sk_bzero(buffer, sizeof(buffer));
+ for (int i = 0; i < width; ++i) {
+ unsigned packed = reinterpret_cast<const uint16_t*>(from)[i];
+ unsigned r = SkPacked16ToR32(packed);
+ unsigned g = SkPacked16ToG32(packed);
+ unsigned b = SkPacked16ToB32(packed);
+ buffer[i] = (r + g + b) / 3;
+ }
+ auto v = SkNx_cast<uint16_t>(Sk8b::Load(buffer));
+ // Convert from 0-255 to 8.8 encoding.
+ return v << 8;
}
- auto v = SkNx_cast<uint16_t>(Sk8b::Load(from));
- // Convert from 0-255 to 8.8 encoding.
- return v << 8;
};
static SK_ALWAYS_INLINE void store(uint8_t* to, const Sk8h& v, int width) {
@@ -286,6 +337,9 @@ static SK_ALWAYS_INLINE void store(uint8_t* to, const Sk8h& v, int width) {
}
};
+static constexpr uint16_t _____ = 0u;
+static constexpr uint16_t kHalf = 0x80u;
+
// In all the blur_x_radius_N and blur_y_radius_N functions the gaussian values are encoded
// in 0.16 format, none of the values is greater than one. The incoming mask values are in 8.8
// format. The resulting multiply has a 8.24 format, by the mulhi truncates the lower 16 bits
@@ -514,14 +568,14 @@ static SK_ALWAYS_INLINE void blur_row(
// Go by multiples of 8 in src.
int x = 0;
for (; x <= srcW - 8; x += 8) {
- blur(load(src, 8, 1), g0, g1, g2, g3, g4, &d0, &d8);
+ blur(LoadA8::load(src, 8), g0, g1, g2, g3, g4, &d0, &d8);
store(dst, d0, 8);
d0 = d8;
d8 = Sk8h{kHalf};
- src += 8;
+ src += LoadA8::StrideOf8;
dst += 8;
}
@@ -529,7 +583,7 @@ static SK_ALWAYS_INLINE void blur_row(
int srcTail = srcW - x;
if (srcTail > 0) {
- blur(load(src, srcTail, 1), g0, g1, g2, g3, g4, &d0, &d8);
+ blur(LoadA8::load(src, srcTail), g0, g1, g2, g3, g4, &d0, &d8);
int dstTail = std::min(8, dstW - x);
store(dst, d0, dstTail);
@@ -721,10 +775,10 @@ static SK_ALWAYS_INLINE Sk8h blur_y_radius_4(
using BlurY = decltype(blur_y_radius_1);
// BlurY will be one of blur_y_radius_(1|2|3|4).
-static SK_ALWAYS_INLINE void blur_column(
+template <typename Loader> static SK_ALWAYS_INLINE void blur_column(
BlurY blur, int radius, int width,
const Sk8h& g0, const Sk8h& g1, const Sk8h& g2, const Sk8h& g3, const Sk8h& g4,
- const uint8_t* src, size_t srcRB, int srcStride, int srcH,
+ const uint8_t* src, size_t srcRB, int srcH,
uint8_t* dst, size_t dstRB) {
Sk8h d01{kHalf}, d12{kHalf}, d23{kHalf}, d34{kHalf},
d45{kHalf}, d56{kHalf}, d67{kHalf}, d78{kHalf};
@@ -737,7 +791,7 @@ static SK_ALWAYS_INLINE void blur_column(
};
for (int y = 0; y < srcH; y += 1) {
- auto s = load(src, width, srcStride);
+ auto s = Loader::load(src, width);
auto b = blur(s,
g0, g1, g2, g3, g4,
&d01, &d12, &d23, &d34, &d45, &d56, &d67, &d78);
@@ -761,9 +815,9 @@ static SK_ALWAYS_INLINE void blur_column(
}
// BlurY will be one of blur_y_radius_(1|2|3|4).
-static SK_ALWAYS_INLINE void blur_y_rect(
+template <typename Loader> static SK_ALWAYS_INLINE void blur_y_rect(
BlurY blur, int radius, uint16_t *gauss,
- const uint8_t *src, size_t srcRB, int srcStride, int srcW, int srcH,
+ const uint8_t *src, size_t srcRB, int srcW, int srcH,
uint8_t *dst, size_t dstRB) {
Sk8h g0{gauss[0]},
@@ -774,51 +828,51 @@ static SK_ALWAYS_INLINE void blur_y_rect(
int x = 0;
for (; x <= srcW - 8; x += 8) {
- blur_column(blur, radius, 8,
- g0, g1, g2, g3, g4,
- src, srcRB, srcStride, srcH,
- dst, dstRB);
- src += 8 * srcStride;
+ blur_column<Loader>(blur, radius, 8,
+ g0, g1, g2, g3, g4,
+ src, srcRB, srcH,
+ dst, dstRB);
+ src += Loader::StrideOf8;
dst += 8;
}
int xTail = srcW - x;
if (xTail > 0) {
- blur_column(blur, radius, xTail,
- g0, g1, g2, g3, g4,
- src, srcRB, srcStride, srcH,
- dst, dstRB);
+ blur_column<Loader>(blur, radius, xTail,
+ g0, g1, g2, g3, g4,
+ src, srcRB, srcH,
+ dst, dstRB);
}
}
-SK_ATTRIBUTE(noinline) static void direct_blur_y(
+template <typename Loader> SK_ATTRIBUTE(noinline) static void direct_blur_y(
int radius, uint16_t* gauss,
- const uint8_t* src, size_t srcRB, int srcStride, int srcW, int srcH,
+ const uint8_t* src, size_t srcRB, int srcW, int srcH,
uint8_t* dst, size_t dstRB) {
switch (radius) {
case 1:
- blur_y_rect(blur_y_radius_1, 1, gauss,
- src, srcRB, srcStride, srcW, srcH,
- dst, dstRB);
+ blur_y_rect<Loader>(blur_y_radius_1, 1, gauss,
+ src, srcRB, srcW, srcH,
+ dst, dstRB);
break;
case 2:
- blur_y_rect(blur_y_radius_2, 2, gauss,
- src, srcRB, srcStride, srcW, srcH,
- dst, dstRB);
+ blur_y_rect<Loader>(blur_y_radius_2, 2, gauss,
+ src, srcRB, srcW, srcH,
+ dst, dstRB);
break;
case 3:
- blur_y_rect(blur_y_radius_3, 3, gauss,
- src, srcRB, srcStride, srcW, srcH,
- dst, dstRB);
+ blur_y_rect<Loader>(blur_y_radius_3, 3, gauss,
+ src, srcRB, srcW, srcH,
+ dst, dstRB);
break;
case 4:
- blur_y_rect(blur_y_radius_4, 4, gauss,
- src, srcRB, srcStride, srcW, srcH,
- dst, dstRB);
+ blur_y_rect<Loader>(blur_y_radius_4, 4, gauss,
+ src, srcRB, srcW, srcH,
+ dst, dstRB);
break;
default:
@@ -871,17 +925,31 @@ static SkIPoint small_blur(double sigmaX, double sigmaY, const SkMask& src, SkMa
//TODO: handle bluring in only one direction.
- int srcStride = 1;
- int srcAlphaOffset = 0;
- if (src.fFormat == SkMask::kARGB32_Format) {
- srcStride = 4;
- srcAlphaOffset = SK_A32_SHIFT / 8;
- }
-
// Blur vertically and copy to destination.
- direct_blur_y(radiusY, gaussFactorsY,
- src.fImage + srcAlphaOffset, srcRB, srcStride, srcW, srcH,
- dst->fImage + radiusX, dstRB);
+ switch (src.fFormat) {
+ case SkMask::kBW_Format:
+ direct_blur_y<LoadBW>(radiusY, gaussFactorsY,
+ src.fImage, srcRB, srcW, srcH,
+ dst->fImage + radiusX, dstRB);
+ break;
+ case SkMask::kA8_Format:
+ direct_blur_y<LoadA8>(radiusY, gaussFactorsY,
+ src.fImage, srcRB, srcW, srcH,
+ dst->fImage + radiusX, dstRB);
+ break;
+ case SkMask::kARGB32_Format:
+ direct_blur_y<LoadARGB>(radiusY, gaussFactorsY,
+ src.fImage, srcRB, srcW, srcH,
+ dst->fImage + radiusX, dstRB);
+ break;
+ case SkMask::kLCD16_Format:
+ direct_blur_y<LoadLCD>(radiusY, gaussFactorsY,
+ src.fImage, srcRB, srcW, srcH,
+ dst->fImage + radiusX, dstRB);
+ break;
+ default:
+ SK_ABORT("Unhandled format.");
+ }
// Blur horizontally in place.
direct_blur_x(radiusX, gaussFactorsX,
@@ -891,6 +959,65 @@ static SkIPoint small_blur(double sigmaX, double sigmaY, const SkMask& src, SkMa
return {radiusX, radiusY};
}
+struct AlphaIterA1 {
+ AlphaIterA1(const uint8_t* ptr, int offset) : fPtr(ptr), fOffset(7 - offset) {}
+ AlphaIterA1(const AlphaIterA1& that) : fPtr(that.fPtr), fOffset(that.fOffset) {}
+ AlphaIterA1& operator++() {
+ if (0 < fOffset ) {
+ --fOffset;
+ } else {
+ ++fPtr;
+ fOffset = 7;
+ }
+ return *this;
+ }
+ AlphaIterA1& operator--() {
+ if (fOffset < 7) {
+ ++fOffset;
+ } else {
+ --fPtr;
+ fOffset = 0;
+ }
+ return *this;
+ }
+ uint8_t operator*() { return ((*fPtr) >> fOffset) & 1 ? 0xFF : 0; }
+ bool operator<(const AlphaIterA1& that) {
+ return fPtr < that.fPtr || (fPtr == that.fPtr && fOffset > that.fOffset);
+ }
+ const uint8_t* fPtr;
+ int fOffset;
+};
+
+using AlphaIterA8 = uint8_t*;
+
+struct AlphaIterARGB {
+ AlphaIterARGB(const uint32_t* ptr) : fPtr(ptr) {}
+ AlphaIterARGB(const AlphaIterARGB& that) : fPtr(that.fPtr) {}
+ AlphaIterARGB& operator++() { ++fPtr; return *this; }
+ AlphaIterARGB& operator--() { --fPtr; return *this; }
+ uint8_t operator*() { return SkGetPackedA32(*fPtr); }
+ bool operator<(const AlphaIterARGB& that) { return fPtr < that.fPtr; }
+ const uint32_t* fPtr;
+};
+
+struct AlphaIterLCD {
+ AlphaIterLCD(const uint16_t* ptr) : fPtr(ptr) {}
+ AlphaIterLCD(const AlphaIterLCD& that) : fPtr(that.fPtr) {}
+ AlphaIterLCD& operator++() { ++fPtr; return *this; }
+ AlphaIterLCD& operator--() { --fPtr; return *this; }
+ uint8_t operator*() {
+ unsigned packed = *fPtr;
+ unsigned r = SkPacked16ToR32(packed);
+ unsigned g = SkPacked16ToG32(packed);
+ unsigned b = SkPacked16ToB32(packed);
+ return (r + g + b) / 3;
+ }
+ bool operator<(const AlphaIterLCD& that) { return fPtr < that.fPtr; }
+ const uint16_t* fPtr;
+};
+
+} // namespace
+
// TODO: assuming sigmaW = sigmaH. Allow different sigmas. Right now the
// API forces the sigmas to be the same.
SkIPoint SkMaskBlurFilter::blur(const SkMask& src, SkMask* dst) const {
@@ -933,20 +1060,33 @@ SkIPoint SkMaskBlurFilter::blur(const SkMask& src, SkMask* dst) const {
auto tmp = alloc.makeArrayDefault<uint8_t>(tmpW * tmpH);
- int srcStride = 1;
- int srcAlphaOffset = 0;
- if (src.fFormat == SkMask::kARGB32_Format) {
- srcStride = 4;
- srcAlphaOffset = SK_A32_SHIFT / 8;
- }
-
// Blur horizontally, and transpose.
const PlanGauss::Scan& scanW = planW.makeBlurScan(srcW, buffer);
for (int y = 0; y < srcH; y++) {
- auto srcStart = &src.fImage[y * src.fRowBytes] + srcAlphaOffset;
+ uint8_t* srcStart = &src.fImage[y * src.fRowBytes];
auto tmpStart = &tmp[y];
- scanW.blur(srcStart, srcStride, srcStart + srcW * srcStride,
- tmpStart, tmpW, tmpStart + tmpW * tmpH);
+ switch (src.fFormat) {
+ case SkMask::kBW_Format:
+ scanW.blur(AlphaIterA1(srcStart, 0), AlphaIterA1(srcStart + (srcW / 8), srcW % 8),
+ tmpStart, tmpW, tmpStart + tmpW * tmpH);
+ break;
+ case SkMask::kA8_Format:
+ scanW.blur(AlphaIterA8(srcStart), AlphaIterA8(srcStart + srcW),
+ tmpStart, tmpW, tmpStart + tmpW * tmpH);
+ break;
+ case SkMask::kARGB32_Format: {
+ const uint32_t* argbStart = reinterpret_cast<const uint32_t*>(srcStart);
+ scanW.blur(AlphaIterARGB(argbStart), AlphaIterARGB(argbStart + srcW),
+ tmpStart, tmpW, tmpStart + tmpW * tmpH);
+ } break;
+ case SkMask::kLCD16_Format: {
+ const uint16_t* lcdStart = reinterpret_cast<const uint16_t*>(srcStart);
+ scanW.blur(AlphaIterLCD(lcdStart), AlphaIterLCD(lcdStart + srcW),
+ tmpStart, tmpW, tmpStart + tmpW * tmpH);
+ } break;
+ default:
+ SK_ABORT("Unhandled format.");
+ }
}
// Blur vertically (scan in memory order because of the transposition),
@@ -956,7 +1096,7 @@ SkIPoint SkMaskBlurFilter::blur(const SkMask& src, SkMask* dst) const {
auto tmpStart = &tmp[y * tmpW];
auto dstStart = &dst->fImage[y];
- scanH.blur(tmpStart, 1, tmpStart + tmpW,
+ scanH.blur(tmpStart, tmpStart + tmpW,
dstStart, dst->fRowBytes, dstStart + dst->fRowBytes * dstH);
}