Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(329)

Unified Diff: src/gpu/text/GrTextUtils.cpp

Issue 1521633002: Move distance field text positioning into GrTextUtils (Closed) Base URL: https://skia.googlesource.com/skia.git@cleanuptext15
Patch Set: tweaks Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/gpu/text/GrTextUtils.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/gpu/text/GrTextUtils.cpp
diff --git a/src/gpu/text/GrTextUtils.cpp b/src/gpu/text/GrTextUtils.cpp
index 969be421c332f9fab147055aee8fb2f8765361dd..2f2c1f116a973a705da558f2c52f1944b926c344 100644
--- a/src/gpu/text/GrTextUtils.cpp
+++ b/src/gpu/text/GrTextUtils.cpp
@@ -10,9 +10,12 @@
#include "GrAtlasTextBlob.h"
#include "GrBatchFontCache.h"
#include "GrBlurUtils.h"
+#include "GrCaps.h"
#include "GrContext.h"
#include "GrDrawContext.h"
#include "GrTextContext.h"
+
+#include "SkDistanceFieldGen.h"
#include "SkDrawProcs.h"
#include "SkFindAndPlaceGlyph.h"
#include "SkGlyphCache.h"
@@ -21,6 +24,20 @@
#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::DrawBmpText(GrAtlasTextBlob* blob, int runIndex,
GrBatchFontCache* fontCache,
const SkSurfaceProps& props, const SkPaint& skPaint,
@@ -135,6 +152,316 @@ void GrTextUtils::BmpAppendGlyph(GrAtlasTextBlob* blob, int runIndex,
SkIntToScalar(vx), SkIntToScalar(vy), 1.0f, false);
}
+bool GrTextUtils::CanDrawAsDistanceFields(const SkPaint& skPaint, const SkMatrix& viewMatrix,
+ const SkSurfaceProps& props, const GrShaderCaps& caps) {
+ // TODO: support perspective (need getMaxScale replacement)
+ if (viewMatrix.hasPerspective()) {
+ return false;
+ }
+
+ 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) {
+ // getMaxScale doesn't support perspective, so neither do we at the moment
+ SkASSERT(!viewMatrix.hasPerspective());
+ SkScalar maxScale = viewMatrix.getMaxScale();
+ SkScalar textSize = skPaint->getTextSize();
+ SkScalar scaledTextSize = textSize;
+ // 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->fMaxMinScale = SkMaxScalar(dfMaskScaleFloor / scaledTextSize, blob->fMaxMinScale);
+ blob->fMinMaxScale = SkMinScalar(dfMaskScaleCeil / scaledTextSize, blob->fMinMaxScale);
+
+ skPaint->setLCDRenderText(false);
+ skPaint->setAutohinted(false);
+ skPaint->setHinting(SkPaint::kNormal_Hinting);
+ skPaint->setSubpixelText(true);
+}
+
+void GrTextUtils::DrawDFText(GrAtlasTextBlob* blob, int runIndex,
+ GrBatchFontCache* fontCache, const SkSurfaceProps& props,
+ const SkPaint& skPaint, GrColor color,
+ 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;
+ }
+
+ SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
+ SkAutoDescriptor desc;
+ skPaint.getScalerContextDescriptor(&desc, props, nullptr, true);
+ SkGlyphCache* origPaintCache = SkGlyphCache::DetachCache(skPaint.getTypeface(),
+ desc.getDesc());
+
+ SkTArray<SkScalar> positions;
+
+ const char* textPtr = text;
+ SkFixed stopX = 0;
+ SkFixed stopY = 0;
+ SkFixed origin = 0;
+ switch (skPaint.getTextAlign()) {
+ case SkPaint::kRight_Align: origin = SK_Fixed1; break;
+ case SkPaint::kCenter_Align: origin = SK_FixedHalf; 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, 0, 0);
+
+ SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph);
+ positions.push_back(SkFixedToScalar(stopX + SkFixedMul(origin, width)));
+
+ SkFixed height = glyph.fAdvanceY;
+ positions.push_back(SkFixedToScalar(stopY + SkFixedMul(origin, height)));
+
+ stopX += width;
+ stopY += height;
+ }
+ SkASSERT(textPtr == stop);
+
+ SkGlyphCache::AttachCache(origPaintCache);
+
+ // now adjust starting point depending on alignment
+ SkScalar alignX = SkFixedToScalar(stopX);
+ SkScalar alignY = SkFixedToScalar(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, skPaint, color, viewMatrix, text, byteLength,
+ positions.begin(), 2, offset);
+}
+
+void GrTextUtils::DrawDFPosText(GrAtlasTextBlob* blob, int runIndex,
+ GrBatchFontCache* fontCache, const SkSurfaceProps& props,
+ const SkPaint& origPaint,
+ GrColor color, 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(origPaint);
+ GrTextUtils::InitDistanceFieldPaint(blob, &dfPaint, &textRatio, viewMatrix);
+ blob->setHasDistanceField();
+ blob->setSubRunHasDistanceFields(runIndex, origPaint.isLCDRenderText());
+
+ GrBatchTextStrike* currStrike = nullptr;
+
+ SkGlyphCache* cache = blob->setupCache(runIndex, props, dfPaint, nullptr, true);
+ SkDrawCacheProc glyphCacheProc = dfPaint.getDrawCacheProc();
+ GrFontScaler* fontScaler = GrTextContext::GetGrFontScaler(cache);
+
+ 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, 0, 0);
+
+ 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, color, fontScaler,
+ 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, 0, 0);
+
+ if (glyph.fWidth) {
+ SkScalar x = offset.x() + pos[0];
+ SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
+
+ SkScalar advanceX = SkFixedToScalar(glyph.fAdvanceX) * alignMul * textRatio;
+ SkScalar advanceY = SkFixedToScalar(glyph.fAdvanceY) * alignMul * textRatio;
+
+ if (!DfAppendGlyph(blob,
+ runIndex,
+ fontCache,
+ &currStrike,
+ glyph,
+ x - advanceX, y - advanceY, color,
+ fontScaler,
+ 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,
+ origPaint, origPaint.getColor(), viewMatrix,
+ fallbackTxt.begin(), fallbackTxt.count(),
+ fallbackPos.begin(), scalarsPerPosition, offset);
+ }
+}
+
+bool GrTextUtils::DfAppendGlyph(GrAtlasTextBlob* blob, int runIndex, GrBatchFontCache* cache,
+ GrBatchTextStrike** strike, const SkGlyph& skGlyph,
+ SkScalar sx, SkScalar sy, GrColor color,
+ GrFontScaler* scaler,
+ SkScalar textRatio, const SkMatrix& viewMatrix) {
+ if (!*strike) {
+ *strike = cache->getStrike(scaler);
+ }
+
+ GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(),
+ skGlyph.getSubXFixed(),
+ skGlyph.getSubYFixed(),
+ GrGlyph::kDistance_MaskStyle);
+ GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, scaler);
+ 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, scaler, skGlyph,
+ sx - dx, sy - dy, scale, true);
+ return true;
+}
+
void GrTextUtils::DrawTextAsPath(GrContext* context, GrDrawContext* dc,
const GrClip& clip,
const SkPaint& skPaint, const SkMatrix& viewMatrix,
« no previous file with comments | « src/gpu/text/GrTextUtils.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698