Use MaskFilter to create SDFs for text.

Easy way to store SDFs in the glyph cache.

Change-Id: Ia67e5c8619862bdee6aa3b293e30507d029e3bf1
Bug: skia:
Reviewed-on: https://skia-review.googlesource.com/123748
Reviewed-by: Ben Wagner <bungeman@google.com>
Commit-Queue: Jim Van Verth <jvanverth@google.com>
This commit is contained in:
Jim Van Verth 2018-05-03 10:40:30 -04:00 committed by Skia Commit-Bot
parent 9eeede2e71
commit d401da64f0
11 changed files with 207 additions and 132 deletions

View File

@ -408,6 +408,8 @@ skia_gpu_sources = [
"$_src/gpu/text/GrDistanceFieldAdjustTable.h",
"$_src/gpu/text/GrGlyphCache.cpp",
"$_src/gpu/text/GrGlyphCache.h",
"$_src/gpu/text/GrSDFMaskFilter.cpp",
"$_src/gpu/text/GrSDFMaskFilter.h",
"$_src/gpu/text/GrTextBlobCache.cpp",
"$_src/gpu/text/GrTextBlobCache.h",
"$_src/gpu/text/GrTextUtils.cpp",

View File

@ -41,6 +41,9 @@
#include "SkTypeface.h"
#include "SkView.h"
#include "SkXfermodeImageFilter.h"
#if SK_SUPPORT_GPU
#include "text/GrSDFMaskFilter.h"
#endif
#include <stdio.h>
#include <time.h>
@ -455,7 +458,11 @@ static sk_sp<SkPathEffect> make_path_effect(bool canBeNull = true) {
static sk_sp<SkMaskFilter> make_mask_filter() {
sk_sp<SkMaskFilter> maskFilter;
#if SK_SUPPORT_GPU
switch (R(4)) {
#else
switch (R(3)) {
#endif
case 0:
maskFilter = SkMaskFilter::MakeBlur(make_blur_style(), make_scalar(),
make_blur_mask_filter_respectctm());
@ -469,7 +476,13 @@ static sk_sp<SkMaskFilter> make_mask_filter() {
light.fSpecular = R(256);
maskFilter = SkEmbossMaskFilter::Make(make_scalar(), light);
}
#if SK_SUPPORT_GPU
case 2:
maskFilter = GrSDFMaskFilter::Make();
case 3:
#else
case 2:
#endif
default:
break;
}

View File

@ -42,6 +42,7 @@ static size_t format_alignment(SkMask::Format format) {
case SkMask::kBW_Format:
case SkMask::kA8_Format:
case SkMask::k3D_Format:
case SkMask::kSDF_Format:
return alignof(uint8_t);
case SkMask::kARGB32_Format:
return alignof(uint32_t);

View File

@ -51,6 +51,33 @@ void SkMask::FreeImage(void* image) {
sk_free(image);
}
SkMask SkMask::PrepareDestination(int radiusX, int radiusY, const SkMask& src) {
SkSafeMath safe;
SkMask dst;
// dstW = srcW + 2 * radiusX;
size_t dstW = safe.add(src.fBounds.width(), safe.add(radiusX, radiusX));
// dstH = srcH + 2 * radiusY;
size_t dstH = safe.add(src.fBounds.height(), safe.add(radiusY, radiusY));
dst.fBounds.set(0, 0, SkTo<int>(dstW), SkTo<int>(dstH));
dst.fBounds.offset(src.fBounds.x(), src.fBounds.y());
dst.fBounds.offset(-radiusX, -radiusY);
dst.fImage = nullptr;
dst.fRowBytes = SkTo<uint32_t>(dstW);
dst.fFormat = SkMask::kA8_Format;
size_t toAlloc = safe.mul(dstW, dstH);
if (safe && src.fImage != nullptr) {
dst.fImage = SkMask::AllocImage(toAlloc);
}
return dst;
}
///////////////////////////////////////////////////////////////////////////////
static const int gMaskFormatToShift[] = {
@ -59,6 +86,7 @@ static const int gMaskFormatToShift[] = {
0, // 3D
2, // ARGB32
1, // LCD16
0, // SDF
};
static int maskFormatToShift(SkMask::Format format) {

View File

@ -25,10 +25,11 @@ struct SkMask {
k3D_Format, //!< 3 8bit per pixl planes: alpha, mul, add
kARGB32_Format, //!< SkPMColor
kLCD16_Format, //!< 565 alpha for r/g/b
kSDF_Format, //!< 8bits representing signed distance field
};
enum {
kCountMaskFormats = kLCD16_Format + 1
kCountMaskFormats = kSDF_Format + 1
};
uint8_t* fImage;
@ -68,7 +69,7 @@ struct SkMask {
x,y are in the same coordiate space as fBounds.
*/
uint8_t* getAddr8(int x, int y) const {
SkASSERT(kA8_Format == fFormat);
SkASSERT(kA8_Format == fFormat || kSDF_Format == fFormat);
SkASSERT(fBounds.contains(x, y));
SkASSERT(fImage != nullptr);
return fImage + x - fBounds.fLeft + (y - fBounds.fTop) * fRowBytes;
@ -126,6 +127,11 @@ struct SkMask {
kJustRenderImage_CreateMode, //!< render into preallocate mask
kComputeBoundsAndRenderImage_CreateMode //!< compute bounds, alloc image and render into it
};
/**
* Returns initial destination mask data padded by radiusX and radiusY
*/
static SkMask PrepareDestination(int radiusX, int radiusY, const SkMask& src);
};
///////////////////////////////////////////////////////////////////////////////

View File

@ -10,7 +10,6 @@
#include "SkMalloc.h"
#include "SkMaskBlurFilter.h"
#include "SkNx.h"
#include "SkSafeMath.h"
#include <cmath>
#include <climits>
@ -256,32 +255,6 @@ bool SkMaskBlurFilter::hasNoBlur() const {
return (3 * fSigmaW <= 1) && (3 * fSigmaH <= 1);
}
static SkMask prepare_destination(int radiusX, int radiusY, const SkMask& src) {
SkSafeMath safe;
SkMask dst;
// dstW = srcW + 2 * radiusX;
size_t dstW = safe.add(src.fBounds.width(), safe.add(radiusX, radiusX));
// dstH = srcH + 2 * radiusY;
size_t dstH = safe.add(src.fBounds.height(), safe.add(radiusY, radiusY));
dst.fBounds.set(0, 0, SkTo<int>(dstW), SkTo<int>(dstH));
dst.fBounds.offset(src.fBounds.x(), src.fBounds.y());
dst.fBounds.offset(-radiusX, -radiusY);
dst.fImage = nullptr;
dst.fRowBytes = SkTo<uint32_t>(dstW);
dst.fFormat = SkMask::kA8_Format;
size_t toAlloc = safe.mul(dstW, dstH);
if (safe && src.fImage != nullptr) {
dst.fImage = SkMask::AllocImage(toAlloc);
}
return dst;
}
static constexpr uint16_t _____ = 0u;
static constexpr uint16_t kHalf = 0x80u;
@ -878,7 +851,7 @@ static SkIPoint small_blur(double sigmaX, double sigmaY, const SkMask& src, SkMa
prepareGauss(filterX, gaussFactorsX);
prepareGauss(filterY, gaussFactorsY);
*dst = prepare_destination(radiusX, radiusY, src);
*dst = SkMask::PrepareDestination(radiusX, radiusY, src);
if (src.fImage == nullptr) {
return {SkTo<int32_t>(radiusX), SkTo<int32_t>(radiusY)};
}
@ -936,7 +909,7 @@ SkIPoint SkMaskBlurFilter::blur(const SkMask& src, SkMask* dst) const {
borderH = planH.border();
SkASSERT(borderH >= 0 && borderW >= 0);
*dst = prepare_destination(borderW, borderH, src);
*dst = SkMask::PrepareDestination(borderW, borderH, src);
if (src.fImage == nullptr) {
return {SkTo<int32_t>(borderW), SkTo<int32_t>(borderH)};
}

View File

@ -23,6 +23,7 @@
#include "GrTextureProxy.h"
#include "GrFragmentProcessor.h"
#include "effects/GrXfermodeFragmentProcessor.h"
#include "text/GrSDFMaskFilter.h"
#endif
SkMaskFilterBase::NinePatch::~NinePatch() {
@ -734,5 +735,7 @@ void SkMaskFilter::InitializeFlattenables() {
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkComposeMF)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkCombineMF)
sk_register_blur_maskfilter_createproc();
#if SK_SUPPORT_GPU
gr_register_sdf_maskfilter_createproc();
#endif
}

View File

@ -7,6 +7,7 @@
#include "GrAtlasTextContext.h"
#include "GrContext.h"
#include "GrContextPriv.h"
#include "GrSDFMaskFilter.h"
#include "GrTextBlobCache.h"
#include "SkDistanceFieldGen.h"
#include "SkDraw.h"
@ -661,6 +662,8 @@ void GrAtlasTextContext::initDistanceFieldPaint(GrAtlasTextBlob* blob,
skPaint->setAutohinted(false);
skPaint->setHinting(SkPaint::kNormal_Hinting);
skPaint->setSubpixelText(true);
skPaint->setMaskFilter(GrSDFMaskFilter::Make());
}
void GrAtlasTextContext::drawDFText(GrAtlasTextBlob* blob, int runIndex,
@ -793,13 +796,11 @@ void GrAtlasTextContext::drawDFPosText(GrAtlasTextBlob* blob, int runIndex,
glyphPos.fY += (2 == scalarsPerPosition ? pos[1] : 0) -
SkFloatToScalar(glyph.fAdvanceY) * alignMul * textRatio;
if (glyph.fMaskFormat == SkMask::kA8_Format ||
glyph.fMaskFormat == SkMask::kBW_Format)
{
if (glyph.fMaskFormat == SkMask::kSDF_Format) {
DfAppendGlyph(blob, runIndex, glyphCache, &currStrike, glyph, glyphPos.fX,
glyphPos.fY, paint.filteredPremulColor(), cache.get(), textRatio);
} else {
// can't append color glyph to SDF batch, send to fallback
// can't append non-SDF glyph to SDF batch, send to fallback
fallbackTextHelper.appendText(glyph, SkToInt(text - lastText), lastText,
glyphPos);
}
@ -877,6 +878,11 @@ void GrAtlasTextContext::FallbackTextHelper::drawText(GrAtlasTextBlob* blob, int
const GrTextUtils::Paint& paint,
SkScalerContextFlags scalerContextFlags) {
if (fFallbackTxt.count()) {
if (fViewMatrix.hasPerspective()) {
// TODO: handle perspective
return;
}
blob->initOverride(runIndex);
blob->setHasBitmap();
SkExclusiveStrikePtr cache;

View File

@ -62,7 +62,8 @@ static inline GrMaskFormat get_packed_glyph_mask_format(const SkGlyph& glyph) {
SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);
switch (format) {
case SkMask::kBW_Format:
// fall through to kA8 -- we store BW glyphs in our 8-bit cache
case SkMask::kSDF_Format:
// fall through to kA8 -- we store BW and SDF glyphs in our 8-bit cache
case SkMask::kA8_Format:
return kA8_GrMaskFormat;
case SkMask::k3D_Format:
@ -89,19 +90,6 @@ static inline bool get_packed_glyph_bounds(SkGlyphCache* cache, const SkGlyph& g
return true;
}
static inline bool get_packed_glyph_df_bounds(SkGlyphCache* cache, const SkGlyph& glyph,
SkIRect* bounds) {
#if 1
// crbug:510931
// Retrieving the image from the cache can actually change the mask format.
cache->findImage(glyph);
#endif
bounds->setXYWH(glyph.fLeft, glyph.fTop, glyph.fWidth, glyph.fHeight);
bounds->outset(SK_DistanceFieldPad, SK_DistanceFieldPad);
return true;
}
// expands each bit in a bitmask to 0 or ~0 of type INT_TYPE. Used to expand a BW glyph mask to
// A8, RGB565, or RGBA8888.
template <typename INT_TYPE>
@ -182,64 +170,6 @@ static bool get_packed_glyph_image(SkGlyphCache* cache, const SkGlyph& glyph, in
return true;
}
static bool get_packed_glyph_df_image(SkGlyphCache* cache, const SkGlyph& glyph,
int width, int height, void* dst) {
SkASSERT(glyph.fWidth + 2*SK_DistanceFieldPad == width);
SkASSERT(glyph.fHeight + 2*SK_DistanceFieldPad == height);
#ifndef SK_USE_LEGACY_DISTANCE_FIELDS
const SkPath* path = cache->findPath(glyph);
if (nullptr == path) {
return false;
}
SkDEBUGCODE(SkRect glyphBounds = SkRect::MakeXYWH(glyph.fLeft,
glyph.fTop,
glyph.fWidth,
glyph.fHeight));
SkASSERT(glyphBounds.contains(path->getBounds()));
// now generate the distance field
SkASSERT(dst);
SkMatrix drawMatrix;
drawMatrix.setTranslate((SkScalar)-glyph.fLeft, (SkScalar)-glyph.fTop);
// Generate signed distance field directly from SkPath
bool succeed = GrGenerateDistanceFieldFromPath((unsigned char*)dst,
*path, drawMatrix,
width, height, width * sizeof(unsigned char));
if (!succeed) {
#endif
const void* image = cache->findImage(glyph);
if (nullptr == image) {
return false;
}
// now generate the distance field
SkASSERT(dst);
SkMask::Format maskFormat = static_cast<SkMask::Format>(glyph.fMaskFormat);
if (SkMask::kA8_Format == maskFormat) {
// make the distance field from the image
SkGenerateDistanceFieldFromA8Image((unsigned char*)dst,
(unsigned char*)image,
glyph.fWidth, glyph.fHeight,
glyph.rowBytes());
} else if (SkMask::kBW_Format == maskFormat) {
// make the distance field from the image
SkGenerateDistanceFieldFromBWImage((unsigned char*)dst,
(unsigned char*)image,
glyph.fWidth, glyph.fHeight,
glyph.rowBytes());
} else {
return false;
}
#ifndef SK_USE_LEGACY_DISTANCE_FIELDS
}
#endif
return true;
}
///////////////////////////////////////////////////////////////////////////////
/*
@ -267,14 +197,8 @@ GrTextStrike::~GrTextStrike() {
GrGlyph* GrTextStrike::generateGlyph(const SkGlyph& skGlyph, GrGlyph::PackedID packed,
SkGlyphCache* cache) {
SkIRect bounds;
if (GrGlyph::kDistance_MaskStyle == GrGlyph::UnpackMaskStyle(packed)) {
if (!get_packed_glyph_df_bounds(cache, skGlyph, &bounds)) {
return nullptr;
}
} else {
if (!get_packed_glyph_bounds(cache, skGlyph, &bounds)) {
return nullptr;
}
if (!get_packed_glyph_bounds(cache, skGlyph, &bounds)) {
return nullptr;
}
GrMaskFormat format = get_packed_glyph_mask_format(skGlyph);
@ -327,22 +251,15 @@ GrDrawOpAtlas::ErrorCode GrTextStrike::addGlyphToAtlas(
SkAutoSMalloc<1024> storage(size);
const SkGlyph& skGlyph = GrToSkGlyph(cache, glyph->fPackedID);
if (isSDFGlyph) {
if (!get_packed_glyph_df_image(cache, skGlyph, width, height,
storage.get())) {
return GrDrawOpAtlas::ErrorCode::kError;
}
} else {
void* dataPtr = storage.get();
if (addPad) {
sk_bzero(dataPtr, size);
dataPtr = (char*)(dataPtr) + rowBytes + bytesPerPixel;
}
if (!get_packed_glyph_image(cache, skGlyph, glyph->width(), glyph->height(),
rowBytes, expectedMaskFormat,
dataPtr)) {
return GrDrawOpAtlas::ErrorCode::kError;
}
void* dataPtr = storage.get();
if (addPad) {
sk_bzero(dataPtr, size);
dataPtr = (char*)(dataPtr) + rowBytes + bytesPerPixel;
}
if (!get_packed_glyph_image(cache, skGlyph, glyph->width(), glyph->height(),
rowBytes, expectedMaskFormat,
dataPtr)) {
return GrDrawOpAtlas::ErrorCode::kError;
}
GrDrawOpAtlas::ErrorCode result = fullAtlasManager->addToAtlas(

View File

@ -0,0 +1,102 @@
/*
* Copyright 2018 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "GrSDFMaskFilter.h"
#include "SkDistanceFieldGen.h"
#include "SkMaskFilterBase.h"
#include "SkReadBuffer.h"
#include "SkSafeMath.h"
#include "SkWriteBuffer.h"
#include "SkString.h"
class SK_API GrSDFMaskFilterImpl : public SkMaskFilterBase {
public:
GrSDFMaskFilterImpl();
// overrides from SkMaskFilterBase
// This method is not exported to java.
SkMask::Format getFormat() const override;
// This method is not exported to java.
bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&,
SkIPoint* margin) const override;
void computeFastBounds(const SkRect&, SkRect*) const override;
void toString(SkString* str) const override;
SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(GrSDFMaskFilterImpl)
protected:
private:
typedef SkMaskFilter INHERITED;
friend void gr_register_sdf_maskfilter_createproc();
};
///////////////////////////////////////////////////////////////////////////////
GrSDFMaskFilterImpl::GrSDFMaskFilterImpl() {}
SkMask::Format GrSDFMaskFilterImpl::getFormat() const {
return SkMask::kSDF_Format;
}
bool GrSDFMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src,
const SkMatrix& matrix, SkIPoint* margin) const {
if (src.fFormat != SkMask::kA8_Format && src.fFormat != SkMask::kBW_Format) {
return false;
}
*dst = SkMask::PrepareDestination(SK_DistanceFieldPad, SK_DistanceFieldPad, src);
dst->fFormat = SkMask::kSDF_Format;
if (margin) {
margin->set(SK_DistanceFieldPad, SK_DistanceFieldPad);
}
if (src.fImage == nullptr) {
return true;
}
if (dst->fImage == nullptr) {
dst->fBounds.setEmpty();
return false;
}
if (src.fFormat == SkMask::kA8_Format) {
return SkGenerateDistanceFieldFromA8Image(dst->fImage, src.fImage,
src.fBounds.width(), src.fBounds.height(),
src.fRowBytes);
} else {
return SkGenerateDistanceFieldFromBWImage(dst->fImage, src.fImage,
src.fBounds.width(), src.fBounds.height(),
src.fRowBytes);
}
}
void GrSDFMaskFilterImpl::computeFastBounds(const SkRect& src,
SkRect* dst) const {
dst->set(src.fLeft - SK_DistanceFieldPad, src.fTop - SK_DistanceFieldPad,
src.fRight + SK_DistanceFieldPad, src.fBottom + SK_DistanceFieldPad);
}
sk_sp<SkFlattenable> GrSDFMaskFilterImpl::CreateProc(SkReadBuffer& buffer) {
return GrSDFMaskFilter::Make();
}
void GrSDFMaskFilterImpl::toString(SkString* str) const {
str->append("GrSDFMaskFilterImpl: ()");
}
void gr_register_sdf_maskfilter_createproc() {
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(GrSDFMaskFilterImpl)
}
///////////////////////////////////////////////////////////////////////////////
sk_sp<SkMaskFilter> GrSDFMaskFilter::Make() {
return sk_sp<SkMaskFilter>(new GrSDFMaskFilterImpl());
}

View File

@ -0,0 +1,24 @@
/*
* Copyright 2018 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrSDFMaskFilter_DEFINED
#define GrSDFMaskFilter_DEFINED
#include "SkMaskFilter.h"
/** \class GrSDFMaskFilter
This mask filter converts an alpha mask to a signed distance field representation
*/
class SK_API GrSDFMaskFilter : public SkMaskFilter {
public:
static sk_sp<SkMaskFilter> Make();
};
extern void gr_register_sdf_maskfilter_createproc();
#endif