Move bmp/mask/sdf functionality from GrTextUtils to GrAtlasTextContext
Change-Id: I3924f2f9fd2860749f0404030d0be502a8153faa Reviewed-on: https://skia-review.googlesource.com/68211 Reviewed-by: Jim Van Verth <jvanverth@google.com> Commit-Queue: Brian Salomon <bsalomon@google.com>
This commit is contained in:
parent
f90a2e6f74
commit
52db9400d7
@ -7,8 +7,10 @@
|
||||
#include "GrAtlasTextContext.h"
|
||||
#include "GrContext.h"
|
||||
#include "GrTextBlobCache.h"
|
||||
#include "SkDistanceFieldGen.h"
|
||||
#include "SkDraw.h"
|
||||
#include "SkDrawFilter.h"
|
||||
#include "SkFindAndPlaceGlyph.h"
|
||||
#include "SkGr.h"
|
||||
#include "ops/GrMeshDrawOp.h"
|
||||
|
||||
@ -24,7 +26,7 @@ bool GrAtlasTextContext::canDraw(const SkPaint& skPaint,
|
||||
const SkMatrix& viewMatrix,
|
||||
const SkSurfaceProps& props,
|
||||
const GrShaderCaps& shaderCaps) {
|
||||
return GrTextUtils::CanDrawAsDistanceFields(skPaint, viewMatrix, props, shaderCaps) ||
|
||||
return CanDrawAsDistanceFields(skPaint, viewMatrix, props, shaderCaps) ||
|
||||
!SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix);
|
||||
}
|
||||
|
||||
@ -178,27 +180,26 @@ void GrAtlasTextContext::RegenerateTextBlob(GrAtlasTextBlob* cacheBlob,
|
||||
if (!runPaint.modifyForRun(it)) {
|
||||
continue;
|
||||
}
|
||||
if (GrTextUtils::CanDrawAsDistanceFields(runPaint, viewMatrix, props, shaderCaps)) {
|
||||
if (CanDrawAsDistanceFields(runPaint, viewMatrix, props, shaderCaps)) {
|
||||
switch (it.positioning()) {
|
||||
case SkTextBlob::kDefault_Positioning: {
|
||||
GrTextUtils::DrawDFText(cacheBlob, run, fontCache, props, runPaint,
|
||||
scalerContextFlags, viewMatrix,
|
||||
(const char*)it.glyphs(), textLen, x + offset.x(),
|
||||
DrawDFText(cacheBlob, run, fontCache, props, runPaint, scalerContextFlags,
|
||||
viewMatrix, (const char*)it.glyphs(), textLen, x + offset.x(),
|
||||
y + offset.y());
|
||||
break;
|
||||
}
|
||||
case SkTextBlob::kHorizontal_Positioning: {
|
||||
SkPoint dfOffset = SkPoint::Make(x, y + offset.y());
|
||||
GrTextUtils::DrawDFPosText(
|
||||
cacheBlob, run, fontCache, props, runPaint, scalerContextFlags,
|
||||
viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 1, dfOffset);
|
||||
DrawDFPosText(cacheBlob, run, fontCache, props, runPaint, scalerContextFlags,
|
||||
viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 1,
|
||||
dfOffset);
|
||||
break;
|
||||
}
|
||||
case SkTextBlob::kFull_Positioning: {
|
||||
SkPoint dfOffset = SkPoint::Make(x, y);
|
||||
GrTextUtils::DrawDFPosText(
|
||||
cacheBlob, run, fontCache, props, runPaint, scalerContextFlags,
|
||||
viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 2, dfOffset);
|
||||
DrawDFPosText(cacheBlob, run, fontCache, props, runPaint, scalerContextFlags,
|
||||
viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 2,
|
||||
dfOffset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -207,21 +208,18 @@ void GrAtlasTextContext::RegenerateTextBlob(GrAtlasTextBlob* cacheBlob,
|
||||
} else {
|
||||
switch (it.positioning()) {
|
||||
case SkTextBlob::kDefault_Positioning:
|
||||
GrTextUtils::DrawBmpText(cacheBlob, run, fontCache, props, runPaint,
|
||||
scalerContextFlags, viewMatrix,
|
||||
(const char*)it.glyphs(), textLen, x + offset.x(),
|
||||
DrawBmpText(cacheBlob, run, fontCache, props, runPaint, scalerContextFlags,
|
||||
viewMatrix, (const char*)it.glyphs(), textLen, x + offset.x(),
|
||||
y + offset.y());
|
||||
break;
|
||||
case SkTextBlob::kHorizontal_Positioning:
|
||||
GrTextUtils::DrawBmpPosText(cacheBlob, run, fontCache, props, runPaint,
|
||||
scalerContextFlags, viewMatrix,
|
||||
(const char*)it.glyphs(), textLen, it.pos(), 1,
|
||||
DrawBmpPosText(cacheBlob, run, fontCache, props, runPaint, scalerContextFlags,
|
||||
viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 1,
|
||||
SkPoint::Make(x, y + offset.y()));
|
||||
break;
|
||||
case SkTextBlob::kFull_Positioning:
|
||||
GrTextUtils::DrawBmpPosText(cacheBlob, run, fontCache, props, runPaint,
|
||||
scalerContextFlags, viewMatrix,
|
||||
(const char*)it.glyphs(), textLen, it.pos(), 2,
|
||||
DrawBmpPosText(cacheBlob, run, fontCache, props, runPaint, scalerContextFlags,
|
||||
viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 2,
|
||||
SkPoint::Make(x, y));
|
||||
break;
|
||||
}
|
||||
@ -246,12 +244,12 @@ GrAtlasTextContext::MakeDrawTextBlob(GrTextBlobCache* blobCache,
|
||||
sk_sp<GrAtlasTextBlob> blob = blobCache->makeBlob(glyphCount, 1);
|
||||
blob->initThrowawayBlob(viewMatrix, x, y);
|
||||
|
||||
if (GrTextUtils::CanDrawAsDistanceFields(paint, viewMatrix, props, shaderCaps)) {
|
||||
GrTextUtils::DrawDFText(blob.get(), 0, fontCache, props, paint, scalerContextFlags,
|
||||
viewMatrix, text, byteLength, x, y);
|
||||
if (CanDrawAsDistanceFields(paint, viewMatrix, props, shaderCaps)) {
|
||||
DrawDFText(blob.get(), 0, fontCache, props, paint, scalerContextFlags, viewMatrix, text,
|
||||
byteLength, x, y);
|
||||
} else {
|
||||
GrTextUtils::DrawBmpText(blob.get(), 0, fontCache, props, paint, scalerContextFlags,
|
||||
viewMatrix, text, byteLength, x, y);
|
||||
DrawBmpText(blob.get(), 0, fontCache, props, paint, scalerContextFlags, viewMatrix, text,
|
||||
byteLength, x, y);
|
||||
}
|
||||
return blob;
|
||||
}
|
||||
@ -275,12 +273,12 @@ GrAtlasTextContext::MakeDrawPosTextBlob(GrTextBlobCache* blobCache,
|
||||
sk_sp<GrAtlasTextBlob> blob = blobCache->makeBlob(glyphCount, 1);
|
||||
blob->initThrowawayBlob(viewMatrix, offset.x(), offset.y());
|
||||
|
||||
if (GrTextUtils::CanDrawAsDistanceFields(paint, viewMatrix, props, shaderCaps)) {
|
||||
GrTextUtils::DrawDFPosText(blob.get(), 0, fontCache, props, paint, scalerContextFlags,
|
||||
viewMatrix, text, byteLength, pos, scalarsPerPosition, offset);
|
||||
if (CanDrawAsDistanceFields(paint, viewMatrix, props, shaderCaps)) {
|
||||
DrawDFPosText(blob.get(), 0, fontCache, props, paint, scalerContextFlags, viewMatrix, text,
|
||||
byteLength, pos, scalarsPerPosition, offset);
|
||||
} else {
|
||||
GrTextUtils::DrawBmpPosText(blob.get(), 0, fontCache, props, paint, scalerContextFlags,
|
||||
viewMatrix, text, byteLength, pos, scalarsPerPosition, offset);
|
||||
DrawBmpPosText(blob.get(), 0, fontCache, props, paint, scalerContextFlags, viewMatrix, text,
|
||||
byteLength, pos, scalarsPerPosition, offset);
|
||||
}
|
||||
return blob;
|
||||
}
|
||||
@ -339,6 +337,425 @@ void GrAtlasTextContext::drawPosText(GrContext* context, GrTextUtils::Target* ta
|
||||
byteLength, pos, scalarsPerPosition, offset, regionClipBounds);
|
||||
}
|
||||
|
||||
void GrAtlasTextContext::DrawBmpText(GrAtlasTextBlob* blob, int runIndex,
|
||||
GrAtlasGlyphCache* fontCache, const SkSurfaceProps& props,
|
||||
const GrTextUtils::Paint& paint, uint32_t scalerContextFlags,
|
||||
const SkMatrix& viewMatrix, const char text[],
|
||||
size_t byteLength, SkScalar x, SkScalar y) {
|
||||
SkASSERT(byteLength == 0 || text != nullptr);
|
||||
|
||||
// nothing to draw
|
||||
if (text == nullptr || byteLength == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure the blob is set for bitmaptext
|
||||
blob->setHasBitmap();
|
||||
|
||||
GrAtlasTextStrike* currStrike = nullptr;
|
||||
|
||||
SkGlyphCache* cache = blob->setupCache(runIndex, props, scalerContextFlags, paint, &viewMatrix);
|
||||
SkFindAndPlaceGlyph::ProcessText(paint.skPaint().getTextEncoding(), text, byteLength, {x, y},
|
||||
viewMatrix, paint.skPaint().getTextAlign(), cache,
|
||||
[&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
|
||||
position += rounding;
|
||||
BmpAppendGlyph(blob, runIndex, fontCache, &currStrike,
|
||||
glyph, SkScalarFloorToInt(position.fX),
|
||||
SkScalarFloorToInt(position.fY),
|
||||
paint.filteredPremulColor(), cache);
|
||||
});
|
||||
|
||||
SkGlyphCache::AttachCache(cache);
|
||||
}
|
||||
|
||||
void GrAtlasTextContext::DrawBmpPosText(GrAtlasTextBlob* blob, int runIndex,
|
||||
GrAtlasGlyphCache* fontCache, const SkSurfaceProps& props,
|
||||
const GrTextUtils::Paint& paint,
|
||||
uint32_t scalerContextFlags, const SkMatrix& viewMatrix,
|
||||
const char text[], size_t byteLength, const SkScalar pos[],
|
||||
int scalarsPerPosition, const SkPoint& offset) {
|
||||
SkASSERT(byteLength == 0 || text != nullptr);
|
||||
SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
|
||||
|
||||
// nothing to draw
|
||||
if (text == nullptr || byteLength == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure the blob is set for bitmaptext
|
||||
blob->setHasBitmap();
|
||||
|
||||
GrAtlasTextStrike* currStrike = nullptr;
|
||||
|
||||
SkGlyphCache* cache = blob->setupCache(runIndex, props, scalerContextFlags, paint, &viewMatrix);
|
||||
|
||||
SkFindAndPlaceGlyph::ProcessPosText(
|
||||
paint.skPaint().getTextEncoding(), text, byteLength, offset, viewMatrix, pos,
|
||||
scalarsPerPosition, paint.skPaint().getTextAlign(), cache,
|
||||
[&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
|
||||
position += rounding;
|
||||
BmpAppendGlyph(blob, runIndex, fontCache, &currStrike, glyph,
|
||||
SkScalarFloorToInt(position.fX), SkScalarFloorToInt(position.fY),
|
||||
paint.filteredPremulColor(), cache);
|
||||
});
|
||||
|
||||
SkGlyphCache::AttachCache(cache);
|
||||
}
|
||||
|
||||
void GrAtlasTextContext::BmpAppendGlyph(GrAtlasTextBlob* blob, int runIndex,
|
||||
GrAtlasGlyphCache* fontCache, GrAtlasTextStrike** strike,
|
||||
const SkGlyph& skGlyph, int vx, int vy, GrColor color,
|
||||
SkGlyphCache* cache) {
|
||||
if (!*strike) {
|
||||
*strike = fontCache->getStrike(cache);
|
||||
}
|
||||
|
||||
GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
|
||||
skGlyph.getSubXFixed(),
|
||||
skGlyph.getSubYFixed(),
|
||||
GrGlyph::kCoverage_MaskStyle);
|
||||
GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, cache);
|
||||
if (!glyph) {
|
||||
return;
|
||||
}
|
||||
|
||||
int x = vx + glyph->fBounds.fLeft;
|
||||
int y = vy + glyph->fBounds.fTop;
|
||||
|
||||
int width = glyph->fBounds.width();
|
||||
int height = glyph->fBounds.height();
|
||||
|
||||
SkRect r;
|
||||
r.fLeft = SkIntToScalar(x);
|
||||
r.fTop = SkIntToScalar(y);
|
||||
r.fRight = r.fLeft + SkIntToScalar(width);
|
||||
r.fBottom = r.fTop + SkIntToScalar(height);
|
||||
|
||||
blob->appendGlyph(runIndex, r, color, *strike, glyph, cache, skGlyph, SkIntToScalar(vx),
|
||||
SkIntToScalar(vy), 1.0f, true);
|
||||
}
|
||||
|
||||
static const int kMinDFFontSize = 18;
|
||||
static const int kSmallDFFontSize = 32;
|
||||
static const int kSmallDFFontLimit = 32;
|
||||
static const int kMediumDFFontSize = 72;
|
||||
static const int kMediumDFFontLimit = 72;
|
||||
static const int kLargeDFFontSize = 162;
|
||||
#ifdef SK_BUILD_FOR_ANDROID
|
||||
static const int kLargeDFFontLimit = 384;
|
||||
#else
|
||||
static const int kLargeDFFontLimit = 2 * kLargeDFFontSize;
|
||||
#endif
|
||||
|
||||
bool GrAtlasTextContext::CanDrawAsDistanceFields(const SkPaint& skPaint, const SkMatrix& viewMatrix,
|
||||
const SkSurfaceProps& props,
|
||||
const GrShaderCaps& caps) {
|
||||
if (!viewMatrix.hasPerspective()) {
|
||||
SkScalar maxScale = viewMatrix.getMaxScale();
|
||||
SkScalar scaledTextSize = maxScale * skPaint.getTextSize();
|
||||
// Hinted text looks far better at small resolutions
|
||||
// Scaling up beyond 2x yields undesireable artifacts
|
||||
if (scaledTextSize < kMinDFFontSize || scaledTextSize > kLargeDFFontLimit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool useDFT = props.isUseDeviceIndependentFonts();
|
||||
#if SK_FORCE_DISTANCE_FIELD_TEXT
|
||||
useDFT = true;
|
||||
#endif
|
||||
|
||||
if (!useDFT && scaledTextSize < kLargeDFFontSize) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// rasterizers and mask filters modify alpha, which doesn't
|
||||
// translate well to distance
|
||||
if (skPaint.getRasterizer() || skPaint.getMaskFilter() || !caps.shaderDerivativeSupport()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: add some stroking support
|
||||
if (skPaint.getStyle() != SkPaint::kFill_Style) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GrAtlasTextContext::InitDistanceFieldPaint(GrAtlasTextBlob* blob,
|
||||
SkPaint* skPaint,
|
||||
SkScalar* textRatio,
|
||||
const SkMatrix& viewMatrix) {
|
||||
SkScalar textSize = skPaint->getTextSize();
|
||||
SkScalar scaledTextSize = textSize;
|
||||
|
||||
if (viewMatrix.hasPerspective()) {
|
||||
// for perspective, we simply force to the medium size
|
||||
// TODO: compute a size based on approximate screen area
|
||||
scaledTextSize = kMediumDFFontLimit;
|
||||
} else {
|
||||
SkScalar maxScale = viewMatrix.getMaxScale();
|
||||
// if we have non-unity scale, we need to choose our base text size
|
||||
// based on the SkPaint's text size multiplied by the max scale factor
|
||||
// TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
|
||||
if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
|
||||
scaledTextSize *= maxScale;
|
||||
}
|
||||
}
|
||||
|
||||
// We have three sizes of distance field text, and within each size 'bucket' there is a floor
|
||||
// and ceiling. A scale outside of this range would require regenerating the distance fields
|
||||
SkScalar dfMaskScaleFloor;
|
||||
SkScalar dfMaskScaleCeil;
|
||||
if (scaledTextSize <= kSmallDFFontLimit) {
|
||||
dfMaskScaleFloor = kMinDFFontSize;
|
||||
dfMaskScaleCeil = kSmallDFFontLimit;
|
||||
*textRatio = textSize / kSmallDFFontSize;
|
||||
skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize));
|
||||
} else if (scaledTextSize <= kMediumDFFontLimit) {
|
||||
dfMaskScaleFloor = kSmallDFFontLimit;
|
||||
dfMaskScaleCeil = kMediumDFFontLimit;
|
||||
*textRatio = textSize / kMediumDFFontSize;
|
||||
skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize));
|
||||
} else {
|
||||
dfMaskScaleFloor = kMediumDFFontLimit;
|
||||
dfMaskScaleCeil = kLargeDFFontLimit;
|
||||
*textRatio = textSize / kLargeDFFontSize;
|
||||
skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize));
|
||||
}
|
||||
|
||||
// Because there can be multiple runs in the blob, we want the overall maxMinScale, and
|
||||
// minMaxScale to make regeneration decisions. Specifically, we want the maximum minimum scale
|
||||
// we can tolerate before we'd drop to a lower mip size, and the minimum maximum scale we can
|
||||
// tolerate before we'd have to move to a large mip size. When we actually test these values
|
||||
// we look at the delta in scale between the new viewmatrix and the old viewmatrix, and test
|
||||
// against these values to decide if we can reuse or not(ie, will a given scale change our mip
|
||||
// level)
|
||||
SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil);
|
||||
blob->setMinAndMaxScale(dfMaskScaleFloor / scaledTextSize, dfMaskScaleCeil / scaledTextSize);
|
||||
|
||||
skPaint->setAntiAlias(true);
|
||||
skPaint->setLCDRenderText(false);
|
||||
skPaint->setAutohinted(false);
|
||||
skPaint->setHinting(SkPaint::kNormal_Hinting);
|
||||
skPaint->setSubpixelText(true);
|
||||
}
|
||||
|
||||
void GrAtlasTextContext::DrawDFText(GrAtlasTextBlob* blob, int runIndex,
|
||||
GrAtlasGlyphCache* fontCache, const SkSurfaceProps& props,
|
||||
const GrTextUtils::Paint& paint, uint32_t scalerContextFlags,
|
||||
const SkMatrix& viewMatrix, const char text[],
|
||||
size_t byteLength, SkScalar x, SkScalar y) {
|
||||
SkASSERT(byteLength == 0 || text != nullptr);
|
||||
|
||||
// nothing to draw
|
||||
if (text == nullptr || byteLength == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const SkPaint& skPaint = paint.skPaint();
|
||||
SkPaint::GlyphCacheProc glyphCacheProc =
|
||||
SkPaint::GetGlyphCacheProc(skPaint.getTextEncoding(), skPaint.isDevKernText(), true);
|
||||
SkAutoDescriptor desc;
|
||||
SkScalerContextEffects effects;
|
||||
// We apply the fake-gamma by altering the distance in the shader, so we ignore the
|
||||
// passed-in scaler context flags. (It's only used when we fall-back to bitmap text).
|
||||
skPaint.getScalerContextDescriptor(&effects, &desc, props, SkPaint::kNone_ScalerContextFlags,
|
||||
nullptr);
|
||||
SkGlyphCache* origPaintCache =
|
||||
SkGlyphCache::DetachCache(skPaint.getTypeface(), effects, desc.getDesc());
|
||||
|
||||
SkTArray<SkScalar> positions;
|
||||
|
||||
const char* textPtr = text;
|
||||
SkScalar stopX = 0;
|
||||
SkScalar stopY = 0;
|
||||
SkScalar origin = 0;
|
||||
switch (skPaint.getTextAlign()) {
|
||||
case SkPaint::kRight_Align: origin = SK_Scalar1; break;
|
||||
case SkPaint::kCenter_Align: origin = SK_ScalarHalf; break;
|
||||
case SkPaint::kLeft_Align: origin = 0; break;
|
||||
}
|
||||
|
||||
SkAutoKern autokern;
|
||||
const char* stop = text + byteLength;
|
||||
while (textPtr < stop) {
|
||||
// don't need x, y here, since all subpixel variants will have the
|
||||
// same advance
|
||||
const SkGlyph& glyph = glyphCacheProc(origPaintCache, &textPtr);
|
||||
|
||||
SkScalar width = SkFloatToScalar(glyph.fAdvanceX) + autokern.adjust(glyph);
|
||||
positions.push_back(stopX + origin * width);
|
||||
|
||||
SkScalar height = SkFloatToScalar(glyph.fAdvanceY);
|
||||
positions.push_back(stopY + origin * height);
|
||||
|
||||
stopX += width;
|
||||
stopY += height;
|
||||
}
|
||||
SkASSERT(textPtr == stop);
|
||||
|
||||
SkGlyphCache::AttachCache(origPaintCache);
|
||||
|
||||
// now adjust starting point depending on alignment
|
||||
SkScalar alignX = stopX;
|
||||
SkScalar alignY = stopY;
|
||||
if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
|
||||
alignX = SkScalarHalf(alignX);
|
||||
alignY = SkScalarHalf(alignY);
|
||||
} else if (skPaint.getTextAlign() == SkPaint::kLeft_Align) {
|
||||
alignX = 0;
|
||||
alignY = 0;
|
||||
}
|
||||
x -= alignX;
|
||||
y -= alignY;
|
||||
SkPoint offset = SkPoint::Make(x, y);
|
||||
|
||||
DrawDFPosText(blob, runIndex, fontCache, props, paint, scalerContextFlags, viewMatrix, text,
|
||||
byteLength, positions.begin(), 2, offset);
|
||||
}
|
||||
|
||||
void GrAtlasTextContext::DrawDFPosText(GrAtlasTextBlob* blob, int runIndex,
|
||||
GrAtlasGlyphCache* fontCache, const SkSurfaceProps& props,
|
||||
const GrTextUtils::Paint& paint, uint32_t scalerContextFlags,
|
||||
const SkMatrix& viewMatrix, const char text[],
|
||||
size_t byteLength, const SkScalar pos[],
|
||||
int scalarsPerPosition, const SkPoint& offset) {
|
||||
SkASSERT(byteLength == 0 || text != nullptr);
|
||||
SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
|
||||
|
||||
// nothing to draw
|
||||
if (text == nullptr || byteLength == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
SkTDArray<char> fallbackTxt;
|
||||
SkTDArray<SkScalar> fallbackPos;
|
||||
|
||||
// Setup distance field paint and text ratio
|
||||
SkScalar textRatio;
|
||||
SkPaint dfPaint(paint);
|
||||
GrAtlasTextContext::InitDistanceFieldPaint(blob, &dfPaint, &textRatio, viewMatrix);
|
||||
blob->setHasDistanceField();
|
||||
blob->setSubRunHasDistanceFields(runIndex, paint.skPaint().isLCDRenderText(),
|
||||
paint.skPaint().isAntiAlias());
|
||||
|
||||
GrAtlasTextStrike* currStrike = nullptr;
|
||||
|
||||
// We apply the fake-gamma by altering the distance in the shader, so we ignore the
|
||||
// passed-in scaler context flags. (It's only used when we fall-back to bitmap text).
|
||||
SkGlyphCache* cache =
|
||||
blob->setupCache(runIndex, props, SkPaint::kNone_ScalerContextFlags, dfPaint, nullptr);
|
||||
SkPaint::GlyphCacheProc glyphCacheProc =
|
||||
SkPaint::GetGlyphCacheProc(dfPaint.getTextEncoding(), dfPaint.isDevKernText(), true);
|
||||
|
||||
const char* stop = text + byteLength;
|
||||
|
||||
if (SkPaint::kLeft_Align == dfPaint.getTextAlign()) {
|
||||
while (text < stop) {
|
||||
const char* lastText = text;
|
||||
// the last 2 parameters are ignored
|
||||
const SkGlyph& glyph = glyphCacheProc(cache, &text);
|
||||
|
||||
if (glyph.fWidth) {
|
||||
SkScalar x = offset.x() + pos[0];
|
||||
SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
|
||||
|
||||
if (!DfAppendGlyph(blob, runIndex, fontCache, &currStrike, glyph, x, y,
|
||||
paint.filteredPremulColor(), cache, textRatio, viewMatrix)) {
|
||||
// couldn't append, send to fallback
|
||||
fallbackTxt.append(SkToInt(text - lastText), lastText);
|
||||
*fallbackPos.append() = pos[0];
|
||||
if (2 == scalarsPerPosition) {
|
||||
*fallbackPos.append() = pos[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
pos += scalarsPerPosition;
|
||||
}
|
||||
} else {
|
||||
SkScalar alignMul =
|
||||
SkPaint::kCenter_Align == dfPaint.getTextAlign() ? SK_ScalarHalf : SK_Scalar1;
|
||||
while (text < stop) {
|
||||
const char* lastText = text;
|
||||
// the last 2 parameters are ignored
|
||||
const SkGlyph& glyph = glyphCacheProc(cache, &text);
|
||||
|
||||
if (glyph.fWidth) {
|
||||
SkScalar x = offset.x() + pos[0];
|
||||
SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
|
||||
|
||||
SkScalar advanceX = SkFloatToScalar(glyph.fAdvanceX) * alignMul * textRatio;
|
||||
SkScalar advanceY = SkFloatToScalar(glyph.fAdvanceY) * alignMul * textRatio;
|
||||
|
||||
if (!DfAppendGlyph(blob, runIndex, fontCache, &currStrike, glyph, x - advanceX,
|
||||
y - advanceY, paint.filteredPremulColor(), cache, textRatio,
|
||||
viewMatrix)) {
|
||||
// couldn't append, send to fallback
|
||||
fallbackTxt.append(SkToInt(text - lastText), lastText);
|
||||
*fallbackPos.append() = pos[0];
|
||||
if (2 == scalarsPerPosition) {
|
||||
*fallbackPos.append() = pos[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
pos += scalarsPerPosition;
|
||||
}
|
||||
}
|
||||
|
||||
SkGlyphCache::AttachCache(cache);
|
||||
if (fallbackTxt.count()) {
|
||||
blob->initOverride(runIndex);
|
||||
GrAtlasTextContext::DrawBmpPosText(blob, runIndex, fontCache, props, paint,
|
||||
scalerContextFlags, viewMatrix, fallbackTxt.begin(),
|
||||
fallbackTxt.count(), fallbackPos.begin(),
|
||||
scalarsPerPosition, offset);
|
||||
}
|
||||
}
|
||||
|
||||
bool GrAtlasTextContext::DfAppendGlyph(GrAtlasTextBlob* blob, int runIndex,
|
||||
GrAtlasGlyphCache* cache, GrAtlasTextStrike** strike,
|
||||
const SkGlyph& skGlyph, SkScalar sx, SkScalar sy,
|
||||
GrColor color, SkGlyphCache* glyphCache, SkScalar textRatio,
|
||||
const SkMatrix& viewMatrix) {
|
||||
if (!*strike) {
|
||||
*strike = cache->getStrike(glyphCache);
|
||||
}
|
||||
|
||||
GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
|
||||
skGlyph.getSubXFixed(),
|
||||
skGlyph.getSubYFixed(),
|
||||
GrGlyph::kDistance_MaskStyle);
|
||||
GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, glyphCache);
|
||||
if (!glyph) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// fallback to color glyph support
|
||||
if (kA8_GrMaskFormat != glyph->fMaskFormat) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
|
||||
SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
|
||||
SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2 * SK_DistanceFieldInset);
|
||||
SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2 * SK_DistanceFieldInset);
|
||||
|
||||
SkScalar scale = textRatio;
|
||||
dx *= scale;
|
||||
dy *= scale;
|
||||
width *= scale;
|
||||
height *= scale;
|
||||
sx += dx;
|
||||
sy += dy;
|
||||
SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height);
|
||||
|
||||
blob->appendGlyph(runIndex, glyphRect, color, *strike, glyph, glyphCache, skGlyph, sx - dx,
|
||||
sy - dy, scale, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if GR_TEST_UTILS
|
||||
|
@ -79,6 +79,48 @@ private:
|
||||
const SkScalar pos[],
|
||||
int scalarsPerPosition,
|
||||
const SkPoint& offset);
|
||||
|
||||
// Functions for appending BMP text to GrAtlasTextBlob
|
||||
static void DrawBmpText(GrAtlasTextBlob*, int runIndex, GrAtlasGlyphCache*,
|
||||
const SkSurfaceProps&, const GrTextUtils::Paint& paint,
|
||||
uint32_t scalerContextFlags, const SkMatrix& viewMatrix,
|
||||
const char text[], size_t byteLength, SkScalar x, SkScalar y);
|
||||
|
||||
static void DrawBmpPosText(GrAtlasTextBlob*, int runIndex, GrAtlasGlyphCache*,
|
||||
const SkSurfaceProps&, const GrTextUtils::Paint& paint,
|
||||
uint32_t scalerContextFlags, const SkMatrix& viewMatrix,
|
||||
const char text[], size_t byteLength, const SkScalar pos[],
|
||||
int scalarsPerPosition, const SkPoint& offset);
|
||||
|
||||
// functions for appending distance field text
|
||||
static bool CanDrawAsDistanceFields(const SkPaint& skPaint, const SkMatrix& viewMatrix,
|
||||
const SkSurfaceProps& props, const GrShaderCaps& caps);
|
||||
|
||||
static void DrawDFText(GrAtlasTextBlob* blob, int runIndex, GrAtlasGlyphCache*,
|
||||
const SkSurfaceProps&, const GrTextUtils::Paint& paint,
|
||||
uint32_t scalerContextFlags, const SkMatrix& viewMatrix,
|
||||
const char text[], size_t byteLength, SkScalar x, SkScalar y);
|
||||
|
||||
static void DrawDFPosText(GrAtlasTextBlob* blob, int runIndex, GrAtlasGlyphCache*,
|
||||
const SkSurfaceProps&, const GrTextUtils::Paint& paint,
|
||||
uint32_t scalerContextFlags, const SkMatrix& viewMatrix,
|
||||
const char text[], size_t byteLength, const SkScalar pos[],
|
||||
int scalarsPerPosition, const SkPoint& offset);
|
||||
|
||||
static void InitDistanceFieldPaint(GrAtlasTextBlob* blob,
|
||||
SkPaint* skPaint,
|
||||
SkScalar* textRatio,
|
||||
const SkMatrix& viewMatrix);
|
||||
|
||||
static void BmpAppendGlyph(GrAtlasTextBlob*, int runIndex, GrAtlasGlyphCache*,
|
||||
GrAtlasTextStrike**, const SkGlyph&, int left, int top,
|
||||
GrColor color, SkGlyphCache*);
|
||||
|
||||
static bool DfAppendGlyph(GrAtlasTextBlob*, int runIndex, GrAtlasGlyphCache*,
|
||||
GrAtlasTextStrike**, const SkGlyph&, SkScalar sx, SkScalar sy,
|
||||
GrColor color, SkGlyphCache* cache, SkScalar textRatio,
|
||||
const SkMatrix& viewMatrix);
|
||||
|
||||
const GrDistanceFieldAdjustTable* dfAdjustTable() const { return fDistanceAdjustTable.get(); }
|
||||
|
||||
sk_sp<const GrDistanceFieldAdjustTable> fDistanceAdjustTable;
|
||||
|
@ -6,37 +6,16 @@
|
||||
*/
|
||||
|
||||
#include "GrTextUtils.h"
|
||||
#include "GrAtlasGlyphCache.h"
|
||||
#include "GrAtlasTextBlob.h"
|
||||
#include "GrBlurUtils.h"
|
||||
#include "GrCaps.h"
|
||||
#include "GrContext.h"
|
||||
#include "SkDistanceFieldGen.h"
|
||||
#include "SkDrawFilter.h"
|
||||
#include "SkDrawProcs.h"
|
||||
#include "SkFindAndPlaceGlyph.h"
|
||||
#include "SkGlyphCache.h"
|
||||
#include "SkGr.h"
|
||||
#include "SkPaint.h"
|
||||
#include "SkRect.h"
|
||||
#include "SkTextBlobRunIterator.h"
|
||||
#include "SkTextMapStateProc.h"
|
||||
#include "SkTextToPathIter.h"
|
||||
|
||||
namespace {
|
||||
static const int kMinDFFontSize = 18;
|
||||
static const int kSmallDFFontSize = 32;
|
||||
static const int kSmallDFFontLimit = 32;
|
||||
static const int kMediumDFFontSize = 72;
|
||||
static const int kMediumDFFontLimit = 72;
|
||||
static const int kLargeDFFontSize = 162;
|
||||
#ifdef SK_BUILD_FOR_ANDROID
|
||||
static const int kLargeDFFontLimit = 384;
|
||||
#else
|
||||
static const int kLargeDFFontLimit = 2 * kLargeDFFontSize;
|
||||
#endif
|
||||
};
|
||||
|
||||
void GrTextUtils::Paint::initFilteredColor() {
|
||||
// This mirrors the logic in skpaint_to_grpaint_impl for handling paint colors
|
||||
if (fDstColorSpaceInfo->colorSpace()) {
|
||||
@ -79,416 +58,24 @@ bool GrTextUtils::RunPaint::modifyForRun(const SkTextBlobRunIterator& run) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void GrTextUtils::DrawBmpText(GrAtlasTextBlob* blob, int runIndex, GrAtlasGlyphCache* fontCache,
|
||||
const SkSurfaceProps& props, const GrTextUtils::Paint& paint,
|
||||
uint32_t scalerContextFlags, const SkMatrix& viewMatrix,
|
||||
const char text[], size_t byteLength, SkScalar x, SkScalar y) {
|
||||
SkASSERT(byteLength == 0 || text != nullptr);
|
||||
uint32_t GrTextUtils::FilterTextFlags(const SkSurfaceProps& surfaceProps, const SkPaint& paint) {
|
||||
uint32_t flags = paint.getFlags();
|
||||
|
||||
// nothing to draw
|
||||
if (text == nullptr || byteLength == 0) {
|
||||
return;
|
||||
if (!paint.isLCDRenderText() || !paint.isAntiAlias()) {
|
||||
return flags;
|
||||
}
|
||||
|
||||
// Ensure the blob is set for bitmaptext
|
||||
blob->setHasBitmap();
|
||||
|
||||
GrAtlasTextStrike* currStrike = nullptr;
|
||||
|
||||
SkGlyphCache* cache = blob->setupCache(runIndex, props, scalerContextFlags, paint, &viewMatrix);
|
||||
SkFindAndPlaceGlyph::ProcessText(
|
||||
paint.skPaint().getTextEncoding(), text, byteLength,
|
||||
{x, y}, viewMatrix, paint.skPaint().getTextAlign(),
|
||||
cache,
|
||||
[&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
|
||||
position += rounding;
|
||||
BmpAppendGlyph(
|
||||
blob, runIndex, fontCache, &currStrike, glyph,
|
||||
SkScalarFloorToInt(position.fX), SkScalarFloorToInt(position.fY),
|
||||
paint.filteredPremulColor(), cache);
|
||||
}
|
||||
);
|
||||
|
||||
SkGlyphCache::AttachCache(cache);
|
||||
if (kUnknown_SkPixelGeometry == surfaceProps.pixelGeometry() || ShouldDisableLCD(paint)) {
|
||||
flags &= ~SkPaint::kLCDRenderText_Flag;
|
||||
flags |= SkPaint::kGenA8FromLCD_Flag;
|
||||
}
|
||||
|
||||
void GrTextUtils::DrawBmpPosText(GrAtlasTextBlob* blob, int runIndex, GrAtlasGlyphCache* fontCache,
|
||||
const SkSurfaceProps& props, const GrTextUtils::Paint& paint,
|
||||
uint32_t scalerContextFlags, const SkMatrix& viewMatrix,
|
||||
const char text[], size_t byteLength, const SkScalar pos[],
|
||||
int scalarsPerPosition, const SkPoint& offset) {
|
||||
SkASSERT(byteLength == 0 || text != nullptr);
|
||||
SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
|
||||
|
||||
// nothing to draw
|
||||
if (text == nullptr || byteLength == 0) {
|
||||
return;
|
||||
return flags;
|
||||
}
|
||||
|
||||
// Ensure the blob is set for bitmaptext
|
||||
blob->setHasBitmap();
|
||||
|
||||
GrAtlasTextStrike* currStrike = nullptr;
|
||||
|
||||
SkGlyphCache* cache = blob->setupCache(runIndex, props, scalerContextFlags, paint, &viewMatrix);
|
||||
|
||||
SkFindAndPlaceGlyph::ProcessPosText(
|
||||
paint.skPaint().getTextEncoding(), text, byteLength,
|
||||
offset, viewMatrix, pos, scalarsPerPosition,
|
||||
paint.skPaint().getTextAlign(), cache,
|
||||
[&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
|
||||
position += rounding;
|
||||
BmpAppendGlyph(
|
||||
blob, runIndex, fontCache, &currStrike, glyph,
|
||||
SkScalarFloorToInt(position.fX), SkScalarFloorToInt(position.fY),
|
||||
paint.filteredPremulColor(), cache);
|
||||
}
|
||||
);
|
||||
|
||||
SkGlyphCache::AttachCache(cache);
|
||||
}
|
||||
|
||||
void GrTextUtils::BmpAppendGlyph(GrAtlasTextBlob* blob, int runIndex,
|
||||
GrAtlasGlyphCache* fontCache,
|
||||
GrAtlasTextStrike** strike, const SkGlyph& skGlyph,
|
||||
int vx, int vy, GrColor color, SkGlyphCache* cache) {
|
||||
if (!*strike) {
|
||||
*strike = fontCache->getStrike(cache);
|
||||
}
|
||||
|
||||
GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
|
||||
skGlyph.getSubXFixed(),
|
||||
skGlyph.getSubYFixed(),
|
||||
GrGlyph::kCoverage_MaskStyle);
|
||||
GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, cache);
|
||||
if (!glyph) {
|
||||
return;
|
||||
}
|
||||
|
||||
int x = vx + glyph->fBounds.fLeft;
|
||||
int y = vy + glyph->fBounds.fTop;
|
||||
|
||||
int width = glyph->fBounds.width();
|
||||
int height = glyph->fBounds.height();
|
||||
|
||||
SkRect r;
|
||||
r.fLeft = SkIntToScalar(x);
|
||||
r.fTop = SkIntToScalar(y);
|
||||
r.fRight = r.fLeft + SkIntToScalar(width);
|
||||
r.fBottom = r.fTop + SkIntToScalar(height);
|
||||
|
||||
blob->appendGlyph(runIndex, r, color, *strike, glyph, cache, skGlyph,
|
||||
SkIntToScalar(vx), SkIntToScalar(vy), 1.0f, true);
|
||||
}
|
||||
|
||||
bool GrTextUtils::CanDrawAsDistanceFields(const SkPaint& skPaint, const SkMatrix& viewMatrix,
|
||||
const SkSurfaceProps& props, const GrShaderCaps& caps) {
|
||||
if (!viewMatrix.hasPerspective()) {
|
||||
SkScalar maxScale = viewMatrix.getMaxScale();
|
||||
SkScalar scaledTextSize = maxScale * skPaint.getTextSize();
|
||||
// Hinted text looks far better at small resolutions
|
||||
// Scaling up beyond 2x yields undesireable artifacts
|
||||
if (scaledTextSize < kMinDFFontSize ||
|
||||
scaledTextSize > kLargeDFFontLimit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool useDFT = props.isUseDeviceIndependentFonts();
|
||||
#if SK_FORCE_DISTANCE_FIELD_TEXT
|
||||
useDFT = true;
|
||||
#endif
|
||||
|
||||
if (!useDFT && scaledTextSize < kLargeDFFontSize) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// rasterizers and mask filters modify alpha, which doesn't
|
||||
// translate well to distance
|
||||
if (skPaint.getRasterizer() || skPaint.getMaskFilter() || !caps.shaderDerivativeSupport()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: add some stroking support
|
||||
if (skPaint.getStyle() != SkPaint::kFill_Style) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GrTextUtils::InitDistanceFieldPaint(GrAtlasTextBlob* blob,
|
||||
SkPaint* skPaint,
|
||||
SkScalar* textRatio,
|
||||
const SkMatrix& viewMatrix) {
|
||||
SkScalar textSize = skPaint->getTextSize();
|
||||
SkScalar scaledTextSize = textSize;
|
||||
|
||||
if (viewMatrix.hasPerspective()) {
|
||||
// for perspective, we simply force to the medium size
|
||||
// TODO: compute a size based on approximate screen area
|
||||
scaledTextSize = kMediumDFFontLimit;
|
||||
} else {
|
||||
SkScalar maxScale = viewMatrix.getMaxScale();
|
||||
// if we have non-unity scale, we need to choose our base text size
|
||||
// based on the SkPaint's text size multiplied by the max scale factor
|
||||
// TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
|
||||
if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
|
||||
scaledTextSize *= maxScale;
|
||||
}
|
||||
}
|
||||
|
||||
// We have three sizes of distance field text, and within each size 'bucket' there is a floor
|
||||
// and ceiling. A scale outside of this range would require regenerating the distance fields
|
||||
SkScalar dfMaskScaleFloor;
|
||||
SkScalar dfMaskScaleCeil;
|
||||
if (scaledTextSize <= kSmallDFFontLimit) {
|
||||
dfMaskScaleFloor = kMinDFFontSize;
|
||||
dfMaskScaleCeil = kSmallDFFontLimit;
|
||||
*textRatio = textSize / kSmallDFFontSize;
|
||||
skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize));
|
||||
} else if (scaledTextSize <= kMediumDFFontLimit) {
|
||||
dfMaskScaleFloor = kSmallDFFontLimit;
|
||||
dfMaskScaleCeil = kMediumDFFontLimit;
|
||||
*textRatio = textSize / kMediumDFFontSize;
|
||||
skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize));
|
||||
} else {
|
||||
dfMaskScaleFloor = kMediumDFFontLimit;
|
||||
dfMaskScaleCeil = kLargeDFFontLimit;
|
||||
*textRatio = textSize / kLargeDFFontSize;
|
||||
skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize));
|
||||
}
|
||||
|
||||
// Because there can be multiple runs in the blob, we want the overall maxMinScale, and
|
||||
// minMaxScale to make regeneration decisions. Specifically, we want the maximum minimum scale
|
||||
// we can tolerate before we'd drop to a lower mip size, and the minimum maximum scale we can
|
||||
// tolerate before we'd have to move to a large mip size. When we actually test these values
|
||||
// we look at the delta in scale between the new viewmatrix and the old viewmatrix, and test
|
||||
// against these values to decide if we can reuse or not(ie, will a given scale change our mip
|
||||
// level)
|
||||
SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil);
|
||||
blob->setMinAndMaxScale(dfMaskScaleFloor / scaledTextSize, dfMaskScaleCeil / scaledTextSize);
|
||||
|
||||
skPaint->setAntiAlias(true);
|
||||
skPaint->setLCDRenderText(false);
|
||||
skPaint->setAutohinted(false);
|
||||
skPaint->setHinting(SkPaint::kNormal_Hinting);
|
||||
skPaint->setSubpixelText(true);
|
||||
}
|
||||
|
||||
void GrTextUtils::DrawDFText(GrAtlasTextBlob* blob, int runIndex,
|
||||
GrAtlasGlyphCache* fontCache, const SkSurfaceProps& props,
|
||||
const GrTextUtils::Paint& paint, uint32_t scalerContextFlags,
|
||||
const SkMatrix& viewMatrix,
|
||||
const char text[], size_t byteLength,
|
||||
SkScalar x, SkScalar y) {
|
||||
SkASSERT(byteLength == 0 || text != nullptr);
|
||||
|
||||
// nothing to draw
|
||||
if (text == nullptr || byteLength == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const SkPaint& skPaint = paint.skPaint();
|
||||
SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(skPaint.getTextEncoding(),
|
||||
skPaint.isDevKernText(),
|
||||
true);
|
||||
SkAutoDescriptor desc;
|
||||
SkScalerContextEffects effects;
|
||||
// We apply the fake-gamma by altering the distance in the shader, so we ignore the
|
||||
// passed-in scaler context flags. (It's only used when we fall-back to bitmap text).
|
||||
skPaint.getScalerContextDescriptor(&effects, &desc, props, SkPaint::kNone_ScalerContextFlags,
|
||||
nullptr);
|
||||
SkGlyphCache* origPaintCache = SkGlyphCache::DetachCache(skPaint.getTypeface(), effects,
|
||||
desc.getDesc());
|
||||
|
||||
SkTArray<SkScalar> positions;
|
||||
|
||||
const char* textPtr = text;
|
||||
SkScalar stopX = 0;
|
||||
SkScalar stopY = 0;
|
||||
SkScalar origin = 0;
|
||||
switch (skPaint.getTextAlign()) {
|
||||
case SkPaint::kRight_Align: origin = SK_Scalar1; break;
|
||||
case SkPaint::kCenter_Align: origin = SK_ScalarHalf; break;
|
||||
case SkPaint::kLeft_Align: origin = 0; break;
|
||||
}
|
||||
|
||||
SkAutoKern autokern;
|
||||
const char* stop = text + byteLength;
|
||||
while (textPtr < stop) {
|
||||
// don't need x, y here, since all subpixel variants will have the
|
||||
// same advance
|
||||
const SkGlyph& glyph = glyphCacheProc(origPaintCache, &textPtr);
|
||||
|
||||
SkScalar width = SkFloatToScalar(glyph.fAdvanceX) + autokern.adjust(glyph);
|
||||
positions.push_back(stopX + origin * width);
|
||||
|
||||
SkScalar height = SkFloatToScalar(glyph.fAdvanceY);
|
||||
positions.push_back(stopY + origin * height);
|
||||
|
||||
stopX += width;
|
||||
stopY += height;
|
||||
}
|
||||
SkASSERT(textPtr == stop);
|
||||
|
||||
SkGlyphCache::AttachCache(origPaintCache);
|
||||
|
||||
// now adjust starting point depending on alignment
|
||||
SkScalar alignX = stopX;
|
||||
SkScalar alignY = stopY;
|
||||
if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
|
||||
alignX = SkScalarHalf(alignX);
|
||||
alignY = SkScalarHalf(alignY);
|
||||
} else if (skPaint.getTextAlign() == SkPaint::kLeft_Align) {
|
||||
alignX = 0;
|
||||
alignY = 0;
|
||||
}
|
||||
x -= alignX;
|
||||
y -= alignY;
|
||||
SkPoint offset = SkPoint::Make(x, y);
|
||||
|
||||
DrawDFPosText(blob, runIndex, fontCache, props, paint, scalerContextFlags, viewMatrix, text,
|
||||
byteLength, positions.begin(), 2, offset);
|
||||
}
|
||||
|
||||
void GrTextUtils::DrawDFPosText(GrAtlasTextBlob* blob, int runIndex, GrAtlasGlyphCache* fontCache,
|
||||
const SkSurfaceProps& props, const GrTextUtils::Paint& paint,
|
||||
uint32_t scalerContextFlags, const SkMatrix& viewMatrix,
|
||||
const char text[], size_t byteLength, const SkScalar pos[],
|
||||
int scalarsPerPosition, const SkPoint& offset) {
|
||||
SkASSERT(byteLength == 0 || text != nullptr);
|
||||
SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
|
||||
|
||||
// nothing to draw
|
||||
if (text == nullptr || byteLength == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
SkTDArray<char> fallbackTxt;
|
||||
SkTDArray<SkScalar> fallbackPos;
|
||||
|
||||
// Setup distance field paint and text ratio
|
||||
SkScalar textRatio;
|
||||
SkPaint dfPaint(paint);
|
||||
GrTextUtils::InitDistanceFieldPaint(blob, &dfPaint, &textRatio, viewMatrix);
|
||||
blob->setHasDistanceField();
|
||||
blob->setSubRunHasDistanceFields(runIndex, paint.skPaint().isLCDRenderText(),
|
||||
paint.skPaint().isAntiAlias());
|
||||
|
||||
GrAtlasTextStrike* currStrike = nullptr;
|
||||
|
||||
// We apply the fake-gamma by altering the distance in the shader, so we ignore the
|
||||
// passed-in scaler context flags. (It's only used when we fall-back to bitmap text).
|
||||
SkGlyphCache* cache = blob->setupCache(runIndex, props, SkPaint::kNone_ScalerContextFlags,
|
||||
dfPaint, nullptr);
|
||||
SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(dfPaint.getTextEncoding(),
|
||||
dfPaint.isDevKernText(),
|
||||
true);
|
||||
|
||||
const char* stop = text + byteLength;
|
||||
|
||||
if (SkPaint::kLeft_Align == dfPaint.getTextAlign()) {
|
||||
while (text < stop) {
|
||||
const char* lastText = text;
|
||||
// the last 2 parameters are ignored
|
||||
const SkGlyph& glyph = glyphCacheProc(cache, &text);
|
||||
|
||||
if (glyph.fWidth) {
|
||||
SkScalar x = offset.x() + pos[0];
|
||||
SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
|
||||
|
||||
if (!DfAppendGlyph(blob, runIndex, fontCache, &currStrike, glyph, x, y,
|
||||
paint.filteredPremulColor(), cache, textRatio, viewMatrix)) {
|
||||
// couldn't append, send to fallback
|
||||
fallbackTxt.append(SkToInt(text-lastText), lastText);
|
||||
*fallbackPos.append() = pos[0];
|
||||
if (2 == scalarsPerPosition) {
|
||||
*fallbackPos.append() = pos[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
pos += scalarsPerPosition;
|
||||
}
|
||||
} else {
|
||||
SkScalar alignMul = SkPaint::kCenter_Align == dfPaint.getTextAlign() ? SK_ScalarHalf
|
||||
: SK_Scalar1;
|
||||
while (text < stop) {
|
||||
const char* lastText = text;
|
||||
// the last 2 parameters are ignored
|
||||
const SkGlyph& glyph = glyphCacheProc(cache, &text);
|
||||
|
||||
if (glyph.fWidth) {
|
||||
SkScalar x = offset.x() + pos[0];
|
||||
SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
|
||||
|
||||
SkScalar advanceX = SkFloatToScalar(glyph.fAdvanceX) * alignMul * textRatio;
|
||||
SkScalar advanceY = SkFloatToScalar(glyph.fAdvanceY) * alignMul * textRatio;
|
||||
|
||||
if (!DfAppendGlyph(blob, runIndex, fontCache, &currStrike, glyph, x - advanceX,
|
||||
y - advanceY, paint.filteredPremulColor(), cache, textRatio,
|
||||
viewMatrix)) {
|
||||
// couldn't append, send to fallback
|
||||
fallbackTxt.append(SkToInt(text-lastText), lastText);
|
||||
*fallbackPos.append() = pos[0];
|
||||
if (2 == scalarsPerPosition) {
|
||||
*fallbackPos.append() = pos[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
pos += scalarsPerPosition;
|
||||
}
|
||||
}
|
||||
|
||||
SkGlyphCache::AttachCache(cache);
|
||||
if (fallbackTxt.count()) {
|
||||
blob->initOverride(runIndex);
|
||||
GrTextUtils::DrawBmpPosText(blob, runIndex, fontCache, props, paint, scalerContextFlags,
|
||||
viewMatrix, fallbackTxt.begin(), fallbackTxt.count(),
|
||||
fallbackPos.begin(), scalarsPerPosition, offset);
|
||||
}
|
||||
}
|
||||
|
||||
bool GrTextUtils::DfAppendGlyph(GrAtlasTextBlob* blob, int runIndex, GrAtlasGlyphCache* cache,
|
||||
GrAtlasTextStrike** strike, const SkGlyph& skGlyph,
|
||||
SkScalar sx, SkScalar sy, GrColor color,
|
||||
SkGlyphCache* glyphCache,
|
||||
SkScalar textRatio, const SkMatrix& viewMatrix) {
|
||||
if (!*strike) {
|
||||
*strike = cache->getStrike(glyphCache);
|
||||
}
|
||||
|
||||
GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
|
||||
skGlyph.getSubXFixed(),
|
||||
skGlyph.getSubYFixed(),
|
||||
GrGlyph::kDistance_MaskStyle);
|
||||
GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, glyphCache);
|
||||
if (!glyph) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// fallback to color glyph support
|
||||
if (kA8_GrMaskFormat != glyph->fMaskFormat) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
|
||||
SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
|
||||
SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2 * SK_DistanceFieldInset);
|
||||
SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2 * SK_DistanceFieldInset);
|
||||
|
||||
SkScalar scale = textRatio;
|
||||
dx *= scale;
|
||||
dy *= scale;
|
||||
width *= scale;
|
||||
height *= scale;
|
||||
sx += dx;
|
||||
sy += dy;
|
||||
SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height);
|
||||
|
||||
blob->appendGlyph(runIndex, glyphRect, color, *strike, glyph, glyphCache, skGlyph,
|
||||
sx - dx, sy - dy, scale, false);
|
||||
return true;
|
||||
bool GrTextUtils::ShouldDisableLCD(const SkPaint& paint) {
|
||||
return paint.getMaskFilter() || paint.getRasterizer() || paint.getPathEffect() ||
|
||||
paint.isFakeBoldText() || paint.getStyle() != SkPaint::kFill_Style;
|
||||
}
|
||||
|
||||
void GrTextUtils::DrawTextAsPath(GrContext* context, GrTextUtils::Target* target,
|
||||
@ -569,26 +156,3 @@ void GrTextUtils::DrawPosTextAsPath(GrContext* context, GrTextUtils::Target* tar
|
||||
pos += scalarsPerPosition;
|
||||
}
|
||||
}
|
||||
|
||||
bool GrTextUtils::ShouldDisableLCD(const SkPaint& paint) {
|
||||
return paint.getMaskFilter() ||
|
||||
paint.getRasterizer() ||
|
||||
paint.getPathEffect() ||
|
||||
paint.isFakeBoldText() ||
|
||||
paint.getStyle() != SkPaint::kFill_Style;
|
||||
}
|
||||
|
||||
uint32_t GrTextUtils::FilterTextFlags(const SkSurfaceProps& surfaceProps, const SkPaint& paint) {
|
||||
uint32_t flags = paint.getFlags();
|
||||
|
||||
if (!paint.isLCDRenderText() || !paint.isAntiAlias()) {
|
||||
return flags;
|
||||
}
|
||||
|
||||
if (kUnknown_SkPixelGeometry == surfaceProps.pixelGeometry() || ShouldDisableLCD(paint)) {
|
||||
flags &= ~SkPaint::kLCDRenderText_Flag;
|
||||
flags |= SkPaint::kGenA8FromLCD_Flag;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
@ -124,64 +124,22 @@ public:
|
||||
const SkSurfaceProps& fProps;
|
||||
};
|
||||
|
||||
// Functions for appending BMP text to GrAtlasTextBlob
|
||||
static void DrawBmpText(GrAtlasTextBlob*, int runIndex, GrAtlasGlyphCache*,
|
||||
const SkSurfaceProps&, const Paint& paint, uint32_t scalerContextFlags,
|
||||
const SkMatrix& viewMatrix, const char text[], size_t byteLength,
|
||||
SkScalar x, SkScalar y);
|
||||
|
||||
static void DrawBmpPosText(GrAtlasTextBlob*, int runIndex, GrAtlasGlyphCache*,
|
||||
const SkSurfaceProps&, const Paint& paint,
|
||||
uint32_t scalerContextFlags, const SkMatrix& viewMatrix,
|
||||
const char text[], size_t byteLength, const SkScalar pos[],
|
||||
int scalarsPerPosition, const SkPoint& offset);
|
||||
|
||||
// functions for appending distance field text
|
||||
static bool CanDrawAsDistanceFields(const SkPaint& skPaint, const SkMatrix& viewMatrix,
|
||||
const SkSurfaceProps& props, const GrShaderCaps& caps);
|
||||
|
||||
static void DrawDFText(GrAtlasTextBlob* blob, int runIndex, GrAtlasGlyphCache*,
|
||||
const SkSurfaceProps&, const Paint& paint, uint32_t scalerContextFlags,
|
||||
const SkMatrix& viewMatrix, const char text[], size_t byteLength,
|
||||
SkScalar x, SkScalar y);
|
||||
|
||||
static void DrawDFPosText(GrAtlasTextBlob* blob, int runIndex, GrAtlasGlyphCache*,
|
||||
const SkSurfaceProps&, const Paint& paint,
|
||||
uint32_t scalerContextFlags, const SkMatrix& viewMatrix,
|
||||
const char text[], size_t byteLength, const SkScalar pos[],
|
||||
int scalarsPerPosition, const SkPoint& offset);
|
||||
|
||||
// Functions for drawing text as paths
|
||||
static void DrawTextAsPath(GrContext*, Target*, const GrClip& clip, const SkPaint& paint,
|
||||
const SkMatrix& viewMatrix, const char text[], size_t byteLength,
|
||||
SkScalar x, SkScalar y, const SkIRect& clipBounds);
|
||||
|
||||
static void DrawPosTextAsPath(GrContext* context, Target*, const SkSurfaceProps& props,
|
||||
const GrClip& clip, const SkPaint& paint,
|
||||
const SkMatrix& viewMatrix, const char text[], size_t byteLength,
|
||||
const SkScalar pos[], int scalarsPerPosition,
|
||||
const SkPoint& offset, const SkIRect& clipBounds);
|
||||
static uint32_t FilterTextFlags(const SkSurfaceProps& surfaceProps, const SkPaint& paint);
|
||||
|
||||
static bool ShouldDisableLCD(const SkPaint& paint);
|
||||
|
||||
// Functions for drawing text as paths
|
||||
static void DrawTextAsPath(GrContext*, GrTextUtils::Target*, const GrClip& clip,
|
||||
const SkPaint& paint, const SkMatrix& viewMatrix, const char text[],
|
||||
size_t byteLength, SkScalar x, SkScalar y,
|
||||
const SkIRect& clipBounds);
|
||||
|
||||
private:
|
||||
static uint32_t FilterTextFlags(const SkSurfaceProps& surfaceProps, const SkPaint& paint);
|
||||
|
||||
static void InitDistanceFieldPaint(GrAtlasTextBlob* blob,
|
||||
SkPaint* skPaint,
|
||||
SkScalar* textRatio,
|
||||
const SkMatrix& viewMatrix);
|
||||
|
||||
static void BmpAppendGlyph(GrAtlasTextBlob*, int runIndex, GrAtlasGlyphCache*,
|
||||
GrAtlasTextStrike**, const SkGlyph&, int left, int top,
|
||||
GrColor color, SkGlyphCache*);
|
||||
|
||||
static bool DfAppendGlyph(GrAtlasTextBlob*, int runIndex, GrAtlasGlyphCache*,
|
||||
GrAtlasTextStrike**, const SkGlyph&,
|
||||
SkScalar sx, SkScalar sy, GrColor color,
|
||||
SkGlyphCache* cache,
|
||||
SkScalar textRatio, const SkMatrix& viewMatrix);
|
||||
static void DrawPosTextAsPath(GrContext* context, GrTextUtils::Target*,
|
||||
const SkSurfaceProps& props, const GrClip& clip,
|
||||
const SkPaint& paint, const SkMatrix& viewMatrix,
|
||||
const char text[], size_t byteLength, const SkScalar pos[],
|
||||
int scalarsPerPosition, const SkPoint& offset,
|
||||
const SkIRect& clipBounds);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user