blob: 47d408f7f8969270ab01beb53c7386931a82dd44 [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"
jvanverth787cdf92014-12-04 10:46:50 -080013#include "GrFontCache.h"
jvanverth@google.comd830d132013-11-11 20:54:09 +000014#include "GrFontScaler.h"
jvanverth2d2a68c2014-06-10 06:42:56 -070015#include "GrGpu.h"
jvanverth@google.comd830d132013-11-11 20:54:09 +000016#include "GrIndexBuffer.h"
egdanield58a0ba2014-06-11 10:30:05 -070017#include "GrStrokeInfo.h"
bsalomonafbf2d62014-09-30 12:18:44 -070018#include "GrTexturePriv.h"
bsalomonafbf2d62014-09-30 12:18:44 -070019
jvanverth2b9dc1d2014-10-20 06:48:59 -070020#include "SkAutoKern.h"
bsalomonafbf2d62014-09-30 12:18:44 -070021#include "SkColorFilter.h"
commit-bot@chromium.org64b08a12014-04-15 17:53:21 +000022#include "SkDistanceFieldGen.h"
commit-bot@chromium.org9f94b912014-01-30 15:22:54 +000023#include "SkDraw.h"
bsalomonafbf2d62014-09-30 12:18:44 -070024#include "SkGlyphCache.h"
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +000025#include "SkGpuDevice.h"
jvanverth@google.comd830d132013-11-11 20:54:09 +000026#include "SkPath.h"
27#include "SkRTConf.h"
28#include "SkStrokeRec.h"
29#include "effects/GrDistanceFieldTextureEffect.h"
30
jvanverthfeceba52014-07-25 19:03:34 -070031SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
32 "Dump the contents of the font cache before every purge.");
33
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +000034static const int kSmallDFFontSize = 32;
35static const int kSmallDFFontLimit = 32;
jvanverth5a3d92f2015-01-28 13:08:40 -080036static const int kMediumDFFontSize = 78;
37static const int kMediumDFFontLimit = 78;
38static const int kLargeDFFontSize = 192;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +000039
jvanverth73f10532014-10-23 11:57:12 -070040static const int kVerticesPerGlyph = 4;
41static const int kIndicesPerGlyph = 6;
jvanverth@google.comd830d132013-11-11 20:54:09 +000042
skia.committer@gmail.come5d70152014-01-29 07:01:48 +000043GrDistanceFieldTextContext::GrDistanceFieldTextContext(GrContext* context,
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000044 const SkDeviceProperties& properties,
45 bool enable)
Mike Klein6a25bd02014-08-29 10:03:59 -040046 : GrTextContext(context, properties) {
jvanverth4736e142014-11-07 07:12:46 -080047#if SK_FORCE_DISTANCE_FIELD_TEXT
Mike Klein6a25bd02014-08-29 10:03:59 -040048 fEnableDFRendering = true;
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000049#else
Mike Klein6a25bd02014-08-29 10:03:59 -040050 fEnableDFRendering = enable;
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000051#endif
Mike Klein6a25bd02014-08-29 10:03:59 -040052 fStrike = NULL;
53 fGammaTexture = NULL;
54
Mike Klein6a25bd02014-08-29 10:03:59 -040055 fEffectTextureUniqueID = SK_InvalidUniqueID;
56 fEffectColor = GrColor_ILLEGAL;
jvanverth6d22eca2014-10-28 11:10:48 -070057 fEffectFlags = kInvalid_DistanceFieldEffectFlag;
Mike Klein6a25bd02014-08-29 10:03:59 -040058
59 fVertices = NULL;
jvanverth73f10532014-10-23 11:57:12 -070060 fCurrVertex = 0;
61 fAllocVertexCount = 0;
62 fTotalVertexCount = 0;
63 fCurrTexture = NULL;
Mike Klein6a25bd02014-08-29 10:03:59 -040064
jvanverth1723bfc2014-07-30 09:16:33 -070065 fVertexBounds.setLargestInverted();
jvanverth@google.comd830d132013-11-11 20:54:09 +000066}
67
jvanverth8c27a182014-10-14 08:45:50 -070068GrDistanceFieldTextContext* GrDistanceFieldTextContext::Create(GrContext* context,
69 const SkDeviceProperties& props,
70 bool enable) {
71 GrDistanceFieldTextContext* textContext = SkNEW_ARGS(GrDistanceFieldTextContext,
72 (context, props, enable));
73 textContext->fFallbackTextContext = GrBitmapTextContext::Create(context, props);
74
75 return textContext;
76}
77
jvanverth@google.comd830d132013-11-11 20:54:09 +000078GrDistanceFieldTextContext::~GrDistanceFieldTextContext() {
jvanverth2d2a68c2014-06-10 06:42:56 -070079 SkSafeSetNull(fGammaTexture);
jvanverth@google.comd830d132013-11-11 20:54:09 +000080}
81
joshualitt5531d512014-12-17 15:50:11 -080082bool GrDistanceFieldTextContext::canDraw(const SkPaint& paint, const SkMatrix& viewMatrix) {
jvanverth5a3d92f2015-01-28 13:08:40 -080083 // TODO: support perspective (need getMaxScale replacement)
84 if (viewMatrix.hasPerspective()) {
85 return false;
86 }
87
88 SkScalar maxScale = viewMatrix.getMaxScale();
89 SkScalar scaledTextSize = maxScale*paint.getTextSize();
90 // Scaling up beyond 2x yields undesireable artifacts
91 if (scaledTextSize > 2*kLargeDFFontSize) {
92 return false;
93 }
94
95 if (!fEnableDFRendering && !paint.isDistanceFieldTextTEMP() &&
96 scaledTextSize < kLargeDFFontSize) {
commit-bot@chromium.orgeefd8a02014-04-08 20:14:32 +000097 return false;
98 }
99
skia.committer@gmail.come1d94432014-04-09 03:04:11 +0000100 // rasterizers and mask filters modify alpha, which doesn't
commit-bot@chromium.orgeefd8a02014-04-08 20:14:32 +0000101 // translate well to distance
102 if (paint.getRasterizer() || paint.getMaskFilter() ||
103 !fContext->getTextTarget()->caps()->shaderDerivativeSupport()) {
104 return false;
105 }
106
107 // TODO: add some stroking support
108 if (paint.getStyle() != SkPaint::kFill_Style) {
109 return false;
110 }
111
jvanverth2faa2282014-10-31 12:59:57 -0700112 return true;
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000113}
114
joshualitt570d2f82015-02-25 13:19:48 -0800115inline void GrDistanceFieldTextContext::init(GrRenderTarget* rt, const GrClip& clip,
116 const GrPaint& paint, const SkPaint& skPaint) {
117 GrTextContext::init(rt, clip, paint, skPaint);
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000118
119 fStrike = NULL;
120
joshualitt5531d512014-12-17 15:50:11 -0800121 const SkMatrix& ctm = fViewMatrix;
jvanverth9564ce62014-09-16 05:45:19 -0700122
123 // getMaxScale doesn't support perspective, so neither do we at the moment
jvanverth76ce81e2014-09-22 14:26:53 -0700124 SkASSERT(!ctm.hasPerspective());
125 SkScalar maxScale = ctm.getMaxScale();
jvanverth9564ce62014-09-16 05:45:19 -0700126 SkScalar textSize = fSkPaint.getTextSize();
jvanverth76ce81e2014-09-22 14:26:53 -0700127 SkScalar scaledTextSize = textSize;
128 // if we have non-unity scale, we need to choose our base text size
129 // based on the SkPaint's text size multiplied by the max scale factor
jvanverth9564ce62014-09-16 05:45:19 -0700130 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
131 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
jvanverth76ce81e2014-09-22 14:26:53 -0700132 scaledTextSize *= maxScale;
jvanverth9564ce62014-09-16 05:45:19 -0700133 }
134
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000135 fVertices = NULL;
jvanverth73f10532014-10-23 11:57:12 -0700136 fCurrVertex = 0;
137 fAllocVertexCount = 0;
138 fTotalVertexCount = 0;
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000139
jvanverth76ce81e2014-09-22 14:26:53 -0700140 if (scaledTextSize <= kSmallDFFontLimit) {
jvanverth9564ce62014-09-16 05:45:19 -0700141 fTextRatio = textSize / kSmallDFFontSize;
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000142 fSkPaint.setTextSize(SkIntToScalar(kSmallDFFontSize));
jvanverthada68ef2014-11-03 14:00:24 -0800143#if DEBUG_TEXT_SIZE
144 fSkPaint.setColor(SkColorSetARGB(0xFF, 0x00, 0x00, 0xFF));
145 fPaint.setColor(GrColorPackRGBA(0x00, 0x00, 0xFF, 0xFF));
146#endif
jvanverth76ce81e2014-09-22 14:26:53 -0700147 } else if (scaledTextSize <= kMediumDFFontLimit) {
jvanverth9564ce62014-09-16 05:45:19 -0700148 fTextRatio = textSize / kMediumDFFontSize;
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000149 fSkPaint.setTextSize(SkIntToScalar(kMediumDFFontSize));
jvanverthada68ef2014-11-03 14:00:24 -0800150#if DEBUG_TEXT_SIZE
151 fSkPaint.setColor(SkColorSetARGB(0xFF, 0x00, 0xFF, 0x00));
152 fPaint.setColor(GrColorPackRGBA(0x00, 0xFF, 0x00, 0xFF));
153#endif
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000154 } else {
jvanverth9564ce62014-09-16 05:45:19 -0700155 fTextRatio = textSize / kLargeDFFontSize;
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000156 fSkPaint.setTextSize(SkIntToScalar(kLargeDFFontSize));
jvanverthada68ef2014-11-03 14:00:24 -0800157#if DEBUG_TEXT_SIZE
158 fSkPaint.setColor(SkColorSetARGB(0xFF, 0xFF, 0x00, 0x00));
159 fPaint.setColor(GrColorPackRGBA(0xFF, 0x00, 0x00, 0xFF));
160#endif
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000161 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000162
commit-bot@chromium.org609ced42014-04-03 18:25:48 +0000163 fUseLCDText = fSkPaint.isLCDRenderText();
skia.committer@gmail.com221b9112014-04-04 03:04:32 +0000164
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000165 fSkPaint.setLCDRenderText(false);
166 fSkPaint.setAutohinted(false);
jvanverth2d2a68c2014-06-10 06:42:56 -0700167 fSkPaint.setHinting(SkPaint::kNormal_Hinting);
commit-bot@chromium.org0bed43c2014-03-14 21:22:38 +0000168 fSkPaint.setSubpixelText(true);
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000169}
170
jvanverth2d2a68c2014-06-10 06:42:56 -0700171static void setup_gamma_texture(GrContext* context, const SkGlyphCache* cache,
172 const SkDeviceProperties& deviceProperties,
173 GrTexture** gammaTexture) {
174 if (NULL == *gammaTexture) {
175 int width, height;
176 size_t size;
177
178#ifdef SK_GAMMA_CONTRAST
179 SkScalar contrast = SK_GAMMA_CONTRAST;
180#else
181 SkScalar contrast = 0.5f;
182#endif
reedb2d77e42014-10-14 08:26:33 -0700183 SkScalar paintGamma = deviceProperties.gamma();
184 SkScalar deviceGamma = deviceProperties.gamma();
jvanverth2d2a68c2014-06-10 06:42:56 -0700185
186 size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
187 &width, &height);
188
189 SkAutoTArray<uint8_t> data((int)size);
190 SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
191
192 // TODO: Update this to use the cache rather than directly creating a texture.
bsalomonf2703d82014-10-28 14:33:06 -0700193 GrSurfaceDesc desc;
194 desc.fFlags = kNone_GrSurfaceFlags;
jvanverth2d2a68c2014-06-10 06:42:56 -0700195 desc.fWidth = width;
196 desc.fHeight = height;
197 desc.fConfig = kAlpha_8_GrPixelConfig;
198
bsalomon5236cf42015-01-14 10:42:08 -0800199 *gammaTexture = context->getGpu()->createTexture(desc, true, NULL, 0);
jvanverth2d2a68c2014-06-10 06:42:56 -0700200 if (NULL == *gammaTexture) {
201 return;
202 }
203
bsalomon81beccc2014-10-13 12:32:55 -0700204 (*gammaTexture)->writePixels(0, 0, width, height,
205 (*gammaTexture)->config(), data.get(), 0,
206 GrContext::kDontFlush_PixelOpsFlag);
jvanverth2d2a68c2014-06-10 06:42:56 -0700207 }
208}
209
joshualitt570d2f82015-02-25 13:19:48 -0800210void GrDistanceFieldTextContext::onDrawText(GrRenderTarget* rt, const GrClip& clip,
211 const GrPaint& paint,
joshualitt25d9c152015-02-18 12:29:52 -0800212 const SkPaint& skPaint, const SkMatrix& viewMatrix,
joshualitt5531d512014-12-17 15:50:11 -0800213 const char text[], size_t byteLength,
214 SkScalar x, SkScalar y) {
jvanverthaab626c2014-10-16 08:04:39 -0700215 SkASSERT(byteLength == 0 || text != NULL);
216
jvanverth2b9dc1d2014-10-20 06:48:59 -0700217 // nothing to draw
218 if (text == NULL || byteLength == 0) {
jvanverthaab626c2014-10-16 08:04:39 -0700219 return;
220 }
221
joshualitt5531d512014-12-17 15:50:11 -0800222 fViewMatrix = viewMatrix;
jvanverth2b9dc1d2014-10-20 06:48:59 -0700223 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
224 SkAutoGlyphCache autoCache(skPaint, &fDeviceProperties, NULL);
225 SkGlyphCache* cache = autoCache.getCache();
jvanverthaab626c2014-10-16 08:04:39 -0700226
jvanverth2b9dc1d2014-10-20 06:48:59 -0700227 SkTArray<SkScalar> positions;
jvanverthaab626c2014-10-16 08:04:39 -0700228
jvanverth2b9dc1d2014-10-20 06:48:59 -0700229 const char* textPtr = text;
230 SkFixed stopX = 0;
231 SkFixed stopY = 0;
232 SkFixed origin;
233 switch (skPaint.getTextAlign()) {
234 case SkPaint::kRight_Align: origin = SK_Fixed1; break;
235 case SkPaint::kCenter_Align: origin = SK_FixedHalf; break;
236 case SkPaint::kLeft_Align: origin = 0; break;
237 default: SkFAIL("Invalid paint origin"); return;
238 }
jvanverthaab626c2014-10-16 08:04:39 -0700239
jvanverth2b9dc1d2014-10-20 06:48:59 -0700240 SkAutoKern autokern;
jvanverthaab626c2014-10-16 08:04:39 -0700241 const char* stop = text + byteLength;
jvanverth2b9dc1d2014-10-20 06:48:59 -0700242 while (textPtr < stop) {
243 // don't need x, y here, since all subpixel variants will have the
244 // same advance
245 const SkGlyph& glyph = glyphCacheProc(cache, &textPtr, 0, 0);
jvanverthaab626c2014-10-16 08:04:39 -0700246
jvanverth2b9dc1d2014-10-20 06:48:59 -0700247 SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph);
248 positions.push_back(SkFixedToScalar(stopX + SkFixedMul_portable(origin, width)));
jvanverthaab626c2014-10-16 08:04:39 -0700249
jvanverth2b9dc1d2014-10-20 06:48:59 -0700250 SkFixed height = glyph.fAdvanceY;
251 positions.push_back(SkFixedToScalar(stopY + SkFixedMul_portable(origin, height)));
jvanverthaab626c2014-10-16 08:04:39 -0700252
jvanverth2b9dc1d2014-10-20 06:48:59 -0700253 stopX += width;
254 stopY += height;
jvanverthaab626c2014-10-16 08:04:39 -0700255 }
jvanverth2b9dc1d2014-10-20 06:48:59 -0700256 SkASSERT(textPtr == stop);
jvanverthaab626c2014-10-16 08:04:39 -0700257
jvanverth2b9dc1d2014-10-20 06:48:59 -0700258 // now adjust starting point depending on alignment
259 SkScalar alignX = SkFixedToScalar(stopX);
260 SkScalar alignY = SkFixedToScalar(stopY);
261 if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
262 alignX = SkScalarHalf(alignX);
263 alignY = SkScalarHalf(alignY);
264 } else if (skPaint.getTextAlign() == SkPaint::kLeft_Align) {
265 alignX = 0;
266 alignY = 0;
jvanverthaab626c2014-10-16 08:04:39 -0700267 }
jvanverth2b9dc1d2014-10-20 06:48:59 -0700268 x -= alignX;
269 y -= alignY;
270 SkPoint offset = SkPoint::Make(x, y);
jvanverthaab626c2014-10-16 08:04:39 -0700271
joshualitt570d2f82015-02-25 13:19:48 -0800272 this->drawPosText(rt, clip, paint, skPaint, viewMatrix, text, byteLength, positions.begin(), 2,
joshualitt25d9c152015-02-18 12:29:52 -0800273 offset);
jvanverthaab626c2014-10-16 08:04:39 -0700274}
275
joshualitt570d2f82015-02-25 13:19:48 -0800276void GrDistanceFieldTextContext::onDrawPosText(GrRenderTarget* rt, const GrClip& clip,
277 const GrPaint& paint,
joshualitt25d9c152015-02-18 12:29:52 -0800278 const SkPaint& skPaint, const SkMatrix& viewMatrix,
joshualitt5531d512014-12-17 15:50:11 -0800279 const char text[], size_t byteLength,
280 const SkScalar pos[], int scalarsPerPosition,
281 const SkPoint& offset) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000282
283 SkASSERT(byteLength == 0 || text != NULL);
284 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
285
286 // nothing to draw
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000287 if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000288 return;
289 }
290
joshualitt5531d512014-12-17 15:50:11 -0800291 fViewMatrix = viewMatrix;
joshualitt570d2f82015-02-25 13:19:48 -0800292 this->init(rt, clip, paint, skPaint);
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000293
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000294 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
295
jvanverth2d2a68c2014-06-10 06:42:56 -0700296 SkAutoGlyphCacheNoGamma autoCache(fSkPaint, &fDeviceProperties, NULL);
297 SkGlyphCache* cache = autoCache.getCache();
298 GrFontScaler* fontScaler = GetGrFontScaler(cache);
299
300 setup_gamma_texture(fContext, cache, fDeviceProperties, &fGammaTexture);
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000301
jvanverth73f10532014-10-23 11:57:12 -0700302 int numGlyphs = fSkPaint.textToGlyphs(text, byteLength, NULL);
303 fTotalVertexCount = kVerticesPerGlyph*numGlyphs;
304
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000305 const char* stop = text + byteLength;
jvanverthfca302c2014-10-20 13:12:54 -0700306 SkTArray<char> fallbackTxt;
307 SkTArray<SkScalar> fallbackPos;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000308
309 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
310 while (text < stop) {
jvanverthfca302c2014-10-20 13:12:54 -0700311 const char* lastText = text;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000312 // the last 2 parameters are ignored
313 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
314
315 if (glyph.fWidth) {
fmalita05c4a432014-09-29 06:29:53 -0700316 SkScalar x = offset.x() + pos[0];
317 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000318
jvanverthfca302c2014-10-20 13:12:54 -0700319 if (!this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
320 glyph.getSubXFixed(),
321 glyph.getSubYFixed()),
djsollen058f01e2014-10-30 11:54:43 -0700322 x, y, fontScaler)) {
jvanverthfca302c2014-10-20 13:12:54 -0700323 // couldn't append, send to fallback
bsalomonef3fcd82014-12-12 08:51:38 -0800324 fallbackTxt.push_back_n(SkToInt(text-lastText), lastText);
jvanverthfca302c2014-10-20 13:12:54 -0700325 fallbackPos.push_back(pos[0]);
326 if (2 == scalarsPerPosition) {
327 fallbackPos.push_back(pos[1]);
328 }
329 }
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000330 }
331 pos += scalarsPerPosition;
332 }
333 } else {
jvanverth2b9dc1d2014-10-20 06:48:59 -0700334 SkScalar alignMul = SkPaint::kCenter_Align == fSkPaint.getTextAlign() ? SK_ScalarHalf
335 : SK_Scalar1;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000336 while (text < stop) {
jvanverthfca302c2014-10-20 13:12:54 -0700337 const char* lastText = text;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000338 // the last 2 parameters are ignored
339 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
340
341 if (glyph.fWidth) {
fmalita05c4a432014-09-29 06:29:53 -0700342 SkScalar x = offset.x() + pos[0];
343 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
skia.committer@gmail.com22e96722013-12-20 07:01:36 +0000344
jvanverth2b9dc1d2014-10-20 06:48:59 -0700345 SkScalar advanceX = SkFixedToScalar(glyph.fAdvanceX)*alignMul*fTextRatio;
346 SkScalar advanceY = SkFixedToScalar(glyph.fAdvanceY)*alignMul*fTextRatio;
347
jvanverthfca302c2014-10-20 13:12:54 -0700348 if (!this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
349 glyph.getSubXFixed(),
350 glyph.getSubYFixed()),
djsollen058f01e2014-10-30 11:54:43 -0700351 x - advanceX, y - advanceY, fontScaler)) {
jvanverthfca302c2014-10-20 13:12:54 -0700352 // couldn't append, send to fallback
bsalomonef3fcd82014-12-12 08:51:38 -0800353 fallbackTxt.push_back_n(SkToInt(text-lastText), lastText);
jvanverthfca302c2014-10-20 13:12:54 -0700354 fallbackPos.push_back(pos[0]);
355 if (2 == scalarsPerPosition) {
356 fallbackPos.push_back(pos[1]);
357 }
358 }
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000359 }
360 pos += scalarsPerPosition;
361 }
362 }
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000363
364 this->finish();
jvanverthfca302c2014-10-20 13:12:54 -0700365
366 if (fallbackTxt.count() > 0) {
joshualitt570d2f82015-02-25 13:19:48 -0800367 fFallbackTextContext->drawPosText(rt, clip, paint, skPaint, viewMatrix, fallbackTxt.begin(),
joshualitt5531d512014-12-17 15:50:11 -0800368 fallbackTxt.count(), fallbackPos.begin(),
369 scalarsPerPosition, offset);
jvanverthfca302c2014-10-20 13:12:54 -0700370 }
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000371}
jvanverth0fedb192014-10-08 09:07:27 -0700372
373static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
374 unsigned r = SkColorGetR(c);
375 unsigned g = SkColorGetG(c);
376 unsigned b = SkColorGetB(c);
377 return GrColorPackRGBA(r, g, b, 0xff);
378}
379
joshualitt9853cce2014-11-17 14:22:48 -0800380static size_t get_vertex_stride(bool useColorVerts) {
jvanverth5a105ff2015-02-18 11:36:35 -0800381 return useColorVerts ? (sizeof(SkPoint) + sizeof(GrColor) + sizeof(SkIPoint16)) :
382 (sizeof(SkPoint) + sizeof(SkIPoint16));
joshualitt9853cce2014-11-17 14:22:48 -0800383}
384
385static void* alloc_vertices(GrDrawTarget* drawTarget,
386 int numVertices,
387 bool useColorVerts) {
jvanverth73f10532014-10-23 11:57:12 -0700388 if (numVertices <= 0) {
389 return NULL;
390 }
391
jvanverth73f10532014-10-23 11:57:12 -0700392 void* vertices = NULL;
393 bool success = drawTarget->reserveVertexAndIndexSpace(numVertices,
joshualitt9853cce2014-11-17 14:22:48 -0800394 get_vertex_stride(useColorVerts),
jvanverth73f10532014-10-23 11:57:12 -0700395 0,
396 &vertices,
397 NULL);
398 GrAlwaysAssert(success);
399 return vertices;
400}
401
jvanverth0fedb192014-10-08 09:07:27 -0700402void GrDistanceFieldTextContext::setupCoverageEffect(const SkColor& filteredColor) {
403 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode);
404 GrTextureParams gammaParams(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
405
406 uint32_t textureUniqueID = fCurrTexture->getUniqueID();
joshualitt5531d512014-12-17 15:50:11 -0800407 const SkMatrix& ctm = fViewMatrix;
jvanverth0fedb192014-10-08 09:07:27 -0700408
409 // set up any flags
410 uint32_t flags = 0;
411 flags |= ctm.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
412 flags |= fUseLCDText ? kUseLCD_DistanceFieldEffectFlag : 0;
413 flags |= fUseLCDText && ctm.rectStaysRect() ?
414 kRectToRect_DistanceFieldEffectFlag : 0;
reedb2d77e42014-10-14 08:26:33 -0700415 bool useBGR = SkPixelGeometryIsBGR(fDeviceProperties.pixelGeometry());
jvanverth0fedb192014-10-08 09:07:27 -0700416 flags |= fUseLCDText && useBGR ? kBGR_DistanceFieldEffectFlag : 0;
417
418 // see if we need to create a new effect
419 if (textureUniqueID != fEffectTextureUniqueID ||
420 filteredColor != fEffectColor ||
joshualitt8059eb92014-12-29 15:10:07 -0800421 flags != fEffectFlags ||
422 !fCachedGeometryProcessor->viewMatrix().cheapEqualTo(fViewMatrix)) {
joshualitt2e3b3e32014-12-09 13:31:14 -0800423 GrColor color = fPaint.getColor();
jvanverth0fedb192014-10-08 09:07:27 -0700424 if (fUseLCDText) {
425 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
joshualitt2e3b3e32014-12-09 13:31:14 -0800426 fCachedGeometryProcessor.reset(GrDistanceFieldLCDTextureEffect::Create(color,
joshualitt8059eb92014-12-29 15:10:07 -0800427 fViewMatrix,
joshualitt2e3b3e32014-12-09 13:31:14 -0800428 fCurrTexture,
jvanverth0fedb192014-10-08 09:07:27 -0700429 params,
430 fGammaTexture,
431 gammaParams,
432 colorNoPreMul,
433 flags));
434 } else {
joshualitt2dd1ae02014-12-03 06:24:10 -0800435 flags |= kColorAttr_DistanceFieldEffectFlag;
joshualitt56995b52014-12-11 15:44:02 -0800436 bool opaque = GrColorIsOpaque(color);
jvanverth0fedb192014-10-08 09:07:27 -0700437#ifdef SK_GAMMA_APPLY_TO_A8
reedb2d77e42014-10-14 08:26:33 -0700438 U8CPU lum = SkColorSpaceLuminance::computeLuminance(fDeviceProperties.gamma(),
jvanverth0fedb192014-10-08 09:07:27 -0700439 filteredColor);
joshualitt2e3b3e32014-12-09 13:31:14 -0800440 fCachedGeometryProcessor.reset(GrDistanceFieldTextureEffect::Create(color,
joshualitt8059eb92014-12-29 15:10:07 -0800441 fViewMatrix,
joshualitt2e3b3e32014-12-09 13:31:14 -0800442 fCurrTexture,
jvanverth0fedb192014-10-08 09:07:27 -0700443 params,
444 fGammaTexture,
445 gammaParams,
446 lum/255.f,
joshualitt56995b52014-12-11 15:44:02 -0800447 flags,
448 opaque));
jvanverth0fedb192014-10-08 09:07:27 -0700449#else
jvanverth5a105ff2015-02-18 11:36:35 -0800450 fCachedGeometryProcessor.reset(GrDistanceFieldTextureEffect::Create(color,
451 fViewMatrix,
452 fCurrTexture,
453 params,
454 flags,
455 opaque));
jvanverth0fedb192014-10-08 09:07:27 -0700456#endif
457 }
458 fEffectTextureUniqueID = textureUniqueID;
459 fEffectColor = filteredColor;
460 fEffectFlags = flags;
461 }
462
463}
464
jvanverth787cdf92014-12-04 10:46:50 -0800465inline bool GrDistanceFieldTextContext::uploadGlyph(GrGlyph* glyph, GrFontScaler* scaler) {
466 if (!fStrike->glyphTooLargeForAtlas(glyph)) {
467 if (fStrike->addGlyphToAtlas(glyph, scaler)) {
468 return true;
469 }
470
471 // try to clear out an unused plot before we flush
472 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
473 fStrike->addGlyphToAtlas(glyph, scaler)) {
474 return true;
475 }
476
477 if (c_DumpFontCache) {
478#ifdef SK_DEVELOPER
479 fContext->getFontCache()->dump();
480#endif
481 }
482
483 // before we purge the cache, we must flush any accumulated draws
484 this->flush();
485 fContext->flush();
486
487 // we should have an unused plot now
488 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
489 fStrike->addGlyphToAtlas(glyph, scaler)) {
490 return true;
491 }
492
493 // we should never get here
494 SkASSERT(false);
495 }
496
497 return false;
498}
499
500
jvanverthfca302c2014-10-20 13:12:54 -0700501// Returns true if this method handled the glyph, false if needs to be passed to fallback
502//
503bool GrDistanceFieldTextContext::appendGlyph(GrGlyph::PackedID packed,
djsollen058f01e2014-10-30 11:54:43 -0700504 SkScalar sx, SkScalar sy,
jvanverth0fedb192014-10-08 09:07:27 -0700505 GrFontScaler* scaler) {
506 if (NULL == fDrawTarget) {
jvanverthfca302c2014-10-20 13:12:54 -0700507 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700508 }
509
510 if (NULL == fStrike) {
511 fStrike = fContext->getFontCache()->getStrike(scaler, true);
512 }
513
514 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
515 if (NULL == glyph || glyph->fBounds.isEmpty()) {
jvanverthfca302c2014-10-20 13:12:54 -0700516 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700517 }
518
jvanverthfca302c2014-10-20 13:12:54 -0700519 // fallback to color glyph support
jvanverth294c3262014-10-10 11:36:12 -0700520 if (kA8_GrMaskFormat != glyph->fMaskFormat) {
jvanverthfca302c2014-10-20 13:12:54 -0700521 return false;
jvanverth294c3262014-10-10 11:36:12 -0700522 }
523
jvanverth2faa2282014-10-31 12:59:57 -0700524 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
525 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
526 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2*SK_DistanceFieldInset);
527 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2*SK_DistanceFieldInset);
jvanverth0fedb192014-10-08 09:07:27 -0700528
jvanverth2faa2282014-10-31 12:59:57 -0700529 SkScalar scale = fTextRatio;
530 dx *= scale;
531 dy *= scale;
532 sx += dx;
533 sy += dy;
534 width *= scale;
535 height *= scale;
536 SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height);
jvanverth0fedb192014-10-08 09:07:27 -0700537
538 // check if we clipped out
jvanverth2faa2282014-10-31 12:59:57 -0700539 SkRect dstRect;
joshualitt5531d512014-12-17 15:50:11 -0800540 const SkMatrix& ctm = fViewMatrix;
jvanverth2faa2282014-10-31 12:59:57 -0700541 (void) ctm.mapRect(&dstRect, glyphRect);
542 if (fClipRect.quickReject(SkScalarTruncToInt(dstRect.left()),
543 SkScalarTruncToInt(dstRect.top()),
544 SkScalarTruncToInt(dstRect.right()),
545 SkScalarTruncToInt(dstRect.bottom()))) {
jvanverth2faa2282014-10-31 12:59:57 -0700546 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700547 }
jvanverth2faa2282014-10-31 12:59:57 -0700548
jvanverth95f9b5f2014-12-05 07:56:53 -0800549 if (NULL == glyph->fPlot) {
550 // needs to be a separate conditional to avoid over-optimization
551 // on Nexus 7 and Nexus 10
joshualittc2625822014-12-18 16:40:54 -0800552
553 // If the glyph is too large we fall back to paths
jvanverth95f9b5f2014-12-05 07:56:53 -0800554 if (!uploadGlyph(glyph, scaler)) {
555 if (NULL == glyph->fPath) {
556 SkPath* path = SkNEW(SkPath);
557 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
558 // flag the glyph as being dead?
559 delete path;
560 return true;
561 }
562 glyph->fPath = path;
jvanverth0fedb192014-10-08 09:07:27 -0700563 }
jvanverth95f9b5f2014-12-05 07:56:53 -0800564
565 // flush any accumulated draws before drawing this glyph as a path.
566 this->flush();
567
jvanverth95f9b5f2014-12-05 07:56:53 -0800568 SkMatrix ctm;
569 ctm.setScale(fTextRatio, fTextRatio);
570 ctm.postTranslate(sx - dx, sy - dy);
joshualitt5531d512014-12-17 15:50:11 -0800571
joshualittc2625822014-12-18 16:40:54 -0800572 SkPath tmpPath(*glyph->fPath);
573 tmpPath.transform(ctm);
574
jvanverth95f9b5f2014-12-05 07:56:53 -0800575 GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle);
joshualitt570d2f82015-02-25 13:19:48 -0800576 fContext->drawPath(fRenderTarget, fClip, fPaint, fViewMatrix, tmpPath, strokeInfo);
jvanverth95f9b5f2014-12-05 07:56:53 -0800577
578 // remove this glyph from the vertices we need to allocate
579 fTotalVertexCount -= kVerticesPerGlyph;
580 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700581 }
jvanverth0fedb192014-10-08 09:07:27 -0700582 }
583
jvanverth0fedb192014-10-08 09:07:27 -0700584 SkASSERT(glyph->fPlot);
585 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
586 glyph->fPlot->setDrawToken(drawToken);
587
588 GrTexture* texture = glyph->fPlot->texture();
589 SkASSERT(texture);
590
jvanverth73f10532014-10-23 11:57:12 -0700591 if (fCurrTexture != texture || fCurrVertex + kVerticesPerGlyph > fTotalVertexCount) {
jvanverth0fedb192014-10-08 09:07:27 -0700592 this->flush();
593 fCurrTexture = texture;
594 fCurrTexture->ref();
595 }
596
597 bool useColorVerts = !fUseLCDText;
598
599 if (NULL == fVertices) {
jvanverth73f10532014-10-23 11:57:12 -0700600 int maxQuadVertices = kVerticesPerGlyph * fContext->getQuadIndexBuffer()->maxQuads();
601 fAllocVertexCount = SkMin32(fTotalVertexCount, maxQuadVertices);
joshualitt9853cce2014-11-17 14:22:48 -0800602 fVertices = alloc_vertices(fDrawTarget,
603 fAllocVertexCount,
604 useColorVerts);
jvanverth0fedb192014-10-08 09:07:27 -0700605 }
606
jvanverth2faa2282014-10-31 12:59:57 -0700607 fVertexBounds.joinNonEmptyArg(glyphRect);
jvanverth0fedb192014-10-08 09:07:27 -0700608
jvanverth5a105ff2015-02-18 11:36:35 -0800609 int u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
610 int v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
611 int u1 = u0 + glyph->fBounds.width() - 2*SK_DistanceFieldInset;
612 int v1 = v0 + glyph->fBounds.height() - 2*SK_DistanceFieldInset;
613
joshualitt9853cce2014-11-17 14:22:48 -0800614 size_t vertSize = get_vertex_stride(useColorVerts);
jvanverth5a105ff2015-02-18 11:36:35 -0800615 intptr_t vertex = reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVertex;
jvanverth0fedb192014-10-08 09:07:27 -0700616
jvanverth5a105ff2015-02-18 11:36:35 -0800617 // V0
618 SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
619 position->set(glyphRect.fLeft, glyphRect.fTop);
jvanverth059034d2015-02-17 08:39:56 -0800620 if (useColorVerts) {
jvanverth5a105ff2015-02-18 11:36:35 -0800621 SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
622 *color = fPaint.getColor();
jvanverth059034d2015-02-17 08:39:56 -0800623 }
jvanverth5a105ff2015-02-18 11:36:35 -0800624 SkIPoint16* textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize -
625 sizeof(SkIPoint16));
626 textureCoords->set(u0, v0);
627 vertex += vertSize;
628
629 // V1
630 position = reinterpret_cast<SkPoint*>(vertex);
631 position->set(glyphRect.fLeft, glyphRect.fBottom);
632 if (useColorVerts) {
633 SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
634 *color = fPaint.getColor();
635 }
636 textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize - sizeof(SkIPoint16));
637 textureCoords->set(u0, v1);
638 vertex += vertSize;
639
640 // V2
641 position = reinterpret_cast<SkPoint*>(vertex);
642 position->set(glyphRect.fRight, glyphRect.fBottom);
643 if (useColorVerts) {
644 SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
645 *color = fPaint.getColor();
646 }
647 textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize - sizeof(SkIPoint16));
648 textureCoords->set(u1, v1);
649 vertex += vertSize;
650
651 // V3
652 position = reinterpret_cast<SkPoint*>(vertex);
653 position->set(glyphRect.fRight, glyphRect.fTop);
654 if (useColorVerts) {
655 SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
656 *color = fPaint.getColor();
657 }
658 textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize - sizeof(SkIPoint16));
659 textureCoords->set(u1, v0);
jvanverth0fedb192014-10-08 09:07:27 -0700660
661 fCurrVertex += 4;
jvanverthfca302c2014-10-20 13:12:54 -0700662
663 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700664}
665
666void GrDistanceFieldTextContext::flush() {
667 if (NULL == fDrawTarget) {
668 return;
669 }
670
jvanverth0fedb192014-10-08 09:07:27 -0700671 if (fCurrVertex > 0) {
egdaniel8dd688b2015-01-22 10:16:09 -0800672 GrPipelineBuilder pipelineBuilder;
joshualitt44701df2015-02-23 14:44:57 -0800673 pipelineBuilder.setFromPaint(fPaint, fRenderTarget, fClip);
joshualitt780b11e2014-11-18 09:40:40 -0800674
jvanverth0fedb192014-10-08 09:07:27 -0700675 // setup our sampler state for our text texture/atlas
676 SkASSERT(SkIsAlign4(fCurrVertex));
677
678 // get our current color
679 SkColor filteredColor;
680 SkColorFilter* colorFilter = fSkPaint.getColorFilter();
681 if (colorFilter) {
682 filteredColor = colorFilter->filterColor(fSkPaint.getColor());
683 } else {
684 filteredColor = fSkPaint.getColor();
685 }
686 this->setupCoverageEffect(filteredColor);
687
jvanverth0fedb192014-10-08 09:07:27 -0700688 // Set draw state
689 if (fUseLCDText) {
egdaniel378092f2014-12-03 10:40:13 -0800690 // TODO: move supportsRGBCoverage check to setupCoverageEffect and only add LCD
691 // processor if the xp can support it. For now we will simply assume that if
692 // fUseLCDText is true, then we have a known color output.
egdaniel8dd688b2015-01-22 10:16:09 -0800693 const GrXPFactory* xpFactory = pipelineBuilder.getXPFactory();
694 if (!xpFactory->supportsRGBCoverage(0, kRGBA_GrColorComponentFlags)) {
tfarina38406c82014-10-31 07:11:12 -0700695 SkDebugf("LCD Text will not draw correctly.\n");
jvanverth0fedb192014-10-08 09:07:27 -0700696 }
joshualitt56995b52014-12-11 15:44:02 -0800697 SkASSERT(!fCachedGeometryProcessor->hasVertexColor());
jvanverth0fedb192014-10-08 09:07:27 -0700698 } else {
jvanverth0fedb192014-10-08 09:07:27 -0700699 // We're using per-vertex color.
joshualitt56995b52014-12-11 15:44:02 -0800700 SkASSERT(fCachedGeometryProcessor->hasVertexColor());
jvanverth0fedb192014-10-08 09:07:27 -0700701 }
jvanverth73f10532014-10-23 11:57:12 -0700702 int nGlyphs = fCurrVertex / kVerticesPerGlyph;
jvanverth0fedb192014-10-08 09:07:27 -0700703 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
egdaniel8dd688b2015-01-22 10:16:09 -0800704 fDrawTarget->drawIndexedInstances(&pipelineBuilder,
joshualitt56995b52014-12-11 15:44:02 -0800705 fCachedGeometryProcessor.get(),
joshualitt9853cce2014-11-17 14:22:48 -0800706 kTriangles_GrPrimitiveType,
jvanverth0fedb192014-10-08 09:07:27 -0700707 nGlyphs,
joshualitt9853cce2014-11-17 14:22:48 -0800708 kVerticesPerGlyph,
709 kIndicesPerGlyph,
710 &fVertexBounds);
jvanverth0fedb192014-10-08 09:07:27 -0700711 fDrawTarget->resetVertexSource();
712 fVertices = NULL;
jvanverth73f10532014-10-23 11:57:12 -0700713 fTotalVertexCount -= fCurrVertex;
jvanverth0fedb192014-10-08 09:07:27 -0700714 fCurrVertex = 0;
715 SkSafeSetNull(fCurrTexture);
716 fVertexBounds.setLargestInverted();
717 }
718}
719
720inline void GrDistanceFieldTextContext::finish() {
721 this->flush();
jvanverth73f10532014-10-23 11:57:12 -0700722 fTotalVertexCount = 0;
jvanverth0fedb192014-10-08 09:07:27 -0700723
724 GrTextContext::finish();
725}
726