blob: eb172af1b6c941c123d516f3c14b22498a0f64fd [file] [log] [blame]
jvanverth@google.comd830d132013-11-11 20:54:09 +00001/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "GrDistanceFieldTextContext.h"
9#include "GrAtlas.h"
jvanverth8c27a182014-10-14 08:45:50 -070010#include "GrBitmapTextContext.h"
jvanverth@google.comd830d132013-11-11 20:54:09 +000011#include "GrDrawTarget.h"
commit-bot@chromium.org6c89c342014-02-14 21:48:29 +000012#include "GrDrawTargetCaps.h"
jvanverthbe03bb62015-03-10 11:53:39 -070013#include "GrFontAtlasSizes.h"
jvanverth787cdf92014-12-04 10:46:50 -080014#include "GrFontCache.h"
jvanverth@google.comd830d132013-11-11 20:54:09 +000015#include "GrFontScaler.h"
jvanverth2d2a68c2014-06-10 06:42:56 -070016#include "GrGpu.h"
jvanverth@google.comd830d132013-11-11 20:54:09 +000017#include "GrIndexBuffer.h"
egdanield58a0ba2014-06-11 10:30:05 -070018#include "GrStrokeInfo.h"
bsalomonafbf2d62014-09-30 12:18:44 -070019#include "GrTexturePriv.h"
bsalomonafbf2d62014-09-30 12:18:44 -070020
jvanverth2b9dc1d2014-10-20 06:48:59 -070021#include "SkAutoKern.h"
bsalomonafbf2d62014-09-30 12:18:44 -070022#include "SkColorFilter.h"
commit-bot@chromium.org64b08a12014-04-15 17:53:21 +000023#include "SkDistanceFieldGen.h"
commit-bot@chromium.org9f94b912014-01-30 15:22:54 +000024#include "SkDraw.h"
bsalomonafbf2d62014-09-30 12:18:44 -070025#include "SkGlyphCache.h"
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +000026#include "SkGpuDevice.h"
jvanverth@google.comd830d132013-11-11 20:54:09 +000027#include "SkPath.h"
28#include "SkRTConf.h"
29#include "SkStrokeRec.h"
30#include "effects/GrDistanceFieldTextureEffect.h"
31
jvanverthfeceba52014-07-25 19:03:34 -070032SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
33 "Dump the contents of the font cache before every purge.");
34
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +000035static const int kSmallDFFontSize = 32;
36static const int kSmallDFFontLimit = 32;
jvanverthcb251f12015-03-11 11:18:11 -070037static const int kMediumDFFontSize = 72;
38static const int kMediumDFFontLimit = 72;
39static const int kLargeDFFontSize = 162;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +000040
jvanverth73f10532014-10-23 11:57:12 -070041static const int kVerticesPerGlyph = 4;
42static const int kIndicesPerGlyph = 6;
jvanverth@google.comd830d132013-11-11 20:54:09 +000043
skia.committer@gmail.come5d70152014-01-29 07:01:48 +000044GrDistanceFieldTextContext::GrDistanceFieldTextContext(GrContext* context,
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000045 const SkDeviceProperties& properties,
46 bool enable)
Mike Klein6a25bd02014-08-29 10:03:59 -040047 : GrTextContext(context, properties) {
jvanverth4736e142014-11-07 07:12:46 -080048#if SK_FORCE_DISTANCE_FIELD_TEXT
Mike Klein6a25bd02014-08-29 10:03:59 -040049 fEnableDFRendering = true;
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000050#else
Mike Klein6a25bd02014-08-29 10:03:59 -040051 fEnableDFRendering = enable;
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000052#endif
Mike Klein6a25bd02014-08-29 10:03:59 -040053 fStrike = NULL;
54 fGammaTexture = NULL;
55
Mike Klein6a25bd02014-08-29 10:03:59 -040056 fEffectTextureUniqueID = SK_InvalidUniqueID;
57 fEffectColor = GrColor_ILLEGAL;
jvanverth6d22eca2014-10-28 11:10:48 -070058 fEffectFlags = kInvalid_DistanceFieldEffectFlag;
Mike Klein6a25bd02014-08-29 10:03:59 -040059
60 fVertices = NULL;
jvanverth73f10532014-10-23 11:57:12 -070061 fCurrVertex = 0;
62 fAllocVertexCount = 0;
63 fTotalVertexCount = 0;
64 fCurrTexture = NULL;
Mike Klein6a25bd02014-08-29 10:03:59 -040065
jvanverth1723bfc2014-07-30 09:16:33 -070066 fVertexBounds.setLargestInverted();
jvanverth@google.comd830d132013-11-11 20:54:09 +000067}
68
jvanverth8c27a182014-10-14 08:45:50 -070069GrDistanceFieldTextContext* GrDistanceFieldTextContext::Create(GrContext* context,
70 const SkDeviceProperties& props,
71 bool enable) {
72 GrDistanceFieldTextContext* textContext = SkNEW_ARGS(GrDistanceFieldTextContext,
73 (context, props, enable));
74 textContext->fFallbackTextContext = GrBitmapTextContext::Create(context, props);
75
76 return textContext;
77}
78
jvanverth@google.comd830d132013-11-11 20:54:09 +000079GrDistanceFieldTextContext::~GrDistanceFieldTextContext() {
jvanverth2d2a68c2014-06-10 06:42:56 -070080 SkSafeSetNull(fGammaTexture);
jvanverth@google.comd830d132013-11-11 20:54:09 +000081}
82
joshualitt5531d512014-12-17 15:50:11 -080083bool GrDistanceFieldTextContext::canDraw(const SkPaint& paint, const SkMatrix& viewMatrix) {
jvanverth5a3d92f2015-01-28 13:08:40 -080084 // TODO: support perspective (need getMaxScale replacement)
85 if (viewMatrix.hasPerspective()) {
86 return false;
87 }
88
89 SkScalar maxScale = viewMatrix.getMaxScale();
90 SkScalar scaledTextSize = maxScale*paint.getTextSize();
91 // Scaling up beyond 2x yields undesireable artifacts
92 if (scaledTextSize > 2*kLargeDFFontSize) {
93 return false;
94 }
95
96 if (!fEnableDFRendering && !paint.isDistanceFieldTextTEMP() &&
97 scaledTextSize < kLargeDFFontSize) {
commit-bot@chromium.orgeefd8a02014-04-08 20:14:32 +000098 return false;
99 }
100
skia.committer@gmail.come1d94432014-04-09 03:04:11 +0000101 // rasterizers and mask filters modify alpha, which doesn't
commit-bot@chromium.orgeefd8a02014-04-08 20:14:32 +0000102 // translate well to distance
103 if (paint.getRasterizer() || paint.getMaskFilter() ||
104 !fContext->getTextTarget()->caps()->shaderDerivativeSupport()) {
105 return false;
106 }
107
108 // TODO: add some stroking support
109 if (paint.getStyle() != SkPaint::kFill_Style) {
110 return false;
111 }
112
jvanverth2faa2282014-10-31 12:59:57 -0700113 return true;
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000114}
115
joshualitt570d2f82015-02-25 13:19:48 -0800116inline void GrDistanceFieldTextContext::init(GrRenderTarget* rt, const GrClip& clip,
117 const GrPaint& paint, const SkPaint& skPaint) {
118 GrTextContext::init(rt, clip, paint, skPaint);
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000119
120 fStrike = NULL;
121
joshualitt5531d512014-12-17 15:50:11 -0800122 const SkMatrix& ctm = fViewMatrix;
jvanverth9564ce62014-09-16 05:45:19 -0700123
124 // getMaxScale doesn't support perspective, so neither do we at the moment
jvanverth76ce81e2014-09-22 14:26:53 -0700125 SkASSERT(!ctm.hasPerspective());
126 SkScalar maxScale = ctm.getMaxScale();
jvanverth9564ce62014-09-16 05:45:19 -0700127 SkScalar textSize = fSkPaint.getTextSize();
jvanverth76ce81e2014-09-22 14:26:53 -0700128 SkScalar scaledTextSize = textSize;
129 // if we have non-unity scale, we need to choose our base text size
130 // based on the SkPaint's text size multiplied by the max scale factor
jvanverth9564ce62014-09-16 05:45:19 -0700131 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
132 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
jvanverth76ce81e2014-09-22 14:26:53 -0700133 scaledTextSize *= maxScale;
jvanverth9564ce62014-09-16 05:45:19 -0700134 }
135
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000136 fVertices = NULL;
jvanverth73f10532014-10-23 11:57:12 -0700137 fCurrVertex = 0;
138 fAllocVertexCount = 0;
139 fTotalVertexCount = 0;
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000140
jvanverth76ce81e2014-09-22 14:26:53 -0700141 if (scaledTextSize <= kSmallDFFontLimit) {
jvanverth9564ce62014-09-16 05:45:19 -0700142 fTextRatio = textSize / kSmallDFFontSize;
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000143 fSkPaint.setTextSize(SkIntToScalar(kSmallDFFontSize));
jvanverthada68ef2014-11-03 14:00:24 -0800144#if DEBUG_TEXT_SIZE
145 fSkPaint.setColor(SkColorSetARGB(0xFF, 0x00, 0x00, 0xFF));
146 fPaint.setColor(GrColorPackRGBA(0x00, 0x00, 0xFF, 0xFF));
147#endif
jvanverth76ce81e2014-09-22 14:26:53 -0700148 } else if (scaledTextSize <= kMediumDFFontLimit) {
jvanverth9564ce62014-09-16 05:45:19 -0700149 fTextRatio = textSize / kMediumDFFontSize;
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000150 fSkPaint.setTextSize(SkIntToScalar(kMediumDFFontSize));
jvanverthada68ef2014-11-03 14:00:24 -0800151#if DEBUG_TEXT_SIZE
152 fSkPaint.setColor(SkColorSetARGB(0xFF, 0x00, 0xFF, 0x00));
153 fPaint.setColor(GrColorPackRGBA(0x00, 0xFF, 0x00, 0xFF));
154#endif
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000155 } else {
jvanverth9564ce62014-09-16 05:45:19 -0700156 fTextRatio = textSize / kLargeDFFontSize;
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000157 fSkPaint.setTextSize(SkIntToScalar(kLargeDFFontSize));
jvanverthada68ef2014-11-03 14:00:24 -0800158#if DEBUG_TEXT_SIZE
159 fSkPaint.setColor(SkColorSetARGB(0xFF, 0xFF, 0x00, 0x00));
160 fPaint.setColor(GrColorPackRGBA(0xFF, 0x00, 0x00, 0xFF));
161#endif
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000162 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000163
commit-bot@chromium.org609ced42014-04-03 18:25:48 +0000164 fUseLCDText = fSkPaint.isLCDRenderText();
skia.committer@gmail.com221b9112014-04-04 03:04:32 +0000165
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000166 fSkPaint.setLCDRenderText(false);
167 fSkPaint.setAutohinted(false);
jvanverth2d2a68c2014-06-10 06:42:56 -0700168 fSkPaint.setHinting(SkPaint::kNormal_Hinting);
commit-bot@chromium.org0bed43c2014-03-14 21:22:38 +0000169 fSkPaint.setSubpixelText(true);
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000170}
171
jvanverth2d2a68c2014-06-10 06:42:56 -0700172static void setup_gamma_texture(GrContext* context, const SkGlyphCache* cache,
173 const SkDeviceProperties& deviceProperties,
174 GrTexture** gammaTexture) {
175 if (NULL == *gammaTexture) {
176 int width, height;
177 size_t size;
178
179#ifdef SK_GAMMA_CONTRAST
180 SkScalar contrast = SK_GAMMA_CONTRAST;
181#else
182 SkScalar contrast = 0.5f;
183#endif
reedb2d77e42014-10-14 08:26:33 -0700184 SkScalar paintGamma = deviceProperties.gamma();
185 SkScalar deviceGamma = deviceProperties.gamma();
jvanverth2d2a68c2014-06-10 06:42:56 -0700186
187 size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
188 &width, &height);
189
190 SkAutoTArray<uint8_t> data((int)size);
191 SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
192
193 // TODO: Update this to use the cache rather than directly creating a texture.
bsalomonf2703d82014-10-28 14:33:06 -0700194 GrSurfaceDesc desc;
195 desc.fFlags = kNone_GrSurfaceFlags;
jvanverth2d2a68c2014-06-10 06:42:56 -0700196 desc.fWidth = width;
197 desc.fHeight = height;
198 desc.fConfig = kAlpha_8_GrPixelConfig;
199
bsalomon5236cf42015-01-14 10:42:08 -0800200 *gammaTexture = context->getGpu()->createTexture(desc, true, NULL, 0);
jvanverth2d2a68c2014-06-10 06:42:56 -0700201 if (NULL == *gammaTexture) {
202 return;
203 }
204
bsalomon81beccc2014-10-13 12:32:55 -0700205 (*gammaTexture)->writePixels(0, 0, width, height,
206 (*gammaTexture)->config(), data.get(), 0,
207 GrContext::kDontFlush_PixelOpsFlag);
jvanverth2d2a68c2014-06-10 06:42:56 -0700208 }
209}
210
joshualitt570d2f82015-02-25 13:19:48 -0800211void GrDistanceFieldTextContext::onDrawText(GrRenderTarget* rt, const GrClip& clip,
212 const GrPaint& paint,
joshualitt25d9c152015-02-18 12:29:52 -0800213 const SkPaint& skPaint, const SkMatrix& viewMatrix,
joshualitt5531d512014-12-17 15:50:11 -0800214 const char text[], size_t byteLength,
215 SkScalar x, SkScalar y) {
jvanverthaab626c2014-10-16 08:04:39 -0700216 SkASSERT(byteLength == 0 || text != NULL);
217
jvanverth2b9dc1d2014-10-20 06:48:59 -0700218 // nothing to draw
219 if (text == NULL || byteLength == 0) {
jvanverthaab626c2014-10-16 08:04:39 -0700220 return;
221 }
222
joshualitt5531d512014-12-17 15:50:11 -0800223 fViewMatrix = viewMatrix;
jvanverth2b9dc1d2014-10-20 06:48:59 -0700224 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
225 SkAutoGlyphCache autoCache(skPaint, &fDeviceProperties, NULL);
226 SkGlyphCache* cache = autoCache.getCache();
jvanverthaab626c2014-10-16 08:04:39 -0700227
jvanverth2b9dc1d2014-10-20 06:48:59 -0700228 SkTArray<SkScalar> positions;
jvanverthaab626c2014-10-16 08:04:39 -0700229
jvanverth2b9dc1d2014-10-20 06:48:59 -0700230 const char* textPtr = text;
231 SkFixed stopX = 0;
232 SkFixed stopY = 0;
233 SkFixed origin;
234 switch (skPaint.getTextAlign()) {
235 case SkPaint::kRight_Align: origin = SK_Fixed1; break;
236 case SkPaint::kCenter_Align: origin = SK_FixedHalf; break;
237 case SkPaint::kLeft_Align: origin = 0; break;
238 default: SkFAIL("Invalid paint origin"); return;
239 }
jvanverthaab626c2014-10-16 08:04:39 -0700240
jvanverth2b9dc1d2014-10-20 06:48:59 -0700241 SkAutoKern autokern;
jvanverthaab626c2014-10-16 08:04:39 -0700242 const char* stop = text + byteLength;
jvanverth2b9dc1d2014-10-20 06:48:59 -0700243 while (textPtr < stop) {
244 // don't need x, y here, since all subpixel variants will have the
245 // same advance
246 const SkGlyph& glyph = glyphCacheProc(cache, &textPtr, 0, 0);
jvanverthaab626c2014-10-16 08:04:39 -0700247
jvanverth2b9dc1d2014-10-20 06:48:59 -0700248 SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph);
249 positions.push_back(SkFixedToScalar(stopX + SkFixedMul_portable(origin, width)));
jvanverthaab626c2014-10-16 08:04:39 -0700250
jvanverth2b9dc1d2014-10-20 06:48:59 -0700251 SkFixed height = glyph.fAdvanceY;
252 positions.push_back(SkFixedToScalar(stopY + SkFixedMul_portable(origin, height)));
jvanverthaab626c2014-10-16 08:04:39 -0700253
jvanverth2b9dc1d2014-10-20 06:48:59 -0700254 stopX += width;
255 stopY += height;
jvanverthaab626c2014-10-16 08:04:39 -0700256 }
jvanverth2b9dc1d2014-10-20 06:48:59 -0700257 SkASSERT(textPtr == stop);
jvanverthaab626c2014-10-16 08:04:39 -0700258
jvanverth2b9dc1d2014-10-20 06:48:59 -0700259 // now adjust starting point depending on alignment
260 SkScalar alignX = SkFixedToScalar(stopX);
261 SkScalar alignY = SkFixedToScalar(stopY);
262 if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
263 alignX = SkScalarHalf(alignX);
264 alignY = SkScalarHalf(alignY);
265 } else if (skPaint.getTextAlign() == SkPaint::kLeft_Align) {
266 alignX = 0;
267 alignY = 0;
jvanverthaab626c2014-10-16 08:04:39 -0700268 }
jvanverth2b9dc1d2014-10-20 06:48:59 -0700269 x -= alignX;
270 y -= alignY;
271 SkPoint offset = SkPoint::Make(x, y);
jvanverthaab626c2014-10-16 08:04:39 -0700272
joshualitt570d2f82015-02-25 13:19:48 -0800273 this->drawPosText(rt, clip, paint, skPaint, viewMatrix, text, byteLength, positions.begin(), 2,
joshualitt25d9c152015-02-18 12:29:52 -0800274 offset);
jvanverthaab626c2014-10-16 08:04:39 -0700275}
276
joshualitt570d2f82015-02-25 13:19:48 -0800277void GrDistanceFieldTextContext::onDrawPosText(GrRenderTarget* rt, const GrClip& clip,
278 const GrPaint& paint,
joshualitt25d9c152015-02-18 12:29:52 -0800279 const SkPaint& skPaint, const SkMatrix& viewMatrix,
joshualitt5531d512014-12-17 15:50:11 -0800280 const char text[], size_t byteLength,
281 const SkScalar pos[], int scalarsPerPosition,
282 const SkPoint& offset) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000283
284 SkASSERT(byteLength == 0 || text != NULL);
285 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
286
287 // nothing to draw
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000288 if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000289 return;
290 }
291
joshualitt5531d512014-12-17 15:50:11 -0800292 fViewMatrix = viewMatrix;
joshualitt570d2f82015-02-25 13:19:48 -0800293 this->init(rt, clip, paint, skPaint);
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000294
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000295 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
296
jvanverth2d2a68c2014-06-10 06:42:56 -0700297 SkAutoGlyphCacheNoGamma autoCache(fSkPaint, &fDeviceProperties, NULL);
298 SkGlyphCache* cache = autoCache.getCache();
299 GrFontScaler* fontScaler = GetGrFontScaler(cache);
300
301 setup_gamma_texture(fContext, cache, fDeviceProperties, &fGammaTexture);
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000302
jvanverth73f10532014-10-23 11:57:12 -0700303 int numGlyphs = fSkPaint.textToGlyphs(text, byteLength, NULL);
304 fTotalVertexCount = kVerticesPerGlyph*numGlyphs;
305
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000306 const char* stop = text + byteLength;
jvanverthfca302c2014-10-20 13:12:54 -0700307 SkTArray<char> fallbackTxt;
308 SkTArray<SkScalar> fallbackPos;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000309
310 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
311 while (text < stop) {
jvanverthfca302c2014-10-20 13:12:54 -0700312 const char* lastText = text;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000313 // the last 2 parameters are ignored
314 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
315
316 if (glyph.fWidth) {
fmalita05c4a432014-09-29 06:29:53 -0700317 SkScalar x = offset.x() + pos[0];
318 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000319
jvanverthfca302c2014-10-20 13:12:54 -0700320 if (!this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
321 glyph.getSubXFixed(),
322 glyph.getSubYFixed()),
djsollen058f01e2014-10-30 11:54:43 -0700323 x, y, fontScaler)) {
jvanverthfca302c2014-10-20 13:12:54 -0700324 // couldn't append, send to fallback
bsalomonef3fcd82014-12-12 08:51:38 -0800325 fallbackTxt.push_back_n(SkToInt(text-lastText), lastText);
jvanverthfca302c2014-10-20 13:12:54 -0700326 fallbackPos.push_back(pos[0]);
327 if (2 == scalarsPerPosition) {
328 fallbackPos.push_back(pos[1]);
329 }
330 }
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000331 }
332 pos += scalarsPerPosition;
333 }
334 } else {
jvanverth2b9dc1d2014-10-20 06:48:59 -0700335 SkScalar alignMul = SkPaint::kCenter_Align == fSkPaint.getTextAlign() ? SK_ScalarHalf
336 : SK_Scalar1;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000337 while (text < stop) {
jvanverthfca302c2014-10-20 13:12:54 -0700338 const char* lastText = text;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000339 // the last 2 parameters are ignored
340 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
341
342 if (glyph.fWidth) {
fmalita05c4a432014-09-29 06:29:53 -0700343 SkScalar x = offset.x() + pos[0];
344 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
skia.committer@gmail.com22e96722013-12-20 07:01:36 +0000345
jvanverth2b9dc1d2014-10-20 06:48:59 -0700346 SkScalar advanceX = SkFixedToScalar(glyph.fAdvanceX)*alignMul*fTextRatio;
347 SkScalar advanceY = SkFixedToScalar(glyph.fAdvanceY)*alignMul*fTextRatio;
348
jvanverthfca302c2014-10-20 13:12:54 -0700349 if (!this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
350 glyph.getSubXFixed(),
351 glyph.getSubYFixed()),
djsollen058f01e2014-10-30 11:54:43 -0700352 x - advanceX, y - advanceY, fontScaler)) {
jvanverthfca302c2014-10-20 13:12:54 -0700353 // couldn't append, send to fallback
bsalomonef3fcd82014-12-12 08:51:38 -0800354 fallbackTxt.push_back_n(SkToInt(text-lastText), lastText);
jvanverthfca302c2014-10-20 13:12:54 -0700355 fallbackPos.push_back(pos[0]);
356 if (2 == scalarsPerPosition) {
357 fallbackPos.push_back(pos[1]);
358 }
359 }
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000360 }
361 pos += scalarsPerPosition;
362 }
363 }
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000364
365 this->finish();
jvanverthfca302c2014-10-20 13:12:54 -0700366
367 if (fallbackTxt.count() > 0) {
joshualitt570d2f82015-02-25 13:19:48 -0800368 fFallbackTextContext->drawPosText(rt, clip, paint, skPaint, viewMatrix, fallbackTxt.begin(),
joshualitt5531d512014-12-17 15:50:11 -0800369 fallbackTxt.count(), fallbackPos.begin(),
370 scalarsPerPosition, offset);
jvanverthfca302c2014-10-20 13:12:54 -0700371 }
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000372}
jvanverth0fedb192014-10-08 09:07:27 -0700373
374static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
375 unsigned r = SkColorGetR(c);
376 unsigned g = SkColorGetG(c);
377 unsigned b = SkColorGetB(c);
378 return GrColorPackRGBA(r, g, b, 0xff);
379}
380
joshualitt9853cce2014-11-17 14:22:48 -0800381static size_t get_vertex_stride(bool useColorVerts) {
jvanverth5a105ff2015-02-18 11:36:35 -0800382 return useColorVerts ? (sizeof(SkPoint) + sizeof(GrColor) + sizeof(SkIPoint16)) :
383 (sizeof(SkPoint) + sizeof(SkIPoint16));
joshualitt9853cce2014-11-17 14:22:48 -0800384}
385
386static void* alloc_vertices(GrDrawTarget* drawTarget,
387 int numVertices,
388 bool useColorVerts) {
jvanverth73f10532014-10-23 11:57:12 -0700389 if (numVertices <= 0) {
390 return NULL;
391 }
392
jvanverth73f10532014-10-23 11:57:12 -0700393 void* vertices = NULL;
394 bool success = drawTarget->reserveVertexAndIndexSpace(numVertices,
joshualitt9853cce2014-11-17 14:22:48 -0800395 get_vertex_stride(useColorVerts),
jvanverth73f10532014-10-23 11:57:12 -0700396 0,
397 &vertices,
398 NULL);
399 GrAlwaysAssert(success);
400 return vertices;
401}
402
jvanverth0fedb192014-10-08 09:07:27 -0700403void GrDistanceFieldTextContext::setupCoverageEffect(const SkColor& filteredColor) {
jvanverthbe03bb62015-03-10 11:53:39 -0700404 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode);
jvanverth0fedb192014-10-08 09:07:27 -0700405 GrTextureParams gammaParams(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
406
407 uint32_t textureUniqueID = fCurrTexture->getUniqueID();
joshualitt5531d512014-12-17 15:50:11 -0800408 const SkMatrix& ctm = fViewMatrix;
jvanverth0fedb192014-10-08 09:07:27 -0700409
410 // set up any flags
411 uint32_t flags = 0;
412 flags |= ctm.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
413 flags |= fUseLCDText ? kUseLCD_DistanceFieldEffectFlag : 0;
414 flags |= fUseLCDText && ctm.rectStaysRect() ?
415 kRectToRect_DistanceFieldEffectFlag : 0;
reedb2d77e42014-10-14 08:26:33 -0700416 bool useBGR = SkPixelGeometryIsBGR(fDeviceProperties.pixelGeometry());
jvanverth0fedb192014-10-08 09:07:27 -0700417 flags |= fUseLCDText && useBGR ? kBGR_DistanceFieldEffectFlag : 0;
418
419 // see if we need to create a new effect
420 if (textureUniqueID != fEffectTextureUniqueID ||
421 filteredColor != fEffectColor ||
joshualitt8059eb92014-12-29 15:10:07 -0800422 flags != fEffectFlags ||
423 !fCachedGeometryProcessor->viewMatrix().cheapEqualTo(fViewMatrix)) {
joshualitt2e3b3e32014-12-09 13:31:14 -0800424 GrColor color = fPaint.getColor();
jvanverth0fedb192014-10-08 09:07:27 -0700425 if (fUseLCDText) {
426 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
joshualitt2e3b3e32014-12-09 13:31:14 -0800427 fCachedGeometryProcessor.reset(GrDistanceFieldLCDTextureEffect::Create(color,
joshualitt8059eb92014-12-29 15:10:07 -0800428 fViewMatrix,
joshualitt2e3b3e32014-12-09 13:31:14 -0800429 fCurrTexture,
jvanverth0fedb192014-10-08 09:07:27 -0700430 params,
431 fGammaTexture,
432 gammaParams,
433 colorNoPreMul,
434 flags));
435 } else {
joshualitt2dd1ae02014-12-03 06:24:10 -0800436 flags |= kColorAttr_DistanceFieldEffectFlag;
joshualitt56995b52014-12-11 15:44:02 -0800437 bool opaque = GrColorIsOpaque(color);
jvanverth0fedb192014-10-08 09:07:27 -0700438#ifdef SK_GAMMA_APPLY_TO_A8
reedb2d77e42014-10-14 08:26:33 -0700439 U8CPU lum = SkColorSpaceLuminance::computeLuminance(fDeviceProperties.gamma(),
jvanverth0fedb192014-10-08 09:07:27 -0700440 filteredColor);
joshualitt2e3b3e32014-12-09 13:31:14 -0800441 fCachedGeometryProcessor.reset(GrDistanceFieldTextureEffect::Create(color,
joshualitt8059eb92014-12-29 15:10:07 -0800442 fViewMatrix,
joshualitt2e3b3e32014-12-09 13:31:14 -0800443 fCurrTexture,
jvanverth0fedb192014-10-08 09:07:27 -0700444 params,
445 fGammaTexture,
446 gammaParams,
447 lum/255.f,
joshualitt56995b52014-12-11 15:44:02 -0800448 flags,
449 opaque));
jvanverth0fedb192014-10-08 09:07:27 -0700450#else
jvanverth5a105ff2015-02-18 11:36:35 -0800451 fCachedGeometryProcessor.reset(GrDistanceFieldTextureEffect::Create(color,
452 fViewMatrix,
453 fCurrTexture,
454 params,
455 flags,
456 opaque));
jvanverth0fedb192014-10-08 09:07:27 -0700457#endif
458 }
459 fEffectTextureUniqueID = textureUniqueID;
460 fEffectColor = filteredColor;
461 fEffectFlags = flags;
462 }
463
464}
465
jvanverth787cdf92014-12-04 10:46:50 -0800466inline bool GrDistanceFieldTextContext::uploadGlyph(GrGlyph* glyph, GrFontScaler* scaler) {
467 if (!fStrike->glyphTooLargeForAtlas(glyph)) {
468 if (fStrike->addGlyphToAtlas(glyph, scaler)) {
469 return true;
470 }
471
472 // try to clear out an unused plot before we flush
473 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
474 fStrike->addGlyphToAtlas(glyph, scaler)) {
475 return true;
476 }
477
478 if (c_DumpFontCache) {
479#ifdef SK_DEVELOPER
480 fContext->getFontCache()->dump();
481#endif
482 }
483
484 // before we purge the cache, we must flush any accumulated draws
485 this->flush();
486 fContext->flush();
487
488 // we should have an unused plot now
489 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
490 fStrike->addGlyphToAtlas(glyph, scaler)) {
491 return true;
492 }
493
494 // we should never get here
495 SkASSERT(false);
496 }
497
498 return false;
499}
500
501
jvanverthfca302c2014-10-20 13:12:54 -0700502// Returns true if this method handled the glyph, false if needs to be passed to fallback
503//
504bool GrDistanceFieldTextContext::appendGlyph(GrGlyph::PackedID packed,
djsollen058f01e2014-10-30 11:54:43 -0700505 SkScalar sx, SkScalar sy,
jvanverth0fedb192014-10-08 09:07:27 -0700506 GrFontScaler* scaler) {
507 if (NULL == fDrawTarget) {
jvanverthfca302c2014-10-20 13:12:54 -0700508 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700509 }
510
511 if (NULL == fStrike) {
512 fStrike = fContext->getFontCache()->getStrike(scaler, true);
513 }
514
515 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
516 if (NULL == glyph || glyph->fBounds.isEmpty()) {
jvanverthfca302c2014-10-20 13:12:54 -0700517 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700518 }
519
jvanverthfca302c2014-10-20 13:12:54 -0700520 // fallback to color glyph support
jvanverth294c3262014-10-10 11:36:12 -0700521 if (kA8_GrMaskFormat != glyph->fMaskFormat) {
jvanverthfca302c2014-10-20 13:12:54 -0700522 return false;
jvanverth294c3262014-10-10 11:36:12 -0700523 }
524
jvanverth2faa2282014-10-31 12:59:57 -0700525 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
526 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
527 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2*SK_DistanceFieldInset);
528 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2*SK_DistanceFieldInset);
jvanverth0fedb192014-10-08 09:07:27 -0700529
jvanverth2faa2282014-10-31 12:59:57 -0700530 SkScalar scale = fTextRatio;
531 dx *= scale;
532 dy *= scale;
533 sx += dx;
534 sy += dy;
535 width *= scale;
536 height *= scale;
537 SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height);
jvanverth0fedb192014-10-08 09:07:27 -0700538
539 // check if we clipped out
jvanverth2faa2282014-10-31 12:59:57 -0700540 SkRect dstRect;
joshualitt5531d512014-12-17 15:50:11 -0800541 const SkMatrix& ctm = fViewMatrix;
jvanverth2faa2282014-10-31 12:59:57 -0700542 (void) ctm.mapRect(&dstRect, glyphRect);
543 if (fClipRect.quickReject(SkScalarTruncToInt(dstRect.left()),
544 SkScalarTruncToInt(dstRect.top()),
545 SkScalarTruncToInt(dstRect.right()),
546 SkScalarTruncToInt(dstRect.bottom()))) {
jvanverth2faa2282014-10-31 12:59:57 -0700547 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700548 }
jvanverth2faa2282014-10-31 12:59:57 -0700549
jvanverth95f9b5f2014-12-05 07:56:53 -0800550 if (NULL == glyph->fPlot) {
551 // needs to be a separate conditional to avoid over-optimization
552 // on Nexus 7 and Nexus 10
joshualittc2625822014-12-18 16:40:54 -0800553
554 // If the glyph is too large we fall back to paths
jvanverth95f9b5f2014-12-05 07:56:53 -0800555 if (!uploadGlyph(glyph, scaler)) {
556 if (NULL == glyph->fPath) {
557 SkPath* path = SkNEW(SkPath);
558 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
559 // flag the glyph as being dead?
560 delete path;
561 return true;
562 }
563 glyph->fPath = path;
jvanverth0fedb192014-10-08 09:07:27 -0700564 }
jvanverth95f9b5f2014-12-05 07:56:53 -0800565
566 // flush any accumulated draws before drawing this glyph as a path.
567 this->flush();
568
jvanverth95f9b5f2014-12-05 07:56:53 -0800569 SkMatrix ctm;
570 ctm.setScale(fTextRatio, fTextRatio);
571 ctm.postTranslate(sx - dx, sy - dy);
joshualitt5531d512014-12-17 15:50:11 -0800572
joshualittc2625822014-12-18 16:40:54 -0800573 SkPath tmpPath(*glyph->fPath);
574 tmpPath.transform(ctm);
575
jvanverth95f9b5f2014-12-05 07:56:53 -0800576 GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle);
joshualitt570d2f82015-02-25 13:19:48 -0800577 fContext->drawPath(fRenderTarget, fClip, fPaint, fViewMatrix, tmpPath, strokeInfo);
jvanverth95f9b5f2014-12-05 07:56:53 -0800578
579 // remove this glyph from the vertices we need to allocate
580 fTotalVertexCount -= kVerticesPerGlyph;
581 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700582 }
jvanverth0fedb192014-10-08 09:07:27 -0700583 }
584
jvanverth0fedb192014-10-08 09:07:27 -0700585 SkASSERT(glyph->fPlot);
586 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
587 glyph->fPlot->setDrawToken(drawToken);
588
589 GrTexture* texture = glyph->fPlot->texture();
590 SkASSERT(texture);
591
jvanverth73f10532014-10-23 11:57:12 -0700592 if (fCurrTexture != texture || fCurrVertex + kVerticesPerGlyph > fTotalVertexCount) {
jvanverth0fedb192014-10-08 09:07:27 -0700593 this->flush();
594 fCurrTexture = texture;
595 fCurrTexture->ref();
596 }
597
598 bool useColorVerts = !fUseLCDText;
599
600 if (NULL == fVertices) {
jvanverth73f10532014-10-23 11:57:12 -0700601 int maxQuadVertices = kVerticesPerGlyph * fContext->getQuadIndexBuffer()->maxQuads();
602 fAllocVertexCount = SkMin32(fTotalVertexCount, maxQuadVertices);
joshualitt9853cce2014-11-17 14:22:48 -0800603 fVertices = alloc_vertices(fDrawTarget,
604 fAllocVertexCount,
605 useColorVerts);
jvanverth0fedb192014-10-08 09:07:27 -0700606 }
607
jvanverth2faa2282014-10-31 12:59:57 -0700608 fVertexBounds.joinNonEmptyArg(glyphRect);
jvanverth0fedb192014-10-08 09:07:27 -0700609
jvanverth5a105ff2015-02-18 11:36:35 -0800610 int u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
611 int v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
612 int u1 = u0 + glyph->fBounds.width() - 2*SK_DistanceFieldInset;
613 int v1 = v0 + glyph->fBounds.height() - 2*SK_DistanceFieldInset;
614
joshualitt9853cce2014-11-17 14:22:48 -0800615 size_t vertSize = get_vertex_stride(useColorVerts);
jvanverth5a105ff2015-02-18 11:36:35 -0800616 intptr_t vertex = reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVertex;
jvanverth0fedb192014-10-08 09:07:27 -0700617
jvanverth5a105ff2015-02-18 11:36:35 -0800618 // V0
619 SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
620 position->set(glyphRect.fLeft, glyphRect.fTop);
jvanverth059034d2015-02-17 08:39:56 -0800621 if (useColorVerts) {
jvanverth5a105ff2015-02-18 11:36:35 -0800622 SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
623 *color = fPaint.getColor();
jvanverth059034d2015-02-17 08:39:56 -0800624 }
jvanverth5a105ff2015-02-18 11:36:35 -0800625 SkIPoint16* textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize -
626 sizeof(SkIPoint16));
627 textureCoords->set(u0, v0);
628 vertex += vertSize;
629
630 // V1
631 position = reinterpret_cast<SkPoint*>(vertex);
632 position->set(glyphRect.fLeft, glyphRect.fBottom);
633 if (useColorVerts) {
634 SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
635 *color = fPaint.getColor();
636 }
637 textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize - sizeof(SkIPoint16));
638 textureCoords->set(u0, v1);
639 vertex += vertSize;
640
641 // V2
642 position = reinterpret_cast<SkPoint*>(vertex);
643 position->set(glyphRect.fRight, glyphRect.fBottom);
644 if (useColorVerts) {
645 SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
646 *color = fPaint.getColor();
647 }
648 textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize - sizeof(SkIPoint16));
649 textureCoords->set(u1, v1);
650 vertex += vertSize;
651
652 // V3
653 position = reinterpret_cast<SkPoint*>(vertex);
654 position->set(glyphRect.fRight, glyphRect.fTop);
655 if (useColorVerts) {
656 SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
657 *color = fPaint.getColor();
658 }
659 textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize - sizeof(SkIPoint16));
660 textureCoords->set(u1, v0);
jvanverth0fedb192014-10-08 09:07:27 -0700661
662 fCurrVertex += 4;
jvanverthfca302c2014-10-20 13:12:54 -0700663
664 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700665}
666
667void GrDistanceFieldTextContext::flush() {
668 if (NULL == fDrawTarget) {
669 return;
670 }
671
jvanverth0fedb192014-10-08 09:07:27 -0700672 if (fCurrVertex > 0) {
egdaniel8dd688b2015-01-22 10:16:09 -0800673 GrPipelineBuilder pipelineBuilder;
joshualitt44701df2015-02-23 14:44:57 -0800674 pipelineBuilder.setFromPaint(fPaint, fRenderTarget, fClip);
joshualitt780b11e2014-11-18 09:40:40 -0800675
jvanverth0fedb192014-10-08 09:07:27 -0700676 // setup our sampler state for our text texture/atlas
677 SkASSERT(SkIsAlign4(fCurrVertex));
678
679 // get our current color
680 SkColor filteredColor;
681 SkColorFilter* colorFilter = fSkPaint.getColorFilter();
682 if (colorFilter) {
683 filteredColor = colorFilter->filterColor(fSkPaint.getColor());
684 } else {
685 filteredColor = fSkPaint.getColor();
686 }
687 this->setupCoverageEffect(filteredColor);
688
jvanverth0fedb192014-10-08 09:07:27 -0700689 // Set draw state
690 if (fUseLCDText) {
egdaniel378092f2014-12-03 10:40:13 -0800691 // TODO: move supportsRGBCoverage check to setupCoverageEffect and only add LCD
692 // processor if the xp can support it. For now we will simply assume that if
693 // fUseLCDText is true, then we have a known color output.
egdaniel8dd688b2015-01-22 10:16:09 -0800694 const GrXPFactory* xpFactory = pipelineBuilder.getXPFactory();
695 if (!xpFactory->supportsRGBCoverage(0, kRGBA_GrColorComponentFlags)) {
tfarina38406c82014-10-31 07:11:12 -0700696 SkDebugf("LCD Text will not draw correctly.\n");
jvanverth0fedb192014-10-08 09:07:27 -0700697 }
joshualitt56995b52014-12-11 15:44:02 -0800698 SkASSERT(!fCachedGeometryProcessor->hasVertexColor());
jvanverth0fedb192014-10-08 09:07:27 -0700699 } else {
jvanverth0fedb192014-10-08 09:07:27 -0700700 // We're using per-vertex color.
joshualitt56995b52014-12-11 15:44:02 -0800701 SkASSERT(fCachedGeometryProcessor->hasVertexColor());
jvanverth0fedb192014-10-08 09:07:27 -0700702 }
jvanverth73f10532014-10-23 11:57:12 -0700703 int nGlyphs = fCurrVertex / kVerticesPerGlyph;
jvanverth0fedb192014-10-08 09:07:27 -0700704 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
egdaniel8dd688b2015-01-22 10:16:09 -0800705 fDrawTarget->drawIndexedInstances(&pipelineBuilder,
joshualitt56995b52014-12-11 15:44:02 -0800706 fCachedGeometryProcessor.get(),
joshualitt9853cce2014-11-17 14:22:48 -0800707 kTriangles_GrPrimitiveType,
jvanverth0fedb192014-10-08 09:07:27 -0700708 nGlyphs,
joshualitt9853cce2014-11-17 14:22:48 -0800709 kVerticesPerGlyph,
710 kIndicesPerGlyph,
711 &fVertexBounds);
jvanverth0fedb192014-10-08 09:07:27 -0700712 fDrawTarget->resetVertexSource();
713 fVertices = NULL;
jvanverth73f10532014-10-23 11:57:12 -0700714 fTotalVertexCount -= fCurrVertex;
jvanverth0fedb192014-10-08 09:07:27 -0700715 fCurrVertex = 0;
716 SkSafeSetNull(fCurrTexture);
717 fVertexBounds.setLargestInverted();
718 }
719}
720
721inline void GrDistanceFieldTextContext::finish() {
722 this->flush();
jvanverth73f10532014-10-23 11:57:12 -0700723 fTotalVertexCount = 0;
jvanverth0fedb192014-10-08 09:07:27 -0700724
725 GrTextContext::finish();
726}
727