blob: 183b83c3e6e7ef8b5baf478663cc5824276e3826 [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"
10#include "GrDrawTarget.h"
commit-bot@chromium.org6c89c342014-02-14 21:48:29 +000011#include "GrDrawTargetCaps.h"
jvanverth@google.comd830d132013-11-11 20:54:09 +000012#include "GrFontScaler.h"
jvanverth2d2a68c2014-06-10 06:42:56 -070013#include "GrGpu.h"
jvanverth@google.comd830d132013-11-11 20:54:09 +000014#include "GrIndexBuffer.h"
egdanield58a0ba2014-06-11 10:30:05 -070015#include "GrStrokeInfo.h"
bsalomonafbf2d62014-09-30 12:18:44 -070016#include "GrTexturePriv.h"
jvanverth@google.comd830d132013-11-11 20:54:09 +000017#include "GrTextStrike.h"
18#include "GrTextStrike_impl.h"
bsalomonafbf2d62014-09-30 12:18:44 -070019
20#include "SkColorFilter.h"
commit-bot@chromium.org64b08a12014-04-15 17:53:21 +000021#include "SkDistanceFieldGen.h"
commit-bot@chromium.org9f94b912014-01-30 15:22:54 +000022#include "SkDraw.h"
bsalomonafbf2d62014-09-30 12:18:44 -070023#include "SkGlyphCache.h"
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +000024#include "SkGpuDevice.h"
jvanverth@google.comd830d132013-11-11 20:54:09 +000025#include "SkPath.h"
26#include "SkRTConf.h"
27#include "SkStrokeRec.h"
28#include "effects/GrDistanceFieldTextureEffect.h"
29
jvanverthfeceba52014-07-25 19:03:34 -070030SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
31 "Dump the contents of the font cache before every purge.");
32
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +000033static const int kSmallDFFontSize = 32;
34static const int kSmallDFFontLimit = 32;
35static const int kMediumDFFontSize = 64;
36static const int kMediumDFFontLimit = 64;
37static const int kLargeDFFontSize = 128;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +000038
jvanverthfeceba52014-07-25 19:03:34 -070039namespace {
40// position + texture coord
41extern const GrVertexAttrib gTextVertexAttribs[] = {
42 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
joshualittb0a8a372014-09-23 09:50:21 -070043 {kVec2f_GrVertexAttribType, sizeof(SkPoint) , kGeometryProcessor_GrVertexAttribBinding}
jvanverthfeceba52014-07-25 19:03:34 -070044};
45
egdaniel7b3d5ee2014-08-28 05:41:14 -070046static const size_t kTextVASize = 2 * sizeof(SkPoint);
47
jvanverthfeceba52014-07-25 19:03:34 -070048// position + color + texture coord
49extern const GrVertexAttrib gTextVertexWithColorAttribs[] = {
50 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
51 {kVec4ub_GrVertexAttribType, sizeof(SkPoint), kColor_GrVertexAttribBinding},
joshualittb0a8a372014-09-23 09:50:21 -070052 {kVec2f_GrVertexAttribType, sizeof(SkPoint) + sizeof(GrColor), kGeometryProcessor_GrVertexAttribBinding}
jvanverthfeceba52014-07-25 19:03:34 -070053};
54
egdaniel7b3d5ee2014-08-28 05:41:14 -070055static const size_t kTextVAColorSize = 2 * sizeof(SkPoint) + sizeof(GrColor);
56
jvanverthfeceba52014-07-25 19:03:34 -070057};
jvanverth@google.comd830d132013-11-11 20:54:09 +000058
skia.committer@gmail.come5d70152014-01-29 07:01:48 +000059GrDistanceFieldTextContext::GrDistanceFieldTextContext(GrContext* context,
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000060 const SkDeviceProperties& properties,
61 bool enable)
Mike Klein6a25bd02014-08-29 10:03:59 -040062 : GrTextContext(context, properties) {
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000063#if SK_FORCE_DISTANCEFIELD_FONTS
Mike Klein6a25bd02014-08-29 10:03:59 -040064 fEnableDFRendering = true;
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000065#else
Mike Klein6a25bd02014-08-29 10:03:59 -040066 fEnableDFRendering = enable;
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000067#endif
Mike Klein6a25bd02014-08-29 10:03:59 -040068 fStrike = NULL;
69 fGammaTexture = NULL;
70
71 fCurrTexture = NULL;
72 fCurrVertex = 0;
73 fEffectTextureUniqueID = SK_InvalidUniqueID;
74 fEffectColor = GrColor_ILLEGAL;
75 fEffectFlags = 0;
76
77 fVertices = NULL;
78 fMaxVertices = 0;
79
jvanverth1723bfc2014-07-30 09:16:33 -070080 fVertexBounds.setLargestInverted();
jvanverth@google.comd830d132013-11-11 20:54:09 +000081}
82
83GrDistanceFieldTextContext::~GrDistanceFieldTextContext() {
jvanverth0fedb192014-10-08 09:07:27 -070084 this->flush();
jvanverth2d2a68c2014-06-10 06:42:56 -070085 SkSafeSetNull(fGammaTexture);
jvanverth@google.comd830d132013-11-11 20:54:09 +000086}
87
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000088bool GrDistanceFieldTextContext::canDraw(const SkPaint& paint) {
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000089 if (!fEnableDFRendering && !paint.isDistanceFieldTextTEMP()) {
commit-bot@chromium.orgeefd8a02014-04-08 20:14:32 +000090 return false;
91 }
92
skia.committer@gmail.come1d94432014-04-09 03:04:11 +000093 // rasterizers and mask filters modify alpha, which doesn't
commit-bot@chromium.orgeefd8a02014-04-08 20:14:32 +000094 // translate well to distance
95 if (paint.getRasterizer() || paint.getMaskFilter() ||
96 !fContext->getTextTarget()->caps()->shaderDerivativeSupport()) {
97 return false;
98 }
99
100 // TODO: add some stroking support
101 if (paint.getStyle() != SkPaint::kFill_Style) {
102 return false;
103 }
104
105 // TODO: choose an appropriate maximum scale for distance fields and
106 // enable perspective
107 if (SkDraw::ShouldDrawTextAsPaths(paint, fContext->getMatrix())) {
108 return false;
109 }
110
111 // distance fields cannot represent color fonts
112 SkScalerContext::Rec rec;
113 SkScalerContext::MakeRec(paint, &fDeviceProperties, NULL, &rec);
114 return rec.getFormat() != SkMask::kARGB32_Format;
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000115}
116
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000117inline void GrDistanceFieldTextContext::init(const GrPaint& paint, const SkPaint& skPaint) {
118 GrTextContext::init(paint, skPaint);
119
120 fStrike = NULL;
121
jvanverth76ce81e2014-09-22 14:26:53 -0700122 const SkMatrix& ctm = fContext->getMatrix();
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 fCurrVertex = 0;
137
138 fVertices = NULL;
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));
jvanverth76ce81e2014-09-22 14:26:53 -0700143 } else if (scaledTextSize <= kMediumDFFontLimit) {
jvanverth9564ce62014-09-16 05:45:19 -0700144 fTextRatio = textSize / kMediumDFFontSize;
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000145 fSkPaint.setTextSize(SkIntToScalar(kMediumDFFontSize));
146 } else {
jvanverth9564ce62014-09-16 05:45:19 -0700147 fTextRatio = textSize / kLargeDFFontSize;
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000148 fSkPaint.setTextSize(SkIntToScalar(kLargeDFFontSize));
149 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000150
commit-bot@chromium.org609ced42014-04-03 18:25:48 +0000151 fUseLCDText = fSkPaint.isLCDRenderText();
skia.committer@gmail.com221b9112014-04-04 03:04:32 +0000152
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000153 fSkPaint.setLCDRenderText(false);
154 fSkPaint.setAutohinted(false);
jvanverth2d2a68c2014-06-10 06:42:56 -0700155 fSkPaint.setHinting(SkPaint::kNormal_Hinting);
commit-bot@chromium.org0bed43c2014-03-14 21:22:38 +0000156 fSkPaint.setSubpixelText(true);
jvanverth2d2a68c2014-06-10 06:42:56 -0700157
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000158}
159
jvanverth2d2a68c2014-06-10 06:42:56 -0700160static void setup_gamma_texture(GrContext* context, const SkGlyphCache* cache,
161 const SkDeviceProperties& deviceProperties,
162 GrTexture** gammaTexture) {
163 if (NULL == *gammaTexture) {
164 int width, height;
165 size_t size;
166
167#ifdef SK_GAMMA_CONTRAST
168 SkScalar contrast = SK_GAMMA_CONTRAST;
169#else
170 SkScalar contrast = 0.5f;
171#endif
reed4a8126e2014-09-22 07:29:03 -0700172 SkScalar paintGamma = deviceProperties.getGamma();
173 SkScalar deviceGamma = deviceProperties.getGamma();
jvanverth2d2a68c2014-06-10 06:42:56 -0700174
175 size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
176 &width, &height);
177
178 SkAutoTArray<uint8_t> data((int)size);
179 SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
180
181 // TODO: Update this to use the cache rather than directly creating a texture.
182 GrTextureDesc desc;
183 desc.fFlags = kDynamicUpdate_GrTextureFlagBit;
184 desc.fWidth = width;
185 desc.fHeight = height;
186 desc.fConfig = kAlpha_8_GrPixelConfig;
187
188 *gammaTexture = context->getGpu()->createTexture(desc, NULL, 0);
189 if (NULL == *gammaTexture) {
190 return;
191 }
192
bsalomon81beccc2014-10-13 12:32:55 -0700193 (*gammaTexture)->writePixels(0, 0, width, height,
194 (*gammaTexture)->config(), data.get(), 0,
195 GrContext::kDontFlush_PixelOpsFlag);
jvanverth2d2a68c2014-06-10 06:42:56 -0700196 }
197}
198
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000199void GrDistanceFieldTextContext::drawText(const GrPaint& paint, const SkPaint& skPaint,
200 const char text[], size_t byteLength,
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000201 SkScalar x, SkScalar y) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000202 SkASSERT(byteLength == 0 || text != NULL);
203
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000204 // nothing to draw or can't draw
205 if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/
206 || fSkPaint.getRasterizer()) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000207 return;
208 }
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000209
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000210 this->init(paint, skPaint);
211
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000212 SkScalar sizeRatio = fTextRatio;
213
214 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
215
jvanverth2d2a68c2014-06-10 06:42:56 -0700216 SkAutoGlyphCacheNoGamma autoCache(fSkPaint, &fDeviceProperties, NULL);
217 SkGlyphCache* cache = autoCache.getCache();
218 GrFontScaler* fontScaler = GetGrFontScaler(cache);
219
220 setup_gamma_texture(fContext, cache, fDeviceProperties, &fGammaTexture);
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000221
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000222 // need to measure first
223 // TODO - generate positions and pre-load cache as well?
224 const char* stop = text + byteLength;
225 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
226 SkFixed stopX = 0;
227 SkFixed stopY = 0;
228
229 const char* textPtr = text;
230 while (textPtr < stop) {
231 // don't need x, y here, since all subpixel variants will have the
232 // same advance
233 const SkGlyph& glyph = glyphCacheProc(cache, &textPtr, 0, 0);
234
235 stopX += glyph.fAdvanceX;
236 stopY += glyph.fAdvanceY;
237 }
238 SkASSERT(textPtr == stop);
239
240 SkScalar alignX = SkFixedToScalar(stopX)*sizeRatio;
241 SkScalar alignY = SkFixedToScalar(stopY)*sizeRatio;
242
243 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
244 alignX = SkScalarHalf(alignX);
245 alignY = SkScalarHalf(alignY);
246 }
247
248 x -= alignX;
249 y -= alignY;
250 }
251
commit-bot@chromium.org5408f8f2014-05-21 19:44:24 +0000252 SkFixed fx = SkScalarToFixed(x);
253 SkFixed fy = SkScalarToFixed(y);
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000254 SkFixed fixedScale = SkScalarToFixed(sizeRatio);
255 while (text < stop) {
commit-bot@chromium.orga9dae712014-03-24 18:34:04 +0000256 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000257
258 if (glyph.fWidth) {
jvanverth0fedb192014-10-08 09:07:27 -0700259 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
260 glyph.getSubXFixed(),
261 glyph.getSubYFixed()),
262 fx,
263 fy,
264 fontScaler);
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000265 }
266
267 fx += SkFixedMul_portable(glyph.fAdvanceX, fixedScale);
268 fy += SkFixedMul_portable(glyph.fAdvanceY, fixedScale);
269 }
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000270
271 this->finish();
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000272}
273
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000274void GrDistanceFieldTextContext::drawPosText(const GrPaint& paint, const SkPaint& skPaint,
275 const char text[], size_t byteLength,
fmalita05c4a432014-09-29 06:29:53 -0700276 const SkScalar pos[], int scalarsPerPosition,
277 const SkPoint& offset) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000278
279 SkASSERT(byteLength == 0 || text != NULL);
280 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
281
282 // nothing to draw
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000283 if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000284 return;
285 }
286
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000287 this->init(paint, skPaint);
288
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000289 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
290
jvanverth2d2a68c2014-06-10 06:42:56 -0700291 SkAutoGlyphCacheNoGamma autoCache(fSkPaint, &fDeviceProperties, NULL);
292 SkGlyphCache* cache = autoCache.getCache();
293 GrFontScaler* fontScaler = GetGrFontScaler(cache);
294
295 setup_gamma_texture(fContext, cache, fDeviceProperties, &fGammaTexture);
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000296
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000297 const char* stop = text + byteLength;
298
299 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
300 while (text < stop) {
301 // the last 2 parameters are ignored
302 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
303
304 if (glyph.fWidth) {
fmalita05c4a432014-09-29 06:29:53 -0700305 SkScalar x = offset.x() + pos[0];
306 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000307
jvanverth0fedb192014-10-08 09:07:27 -0700308 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
309 glyph.getSubXFixed(),
310 glyph.getSubYFixed()),
311 SkScalarToFixed(x),
312 SkScalarToFixed(y),
313 fontScaler);
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000314 }
315 pos += scalarsPerPosition;
316 }
317 } else {
318 int alignShift = SkPaint::kCenter_Align == fSkPaint.getTextAlign() ? 1 : 0;
319 while (text < stop) {
320 // the last 2 parameters are ignored
321 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
322
323 if (glyph.fWidth) {
fmalita05c4a432014-09-29 06:29:53 -0700324 SkScalar x = offset.x() + pos[0];
325 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
skia.committer@gmail.com22e96722013-12-20 07:01:36 +0000326
jvanverth0fedb192014-10-08 09:07:27 -0700327 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
328 glyph.getSubXFixed(),
329 glyph.getSubYFixed()),
330 SkScalarToFixed(x) - (glyph.fAdvanceX >> alignShift),
331 SkScalarToFixed(y) - (glyph.fAdvanceY >> alignShift),
332 fontScaler);
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000333 }
334 pos += scalarsPerPosition;
335 }
336 }
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000337
338 this->finish();
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000339}
jvanverth0fedb192014-10-08 09:07:27 -0700340
341static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
342 unsigned r = SkColorGetR(c);
343 unsigned g = SkColorGetG(c);
344 unsigned b = SkColorGetB(c);
345 return GrColorPackRGBA(r, g, b, 0xff);
346}
347
348void GrDistanceFieldTextContext::setupCoverageEffect(const SkColor& filteredColor) {
349 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode);
350 GrTextureParams gammaParams(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
351
352 uint32_t textureUniqueID = fCurrTexture->getUniqueID();
353 const SkMatrix& ctm = fContext->getMatrix();
354
355 // set up any flags
356 uint32_t flags = 0;
357 flags |= ctm.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
358 flags |= fUseLCDText ? kUseLCD_DistanceFieldEffectFlag : 0;
359 flags |= fUseLCDText && ctm.rectStaysRect() ?
360 kRectToRect_DistanceFieldEffectFlag : 0;
361 bool useBGR = SkPixelGeometryIsBGR(fDeviceProperties.fPixelGeometry);
362 flags |= fUseLCDText && useBGR ? kBGR_DistanceFieldEffectFlag : 0;
363
364 // see if we need to create a new effect
365 if (textureUniqueID != fEffectTextureUniqueID ||
366 filteredColor != fEffectColor ||
367 flags != fEffectFlags) {
368 if (fUseLCDText) {
369 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
370 fCachedGeometryProcessor.reset(GrDistanceFieldLCDTextureEffect::Create(fCurrTexture,
371 params,
372 fGammaTexture,
373 gammaParams,
374 colorNoPreMul,
375 flags));
376 } else {
377#ifdef SK_GAMMA_APPLY_TO_A8
378 U8CPU lum = SkColorSpaceLuminance::computeLuminance(fDeviceProperties.getGamma(),
379 filteredColor);
380 fCachedGeometryProcessor.reset(GrDistanceFieldTextureEffect::Create(fCurrTexture,
381 params,
382 fGammaTexture,
383 gammaParams,
384 lum/255.f,
385 flags));
386#else
387 fCachedGeometryProcessor.reset(GrDistanceFieldTextureEffect::Create(fCurrTexture,
388 params, flags));
389#endif
390 }
391 fEffectTextureUniqueID = textureUniqueID;
392 fEffectColor = filteredColor;
393 fEffectFlags = flags;
394 }
395
396}
397
398void GrDistanceFieldTextContext::appendGlyph(GrGlyph::PackedID packed,
399 SkFixed vx, SkFixed vy,
400 GrFontScaler* scaler) {
401 if (NULL == fDrawTarget) {
402 return;
403 }
404
405 if (NULL == fStrike) {
406 fStrike = fContext->getFontCache()->getStrike(scaler, true);
407 }
408
409 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
410 if (NULL == glyph || glyph->fBounds.isEmpty()) {
411 return;
412 }
413
jvanverth294c3262014-10-10 11:36:12 -0700414 // TODO: support color glyphs
415 if (kA8_GrMaskFormat != glyph->fMaskFormat) {
416 return;
417 }
418
jvanverth0fedb192014-10-08 09:07:27 -0700419 SkScalar sx = SkFixedToScalar(vx);
420 SkScalar sy = SkFixedToScalar(vy);
421/*
422 // not valid, need to find a different solution for this
423 vx += SkIntToFixed(glyph->fBounds.fLeft);
424 vy += SkIntToFixed(glyph->fBounds.fTop);
425
426 // keep them as ints until we've done the clip-test
427 GrFixed width = glyph->fBounds.width();
428 GrFixed height = glyph->fBounds.height();
429
430 // check if we clipped out
431 if (true || NULL == glyph->fPlot) {
432 int x = vx >> 16;
433 int y = vy >> 16;
434 if (fClipRect.quickReject(x, y, x + width, y + height)) {
435// SkCLZ(3); // so we can set a break-point in the debugger
436 return;
437 }
438 }
439*/
440 if (NULL == glyph->fPlot) {
441 if (!fStrike->glyphTooLargeForAtlas(glyph)) {
442 if (fStrike->addGlyphToAtlas(glyph, scaler)) {
443 goto HAS_ATLAS;
444 }
445
446 // try to clear out an unused plot before we flush
jvanverth294c3262014-10-10 11:36:12 -0700447 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
jvanverth0fedb192014-10-08 09:07:27 -0700448 fStrike->addGlyphToAtlas(glyph, scaler)) {
449 goto HAS_ATLAS;
450 }
451
452 if (c_DumpFontCache) {
453#ifdef SK_DEVELOPER
454 fContext->getFontCache()->dump();
455#endif
456 }
457
458 // before we purge the cache, we must flush any accumulated draws
459 this->flush();
460 fContext->flush();
461
462 // we should have an unused plot now
jvanverth294c3262014-10-10 11:36:12 -0700463 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
jvanverth0fedb192014-10-08 09:07:27 -0700464 fStrike->addGlyphToAtlas(glyph, scaler)) {
465 goto HAS_ATLAS;
466 }
467 }
468
469 if (NULL == glyph->fPath) {
470 SkPath* path = SkNEW(SkPath);
471 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
472 // flag the glyph as being dead?
473 delete path;
474 return;
475 }
476 glyph->fPath = path;
477 }
478
479 GrContext::AutoMatrix am;
480 SkMatrix ctm;
481 ctm.setScale(fTextRatio, fTextRatio);
482 ctm.postTranslate(sx, sy);
483 GrPaint tmpPaint(fPaint);
484 am.setPreConcat(fContext, ctm, &tmpPaint);
485 GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle);
486 fContext->drawPath(tmpPaint, *glyph->fPath, strokeInfo);
487 return;
488 }
489
490HAS_ATLAS:
491 SkASSERT(glyph->fPlot);
492 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
493 glyph->fPlot->setDrawToken(drawToken);
494
495 GrTexture* texture = glyph->fPlot->texture();
496 SkASSERT(texture);
497
498 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
499 this->flush();
500 fCurrTexture = texture;
501 fCurrTexture->ref();
502 }
503
504 bool useColorVerts = !fUseLCDText;
505
506 if (NULL == fVertices) {
507 // If we need to reserve vertices allow the draw target to suggest
508 // a number of verts to reserve and whether to perform a flush.
509 fMaxVertices = kMinRequestedVerts;
510 if (useColorVerts) {
511 fDrawTarget->drawState()->setVertexAttribs<gTextVertexWithColorAttribs>(
512 SK_ARRAY_COUNT(gTextVertexWithColorAttribs), kTextVAColorSize);
513 } else {
514 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
515 SK_ARRAY_COUNT(gTextVertexAttribs), kTextVASize);
516 }
517 bool flush = fDrawTarget->geometryHints(&fMaxVertices, NULL);
518 if (flush) {
519 this->flush();
520 fContext->flush();
521 if (useColorVerts) {
522 fDrawTarget->drawState()->setVertexAttribs<gTextVertexWithColorAttribs>(
523 SK_ARRAY_COUNT(gTextVertexWithColorAttribs), kTextVAColorSize);
524 } else {
525 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
526 SK_ARRAY_COUNT(gTextVertexAttribs), kTextVASize);
527 }
528 }
529 fMaxVertices = kDefaultRequestedVerts;
530 // ignore return, no point in flushing again.
531 fDrawTarget->geometryHints(&fMaxVertices, NULL);
532
533 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
534 if (fMaxVertices < kMinRequestedVerts) {
535 fMaxVertices = kDefaultRequestedVerts;
536 } else if (fMaxVertices > maxQuadVertices) {
537 // don't exceed the limit of the index buffer
538 fMaxVertices = maxQuadVertices;
539 }
540 bool success = fDrawTarget->reserveVertexAndIndexSpace(fMaxVertices,
541 0,
542 &fVertices,
543 NULL);
544 GrAlwaysAssert(success);
545 }
546
547 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
548 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
549 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2*SK_DistanceFieldInset);
550 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2*SK_DistanceFieldInset);
551
552 SkScalar scale = fTextRatio;
553 dx *= scale;
554 dy *= scale;
555 sx += dx;
556 sy += dy;
557 width *= scale;
558 height *= scale;
559
560 SkFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX + SK_DistanceFieldInset);
561 SkFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY + SK_DistanceFieldInset);
562 SkFixed tw = SkIntToFixed(glyph->fBounds.width() - 2*SK_DistanceFieldInset);
563 SkFixed th = SkIntToFixed(glyph->fBounds.height() - 2*SK_DistanceFieldInset);
564
565 SkRect r;
566 r.fLeft = sx;
567 r.fTop = sy;
568 r.fRight = sx + width;
569 r.fBottom = sy + height;
570
571 fVertexBounds.joinNonEmptyArg(r);
572
573 size_t vertSize = fUseLCDText ? (2 * sizeof(SkPoint))
574 : (2 * sizeof(SkPoint) + sizeof(GrColor));
575
576 SkASSERT(vertSize == fDrawTarget->getDrawState().getVertexStride());
577
578 SkPoint* positions = reinterpret_cast<SkPoint*>(
579 reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVertex);
580 positions->setRectFan(r.fLeft, r.fTop, r.fRight, r.fBottom, vertSize);
581
582 // The texture coords are last in both the with and without color vertex layouts.
583 SkPoint* textureCoords = reinterpret_cast<SkPoint*>(
584 reinterpret_cast<intptr_t>(positions) + vertSize - sizeof(SkPoint));
585 textureCoords->setRectFan(SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx)),
586 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty)),
587 SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx + tw)),
588 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty + th)),
589 vertSize);
590 if (useColorVerts) {
591 if (0xFF == GrColorUnpackA(fPaint.getColor())) {
592 fDrawTarget->drawState()->setHint(GrDrawState::kVertexColorsAreOpaque_Hint, true);
593 }
594 // color comes after position.
595 GrColor* colors = reinterpret_cast<GrColor*>(positions + 1);
596 for (int i = 0; i < 4; ++i) {
597 *colors = fPaint.getColor();
598 colors = reinterpret_cast<GrColor*>(reinterpret_cast<intptr_t>(colors) + vertSize);
599 }
600 }
601
602 fCurrVertex += 4;
603}
604
605void GrDistanceFieldTextContext::flush() {
606 if (NULL == fDrawTarget) {
607 return;
608 }
609
610 GrDrawState* drawState = fDrawTarget->drawState();
611 GrDrawState::AutoRestoreEffects are(drawState);
612
613 drawState->setFromPaint(fPaint, fContext->getMatrix(), fContext->getRenderTarget());
614
615 if (fCurrVertex > 0) {
616 // setup our sampler state for our text texture/atlas
617 SkASSERT(SkIsAlign4(fCurrVertex));
618
619 // get our current color
620 SkColor filteredColor;
621 SkColorFilter* colorFilter = fSkPaint.getColorFilter();
622 if (colorFilter) {
623 filteredColor = colorFilter->filterColor(fSkPaint.getColor());
624 } else {
625 filteredColor = fSkPaint.getColor();
626 }
627 this->setupCoverageEffect(filteredColor);
628
629 // Effects could be stored with one of the cache objects (atlas?)
630 drawState->setGeometryProcessor(fCachedGeometryProcessor.get());
631
632 // Set draw state
633 if (fUseLCDText) {
634 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
635 if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
636 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
637 fPaint.numColorStages()) {
638 GrPrintf("LCD Text will not draw correctly.\n");
639 }
640 SkASSERT(!drawState->hasColorVertexAttribute());
641 // We don't use the GrPaint's color in this case because it's been premultiplied by
642 // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by
643 // the mask texture color. The end result is that we get
644 // mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor
645 int a = SkColorGetA(fSkPaint.getColor());
646 // paintAlpha
647 drawState->setColor(SkColorSetARGB(a, a, a, a));
648 // paintColor
649 drawState->setBlendConstant(colorNoPreMul);
650 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
651 } else {
652 // set back to normal in case we took LCD path previously.
653 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
654 // We're using per-vertex color.
655 SkASSERT(drawState->hasColorVertexAttribute());
656 }
657 int nGlyphs = fCurrVertex / 4;
658 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
659 fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
660 nGlyphs,
661 4, 6, &fVertexBounds);
662 fDrawTarget->resetVertexSource();
663 fVertices = NULL;
664 fMaxVertices = 0;
665 fCurrVertex = 0;
666 SkSafeSetNull(fCurrTexture);
667 fVertexBounds.setLargestInverted();
668 }
669}
670
671inline void GrDistanceFieldTextContext::finish() {
672 this->flush();
673
674 GrTextContext::finish();
675}
676