Just pass color glyph masks to filters.

Allow the filters to try to apply themselves to the color mask. Most
mask filters will just return false, but allow them the opprotunity.
This removes the behavior of trying to create a mask from the color
mask.

This updates the blur mask filter to handle kARGB32_Format directly by
using just the alpha (which mirrors current behavior).

Change-Id: I15eb736060ecf9b24aca874758c167b74d9ebc22
Reviewed-on: https://skia-review.googlesource.com/124185
Reviewed-by: Herb Derby <herb@google.com>
Commit-Queue: Ben Wagner <bungeman@google.com>
This commit is contained in:
Ben Wagner 2018-04-25 15:27:40 -04:00 committed by Skia Commit-Bot
parent 34a388edbd
commit 275df2e873
7 changed files with 110 additions and 175 deletions

View File

@ -97,8 +97,7 @@ void SkMask_FreeImage(uint8_t* image) {
bool SkBlurMask::BoxBlur(SkMask* dst, const SkMask& src, SkScalar sigma, SkBlurStyle style,
SkIPoint* margin) {
if (src.fFormat != SkMask::kA8_Format) {
if (src.fFormat != SkMask::kA8_Format && src.fFormat != SkMask::kARGB32_Format) {
return false;
}

View File

@ -17,43 +17,7 @@
static const double kPi = 3.14159265358979323846264338327950288;
class BlurScanInterface {
public:
virtual ~BlurScanInterface() = default;
virtual void blur(const uint8_t* src, int srcStride, const uint8_t* srcEnd,
uint8_t* dst, int dstStride, uint8_t* dstEnd) const = 0;
virtual bool canBlur4() { return false; }
virtual void blur4Transpose(
const uint8_t* src, int srcStride, const uint8_t* srcEnd,
uint8_t* dst, int dstStride, uint8_t* dstEnd) const {
SK_ABORT("This should not be called.");
}
};
class PlanningInterface {
public:
virtual ~PlanningInterface() = default;
virtual size_t bufferSize() const = 0;
virtual int border() const = 0;
virtual bool needsBlur() const = 0;
virtual BlurScanInterface* makeBlurScan(
SkArenaAlloc* alloc, int width, uint32_t* buffer) const = 0;
};
class None final : public PlanningInterface {
public:
None() = default;
size_t bufferSize() const override { return 0; }
int border() const override { return 0; }
bool needsBlur() const override { return false; }
BlurScanInterface* makeBlurScan(
SkArenaAlloc* alloc, int width, uint32_t* buffer) const override {
SK_ABORT("Should never be called.");
return nullptr;
}
};
class PlanGauss final : public PlanningInterface {
class PlanGauss final {
public:
explicit PlanGauss(double sigma) {
auto possibleWindow = static_cast<int>(floor(sigma * 3 * sqrt(2 * kPi) / 4 + 0.5));
@ -117,36 +81,17 @@ public:
fWeight = static_cast<uint64_t>(round(1.0 / divisor * (1ull << 32)));
}
size_t bufferSize() const override { return fPass0Size + fPass1Size + fPass2Size; }
size_t bufferSize() const { return fPass0Size + fPass1Size + fPass2Size; }
int border() const override { return fBorder; }
bool needsBlur() const override { return true; }
BlurScanInterface* makeBlurScan(
SkArenaAlloc* alloc, int width, uint32_t* buffer) const override
{
uint32_t* buffer0, *buffer0End, *buffer1, *buffer1End, *buffer2, *buffer2End;
buffer0 = buffer;
buffer0End = buffer1 = buffer0 + fPass0Size;
buffer1End = buffer2 = buffer1 + fPass1Size;
buffer2End = buffer2 + fPass2Size;
int noChangeCount = fSlidingWindow > width ? fSlidingWindow - width : 0;
return alloc->make<Gauss>(
fWeight, noChangeCount,
buffer0, buffer0End,
buffer1, buffer1End,
buffer2, buffer2End);
}
int border() const { return fBorder; }
public:
class Gauss final : public BlurScanInterface {
class Scan {
public:
Gauss(uint64_t weight, int noChangeCount,
uint32_t* buffer0, uint32_t* buffer0End,
uint32_t* buffer1, uint32_t* buffer1End,
uint32_t* buffer2, uint32_t* buffer2End)
Scan(uint64_t weight, int noChangeCount,
uint32_t* buffer0, uint32_t* buffer0End,
uint32_t* buffer1, uint32_t* buffer1End,
uint32_t* buffer2, uint32_t* buffer2End)
: fWeight{weight}
, fNoChangeCount{noChangeCount}
, fBuffer0{buffer0}
@ -158,7 +103,7 @@ public:
{ }
void blur(const uint8_t* src, int srcStride, const uint8_t* srcEnd,
uint8_t* dst, int dstStride, uint8_t* dstEnd) const override {
uint8_t* dst, int dstStride, uint8_t* dstEnd) const {
auto buffer0Cursor = fBuffer0;
auto buffer1Cursor = fBuffer1;
auto buffer2Cursor = fBuffer2;
@ -264,6 +209,21 @@ public:
uint32_t* fBuffer2End;
};
Scan makeBlurScan(int width, uint32_t* buffer) const {
uint32_t* buffer0, *buffer0End, *buffer1, *buffer1End, *buffer2, *buffer2End;
buffer0 = buffer;
buffer0End = buffer1 = buffer0 + fPass0Size;
buffer1End = buffer2 = buffer1 + fPass1Size;
buffer2End = buffer2 + fPass2Size;
int noChangeCount = fSlidingWindow > width ? fSlidingWindow - width : 0;
return Scan(
fWeight, noChangeCount,
buffer0, buffer0End,
buffer1, buffer1End,
buffer2, buffer2End);
}
uint64_t fWeight;
int fBorder;
int fSlidingWindow;
@ -325,12 +285,13 @@ static SkMask prepare_destination(int radiusX, int radiusY, const SkMask& src) {
static constexpr uint16_t _____ = 0u;
static constexpr uint16_t kHalf = 0x80u;
static SK_ALWAYS_INLINE Sk8h load(const uint8_t* from, int width) {
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) {
if (width < 8 || stride != 1) {
sk_bzero(buffer, sizeof(buffer));
for (int i = 0; i < width; i++) {
buffer[i] = from[i];
buffer[i] = from[i * stride];
}
from = buffer;
}
@ -580,7 +541,7 @@ 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), g0, g1, g2, g3, g4, &d0, &d8);
blur(load(src, 8, 1), g0, g1, g2, g3, g4, &d0, &d8);
store(dst, d0, 8);
@ -595,7 +556,7 @@ static SK_ALWAYS_INLINE void blur_row(
int srcTail = srcW - x;
if (srcTail > 0) {
blur(load(src, srcTail), g0, g1, g2, g3, g4, &d0, &d8);
blur(load(src, srcTail, 1), g0, g1, g2, g3, g4, &d0, &d8);
int dstTail = std::min(8, dstW - x);
store(dst, d0, dstTail);
@ -790,26 +751,26 @@ using BlurY = decltype(blur_y_radius_1);
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 srcStride, int srcH,
uint8_t* dst, size_t dstStride) {
const uint8_t* src, size_t srcRB, int srcStride, 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};
auto flush = [&](uint8_t* to, const Sk8h& v0, const Sk8h& v1) {
store(to, v0, width);
to += dstStride;
to += dstRB;
store(to, v1, width);
return to + dstStride;
return to + dstRB;
};
for (int y = 0; y < srcH; y += 1) {
auto s = load(src, width);
auto s = load(src, width, srcStride);
auto b = blur(s,
g0, g1, g2, g3, g4,
&d01, &d12, &d23, &d34, &d45, &d56, &d67, &d78);
store(dst, b, width);
src += srcStride;
dst += dstStride;
src += srcRB;
dst += dstRB;
}
if (radius >= 1) {
@ -829,8 +790,8 @@ 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(
BlurY blur, int radius, uint16_t *gauss,
const uint8_t *src, size_t srcStride, int srcW, int srcH,
uint8_t *dst, size_t dstStride) {
const uint8_t *src, size_t srcRB, int srcStride, int srcW, int srcH,
uint8_t *dst, size_t dstRB) {
Sk8h g0{gauss[0]},
g1{gauss[1]},
@ -842,9 +803,9 @@ static SK_ALWAYS_INLINE void blur_y_rect(
for (; x <= srcW - 8; x += 8) {
blur_column(blur, radius, 8,
g0, g1, g2, g3, g4,
src, srcStride, srcH,
dst, dstStride);
src += 8;
src, srcRB, srcStride, srcH,
dst, dstRB);
src += 8 * srcStride;
dst += 8;
}
@ -852,39 +813,39 @@ static SK_ALWAYS_INLINE void blur_y_rect(
if (xTail > 0) {
blur_column(blur, radius, xTail,
g0, g1, g2, g3, g4,
src, srcStride, srcH,
dst, dstStride);
src, srcRB, srcStride, srcH,
dst, dstRB);
}
}
SK_ATTRIBUTE(noinline) static void direct_blur_y(
int radius, uint16_t* gauss,
const uint8_t* src, size_t srcStride, int srcW, int srcH,
uint8_t* dst, size_t dstStride) {
const uint8_t* src, size_t srcRB, int srcStride, int srcW, int srcH,
uint8_t* dst, size_t dstRB) {
switch (radius) {
case 1:
blur_y_rect(blur_y_radius_1, 1, gauss,
src, srcStride, srcW, srcH,
dst, dstStride);
src, srcRB, srcStride, srcW, srcH,
dst, dstRB);
break;
case 2:
blur_y_rect(blur_y_radius_2, 2, gauss,
src, srcStride, srcW, srcH,
dst, dstStride);
src, srcRB, srcStride, srcW, srcH,
dst, dstRB);
break;
case 3:
blur_y_rect(blur_y_radius_3, 3, gauss,
src, srcStride, srcW, srcH,
dst, dstStride);
src, srcRB, srcStride, srcW, srcH,
dst, dstRB);
break;
case 4:
blur_y_rect(blur_y_radius_4, 4, gauss,
src, srcStride, srcW, srcH,
dst, dstStride);
src, srcRB, srcStride, srcW, srcH,
dst, dstRB);
break;
default:
@ -932,20 +893,27 @@ static SkIPoint small_blur(double sigmaX, double sigmaY, const SkMask& src, SkMa
int dstW = dst->fBounds.width(),
dstH = dst->fBounds.height();
size_t srcStride = src.fRowBytes,
dstStride = dst->fRowBytes;
size_t srcRB = src.fRowBytes,
dstRB = dst->fRowBytes;
//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, srcStride, srcW, srcH,
dst->fImage + radiusX, dstStride);
src.fImage + srcAlphaOffset, srcRB, srcStride, srcW, srcH,
dst->fImage + radiusX, dstRB);
// Blur horizontally in place.
direct_blur_x(radiusX, gaussFactorsX,
dst->fImage + radiusX, dstStride, srcW,
dst->fImage, dstStride, dstW, dstH);
dst->fImage + radiusX, dstRB, srcW,
dst->fImage, dstRB, dstW, dstH);
return {radiusX, radiusY};
}
@ -961,11 +929,11 @@ SkIPoint SkMaskBlurFilter::blur(const SkMask& src, SkMask* dst) const {
// 1024 is a place holder guess until more analysis can be done.
SkSTArenaAlloc<1024> alloc;
PlanningInterface* planW = alloc.make<PlanGauss>(fSigmaW);
PlanningInterface* planH = alloc.make<PlanGauss>(fSigmaH);
PlanGauss planW(fSigmaW);
PlanGauss planH(fSigmaH);
int borderW = planW->border(),
borderH = planH->border();
int borderW = planW.border(),
borderH = planH.border();
SkASSERT(borderH >= 0 && borderW >= 0);
*dst = prepare_destination(borderW, borderH, src);
@ -983,41 +951,40 @@ SkIPoint SkMaskBlurFilter::blur(const SkMask& src, SkMask* dst) const {
dstH = dst->fBounds.height();
SkASSERT(srcW >= 0 && srcH >= 0 && dstW >= 0 && dstH >= 0);
auto bufferSize = std::max(planW->bufferSize(), planH->bufferSize());
auto bufferSize = std::max(planW.bufferSize(), planH.bufferSize());
auto buffer = alloc.makeArrayDefault<uint32_t>(bufferSize);
if (planW->needsBlur() && planH->needsBlur()) {
// Blur both directions.
int tmpW = srcH,
tmpH = dstW;
// Blur both directions.
int tmpW = srcH,
tmpH = dstW;
auto tmp = alloc.makeArrayDefault<uint8_t>(tmpW * tmpH);
auto tmp = alloc.makeArrayDefault<uint8_t>(tmpW * tmpH);
// Blur horizontally, and transpose.
auto scanW = planW->makeBlurScan(&alloc, srcW, buffer);
for (int y = 0; y < srcH; y++) {
auto srcStart = &src.fImage[y * src.fRowBytes];
auto tmpStart = &tmp[y];
scanW->blur(srcStart, 1, srcStart + srcW,
tmpStart, tmpW, tmpStart + tmpW * tmpH);
}
int srcStride = 1;
int srcAlphaOffset = 0;
if (src.fFormat == SkMask::kARGB32_Format) {
srcStride = 4;
srcAlphaOffset = SK_A32_SHIFT / 8;
}
// Blur vertically (scan in memory order because of the transposition),
// and transpose back to the original orientation.
auto scanH = planH->makeBlurScan(&alloc, tmpW, buffer);
for (int y = 0; y < tmpH; y++) {
auto tmpStart = &tmp[y * tmpW];
auto dstStart = &dst->fImage[y];
// 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;
auto tmpStart = &tmp[y];
scanW.blur(srcStart, srcStride, srcStart + srcW * srcStride,
tmpStart, tmpW, tmpStart + tmpW * tmpH);
}
scanH->blur(tmpStart, 1, tmpStart + tmpW,
dstStart, dst->fRowBytes, dstStart + dst->fRowBytes * dstH);
}
} else {
// Copy to dst. No Blur.
SkASSERT(false); // should not get here
for (int y = 0; y < srcH; y++) {
std::memcpy(&dst->fImage[y * dst->fRowBytes], &src.fImage[y * src.fRowBytes], dstW);
}
// Blur vertically (scan in memory order because of the transposition),
// and transpose back to the original orientation.
const PlanGauss::Scan& scanH = planH.makeBlurScan(tmpW, buffer);
for (int y = 0; y < tmpH; y++) {
auto tmpStart = &tmp[y * tmpW];
auto dstStart = &dst->fImage[y];
scanH.blur(tmpStart, 1, tmpStart + tmpW,
dstStart, dst->fRowBytes, dstStart + dst->fRowBytes * dstH);
}
return {SkTo<int32_t>(borderW), SkTo<int32_t>(borderH)};

View File

@ -34,11 +34,6 @@ SkMaskFilterBase::NinePatch::~NinePatch() {
}
}
bool SkMaskFilterBase::filterMask(SkMask*, const SkMask&, const SkMatrix&,
SkIPoint*) const {
return false;
}
bool SkMaskFilterBase::asABlur(BlurRec*) const {
return false;
}

View File

@ -54,7 +54,7 @@ public:
@return true if the dst mask was correctly created.
*/
virtual bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&,
SkIPoint* margin) const;
SkIPoint* margin) const = 0;
#if SK_SUPPORT_GPU
/**

View File

@ -130,7 +130,7 @@ void SkScalerContext::getMetrics(SkGlyph* glyph) {
}
// If we are going to create the mask, then we cannot keep the color
if ((generatingImageFromPath || fMaskFilter) && SkMask::kARGB32_Format == glyph->fMaskFormat) {
if (generatingImageFromPath && SkMask::kARGB32_Format == glyph->fMaskFormat) {
glyph->fMaskFormat = SkMask::kA8_Format;
}
@ -382,26 +382,6 @@ static void generateMask(const SkMask& mask, const SkPath& path,
}
}
static void extract_alpha(const SkMask& dst,
const SkPMColor* srcRow, size_t srcRB) {
int width = dst.fBounds.width();
int height = dst.fBounds.height();
int dstRB = dst.fRowBytes;
uint8_t* dstRow = dst.fImage;
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
dstRow[x] = SkGetPackedA32(srcRow[x]);
}
// zero any padding on each row
for (int x = width; x < dstRB; ++x) {
dstRow[x] = 0;
}
dstRow += dstRB;
srcRow = (const SkPMColor*)((const char*)srcRow + srcRB);
}
}
void SkScalerContext::getImage(const SkGlyph& origGlyph) {
const SkGlyph* glyph = &origGlyph;
SkGlyph tmpGlyph;
@ -456,19 +436,7 @@ void SkScalerContext::getImage(const SkGlyph& origGlyph) {
// the src glyph image shouldn't be 3D
SkASSERT(SkMask::k3D_Format != glyph->fMaskFormat);
SkAutoSMalloc<32*32> a8storage;
glyph->toMask(&srcM);
if (SkMask::kARGB32_Format == srcM.fFormat) {
// now we need to extract the alpha-channel from the glyph's image
// and copy it into a temp buffer, and then point srcM at that temp.
srcM.fFormat = SkMask::kA8_Format;
srcM.fRowBytes = SkAlign4(srcM.fBounds.width());
size_t size = srcM.computeImageSize();
a8storage.reset(size);
srcM.fImage = (uint8_t*)a8storage.get();
extract_alpha(srcM,
(const SkPMColor*)glyph->fImage, glyph->rowBytes());
}
fRec.getMatrixFrom2x2(&matrix);
@ -496,7 +464,7 @@ void SkScalerContext::getImage(const SkGlyph& origGlyph) {
}
SkMask::FreeImage(dstM.fImage);
if (fPreBlendForFilter.isApplicable()) {
if (SkMask::kA8_Format == dstM.fFormat && fPreBlendForFilter.isApplicable()) {
applyLUTToA8Mask(srcM, fPreBlendForFilter.fG);
}
}

View File

@ -72,6 +72,10 @@ SkMask::Format SkEmbossMaskFilter::getFormat() const {
bool SkEmbossMaskFilter::filterMask(SkMask* dst, const SkMask& src,
const SkMatrix& matrix, SkIPoint* margin) const {
if (src.fFormat != SkMask::kA8_Format) {
return false;
}
SkScalar sigma = matrix.mapRadius(fBlurSigma);
if (!SkBlurMask::BoxBlur(dst, src, sigma, kInner_SkBlurStyle)) {

View File

@ -70,7 +70,9 @@ static void rect_memcpy(void* dst, size_t dstRB, const void* src, size_t srcRB,
bool SkShaderMF::filterMask(SkMask* dst, const SkMask& src, const SkMatrix& ctm,
SkIPoint* margin) const {
SkASSERT(src.fFormat == SkMask::kA8_Format);
if (src.fFormat != SkMask::kA8_Format) {
return false;
}
if (margin) {
margin->set(0, 0);