blob: ce2b1759bc1eca61664935b6e5c8ea3eb3146d91 [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"
jvanverth@google.comd830d132013-11-11 20:54:09 +000013#include "GrFontScaler.h"
jvanverth2d2a68c2014-06-10 06:42:56 -070014#include "GrGpu.h"
jvanverth@google.comd830d132013-11-11 20:54:09 +000015#include "GrIndexBuffer.h"
egdanield58a0ba2014-06-11 10:30:05 -070016#include "GrStrokeInfo.h"
bsalomonafbf2d62014-09-30 12:18:44 -070017#include "GrTexturePriv.h"
jvanverth@google.comd830d132013-11-11 20:54:09 +000018#include "GrTextStrike.h"
19#include "GrTextStrike_impl.h"
bsalomonafbf2d62014-09-30 12:18:44 -070020
21#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
jvanverth7851a562014-10-16 06:20:35 -070031#include "SkDrawProcs.h"
32#include "SkTextMapStateProc.h"
33
jvanverthfeceba52014-07-25 19:03:34 -070034SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
35 "Dump the contents of the font cache before every purge.");
36
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +000037static const int kSmallDFFontSize = 32;
38static const int kSmallDFFontLimit = 32;
39static const int kMediumDFFontSize = 64;
40static const int kMediumDFFontLimit = 64;
41static const int kLargeDFFontSize = 128;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +000042
jvanverthfeceba52014-07-25 19:03:34 -070043namespace {
44// position + texture coord
45extern const GrVertexAttrib gTextVertexAttribs[] = {
46 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
joshualittb0a8a372014-09-23 09:50:21 -070047 {kVec2f_GrVertexAttribType, sizeof(SkPoint) , kGeometryProcessor_GrVertexAttribBinding}
jvanverthfeceba52014-07-25 19:03:34 -070048};
49
egdaniel7b3d5ee2014-08-28 05:41:14 -070050static const size_t kTextVASize = 2 * sizeof(SkPoint);
51
jvanverthfeceba52014-07-25 19:03:34 -070052// position + color + texture coord
53extern const GrVertexAttrib gTextVertexWithColorAttribs[] = {
54 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
55 {kVec4ub_GrVertexAttribType, sizeof(SkPoint), kColor_GrVertexAttribBinding},
joshualittb0a8a372014-09-23 09:50:21 -070056 {kVec2f_GrVertexAttribType, sizeof(SkPoint) + sizeof(GrColor), kGeometryProcessor_GrVertexAttribBinding}
jvanverthfeceba52014-07-25 19:03:34 -070057};
58
egdaniel7b3d5ee2014-08-28 05:41:14 -070059static const size_t kTextVAColorSize = 2 * sizeof(SkPoint) + sizeof(GrColor);
60
jvanverthfeceba52014-07-25 19:03:34 -070061};
jvanverth@google.comd830d132013-11-11 20:54:09 +000062
skia.committer@gmail.come5d70152014-01-29 07:01:48 +000063GrDistanceFieldTextContext::GrDistanceFieldTextContext(GrContext* context,
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000064 const SkDeviceProperties& properties,
65 bool enable)
Mike Klein6a25bd02014-08-29 10:03:59 -040066 : GrTextContext(context, properties) {
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000067#if SK_FORCE_DISTANCEFIELD_FONTS
Mike Klein6a25bd02014-08-29 10:03:59 -040068 fEnableDFRendering = true;
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000069#else
Mike Klein6a25bd02014-08-29 10:03:59 -040070 fEnableDFRendering = enable;
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000071#endif
Mike Klein6a25bd02014-08-29 10:03:59 -040072 fStrike = NULL;
73 fGammaTexture = NULL;
74
75 fCurrTexture = NULL;
76 fCurrVertex = 0;
77 fEffectTextureUniqueID = SK_InvalidUniqueID;
78 fEffectColor = GrColor_ILLEGAL;
79 fEffectFlags = 0;
80
81 fVertices = NULL;
82 fMaxVertices = 0;
83
jvanverth1723bfc2014-07-30 09:16:33 -070084 fVertexBounds.setLargestInverted();
jvanverth@google.comd830d132013-11-11 20:54:09 +000085}
86
jvanverth8c27a182014-10-14 08:45:50 -070087GrDistanceFieldTextContext* GrDistanceFieldTextContext::Create(GrContext* context,
88 const SkDeviceProperties& props,
89 bool enable) {
90 GrDistanceFieldTextContext* textContext = SkNEW_ARGS(GrDistanceFieldTextContext,
91 (context, props, enable));
92 textContext->fFallbackTextContext = GrBitmapTextContext::Create(context, props);
93
94 return textContext;
95}
96
jvanverth@google.comd830d132013-11-11 20:54:09 +000097GrDistanceFieldTextContext::~GrDistanceFieldTextContext() {
jvanverth0fedb192014-10-08 09:07:27 -070098 this->flush();
jvanverth2d2a68c2014-06-10 06:42:56 -070099 SkSafeSetNull(fGammaTexture);
jvanverth@google.comd830d132013-11-11 20:54:09 +0000100}
101
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000102bool GrDistanceFieldTextContext::canDraw(const SkPaint& paint) {
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +0000103 if (!fEnableDFRendering && !paint.isDistanceFieldTextTEMP()) {
commit-bot@chromium.orgeefd8a02014-04-08 20:14:32 +0000104 return false;
105 }
106
skia.committer@gmail.come1d94432014-04-09 03:04:11 +0000107 // rasterizers and mask filters modify alpha, which doesn't
commit-bot@chromium.orgeefd8a02014-04-08 20:14:32 +0000108 // translate well to distance
109 if (paint.getRasterizer() || paint.getMaskFilter() ||
110 !fContext->getTextTarget()->caps()->shaderDerivativeSupport()) {
111 return false;
112 }
113
114 // TODO: add some stroking support
115 if (paint.getStyle() != SkPaint::kFill_Style) {
116 return false;
117 }
118
119 // TODO: choose an appropriate maximum scale for distance fields and
120 // enable perspective
121 if (SkDraw::ShouldDrawTextAsPaths(paint, fContext->getMatrix())) {
122 return false;
123 }
124
125 // distance fields cannot represent color fonts
126 SkScalerContext::Rec rec;
127 SkScalerContext::MakeRec(paint, &fDeviceProperties, NULL, &rec);
128 return rec.getFormat() != SkMask::kARGB32_Format;
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000129}
130
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000131inline void GrDistanceFieldTextContext::init(const GrPaint& paint, const SkPaint& skPaint) {
132 GrTextContext::init(paint, skPaint);
133
134 fStrike = NULL;
135
jvanverth76ce81e2014-09-22 14:26:53 -0700136 const SkMatrix& ctm = fContext->getMatrix();
jvanverth9564ce62014-09-16 05:45:19 -0700137
138 // getMaxScale doesn't support perspective, so neither do we at the moment
jvanverth76ce81e2014-09-22 14:26:53 -0700139 SkASSERT(!ctm.hasPerspective());
140 SkScalar maxScale = ctm.getMaxScale();
jvanverth9564ce62014-09-16 05:45:19 -0700141 SkScalar textSize = fSkPaint.getTextSize();
jvanverth76ce81e2014-09-22 14:26:53 -0700142 SkScalar scaledTextSize = textSize;
143 // if we have non-unity scale, we need to choose our base text size
144 // based on the SkPaint's text size multiplied by the max scale factor
jvanverth9564ce62014-09-16 05:45:19 -0700145 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
146 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
jvanverth76ce81e2014-09-22 14:26:53 -0700147 scaledTextSize *= maxScale;
jvanverth9564ce62014-09-16 05:45:19 -0700148 }
149
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000150 fCurrVertex = 0;
151
152 fVertices = NULL;
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000153
jvanverth76ce81e2014-09-22 14:26:53 -0700154 if (scaledTextSize <= kSmallDFFontLimit) {
jvanverth9564ce62014-09-16 05:45:19 -0700155 fTextRatio = textSize / kSmallDFFontSize;
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000156 fSkPaint.setTextSize(SkIntToScalar(kSmallDFFontSize));
jvanverth76ce81e2014-09-22 14:26:53 -0700157 } else if (scaledTextSize <= kMediumDFFontLimit) {
jvanverth9564ce62014-09-16 05:45:19 -0700158 fTextRatio = textSize / kMediumDFFontSize;
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000159 fSkPaint.setTextSize(SkIntToScalar(kMediumDFFontSize));
160 } else {
jvanverth9564ce62014-09-16 05:45:19 -0700161 fTextRatio = textSize / kLargeDFFontSize;
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000162 fSkPaint.setTextSize(SkIntToScalar(kLargeDFFontSize));
163 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000164
commit-bot@chromium.org609ced42014-04-03 18:25:48 +0000165 fUseLCDText = fSkPaint.isLCDRenderText();
skia.committer@gmail.com221b9112014-04-04 03:04:32 +0000166
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000167 fSkPaint.setLCDRenderText(false);
168 fSkPaint.setAutohinted(false);
jvanverth2d2a68c2014-06-10 06:42:56 -0700169 fSkPaint.setHinting(SkPaint::kNormal_Hinting);
commit-bot@chromium.org0bed43c2014-03-14 21:22:38 +0000170 fSkPaint.setSubpixelText(true);
jvanverth2d2a68c2014-06-10 06:42:56 -0700171
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000172}
173
jvanverth2d2a68c2014-06-10 06:42:56 -0700174static void setup_gamma_texture(GrContext* context, const SkGlyphCache* cache,
175 const SkDeviceProperties& deviceProperties,
176 GrTexture** gammaTexture) {
177 if (NULL == *gammaTexture) {
178 int width, height;
179 size_t size;
180
181#ifdef SK_GAMMA_CONTRAST
182 SkScalar contrast = SK_GAMMA_CONTRAST;
183#else
184 SkScalar contrast = 0.5f;
185#endif
reedb2d77e42014-10-14 08:26:33 -0700186 SkScalar paintGamma = deviceProperties.gamma();
187 SkScalar deviceGamma = deviceProperties.gamma();
jvanverth2d2a68c2014-06-10 06:42:56 -0700188
189 size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
190 &width, &height);
191
192 SkAutoTArray<uint8_t> data((int)size);
193 SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
194
195 // TODO: Update this to use the cache rather than directly creating a texture.
196 GrTextureDesc desc;
197 desc.fFlags = kDynamicUpdate_GrTextureFlagBit;
198 desc.fWidth = width;
199 desc.fHeight = height;
200 desc.fConfig = kAlpha_8_GrPixelConfig;
201
202 *gammaTexture = context->getGpu()->createTexture(desc, NULL, 0);
203 if (NULL == *gammaTexture) {
204 return;
205 }
206
bsalomon81beccc2014-10-13 12:32:55 -0700207 (*gammaTexture)->writePixels(0, 0, width, height,
208 (*gammaTexture)->config(), data.get(), 0,
209 GrContext::kDontFlush_PixelOpsFlag);
jvanverth2d2a68c2014-06-10 06:42:56 -0700210 }
211}
212
jvanverth8c27a182014-10-14 08:45:50 -0700213void GrDistanceFieldTextContext::onDrawPosText(const GrPaint& paint, const SkPaint& skPaint,
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000214 const char text[], size_t byteLength,
fmalita05c4a432014-09-29 06:29:53 -0700215 const SkScalar pos[], int scalarsPerPosition,
216 const SkPoint& offset) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000217
218 SkASSERT(byteLength == 0 || text != NULL);
219 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
220
221 // nothing to draw
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000222 if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000223 return;
224 }
225
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000226 this->init(paint, skPaint);
227
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000228 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
229
jvanverth2d2a68c2014-06-10 06:42:56 -0700230 SkAutoGlyphCacheNoGamma autoCache(fSkPaint, &fDeviceProperties, NULL);
231 SkGlyphCache* cache = autoCache.getCache();
232 GrFontScaler* fontScaler = GetGrFontScaler(cache);
233
234 setup_gamma_texture(fContext, cache, fDeviceProperties, &fGammaTexture);
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000235
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000236 const char* stop = text + byteLength;
237
238 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
239 while (text < stop) {
240 // the last 2 parameters are ignored
241 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
242
243 if (glyph.fWidth) {
fmalita05c4a432014-09-29 06:29:53 -0700244 SkScalar x = offset.x() + pos[0];
245 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000246
jvanverth0fedb192014-10-08 09:07:27 -0700247 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
248 glyph.getSubXFixed(),
249 glyph.getSubYFixed()),
250 SkScalarToFixed(x),
251 SkScalarToFixed(y),
252 fontScaler);
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000253 }
254 pos += scalarsPerPosition;
255 }
256 } else {
jvanverth7851a562014-10-16 06:20:35 -0700257 SkScalar alignMul = SkPaint::kCenter_Align == fSkPaint.getTextAlign() ? SK_ScalarHalf
258 : SK_Scalar1;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000259 while (text < stop) {
260 // the last 2 parameters are ignored
261 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
262
263 if (glyph.fWidth) {
fmalita05c4a432014-09-29 06:29:53 -0700264 SkScalar x = offset.x() + pos[0];
265 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
skia.committer@gmail.com22e96722013-12-20 07:01:36 +0000266
jvanverth7851a562014-10-16 06:20:35 -0700267 SkScalar advanceX = SkFixedToScalar(glyph.fAdvanceX)*alignMul*fTextRatio;
268 SkScalar advanceY = SkFixedToScalar(glyph.fAdvanceY)*alignMul*fTextRatio;
269
jvanverth0fedb192014-10-08 09:07:27 -0700270 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
271 glyph.getSubXFixed(),
272 glyph.getSubYFixed()),
jvanverth7851a562014-10-16 06:20:35 -0700273 SkScalarToFixed(x - advanceX),
274 SkScalarToFixed(y - advanceY),
jvanverth0fedb192014-10-08 09:07:27 -0700275 fontScaler);
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000276 }
277 pos += scalarsPerPosition;
278 }
279 }
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000280
281 this->finish();
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000282}
jvanverth0fedb192014-10-08 09:07:27 -0700283
284static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
285 unsigned r = SkColorGetR(c);
286 unsigned g = SkColorGetG(c);
287 unsigned b = SkColorGetB(c);
288 return GrColorPackRGBA(r, g, b, 0xff);
289}
290
291void GrDistanceFieldTextContext::setupCoverageEffect(const SkColor& filteredColor) {
292 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode);
293 GrTextureParams gammaParams(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
294
295 uint32_t textureUniqueID = fCurrTexture->getUniqueID();
296 const SkMatrix& ctm = fContext->getMatrix();
297
298 // set up any flags
299 uint32_t flags = 0;
300 flags |= ctm.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
301 flags |= fUseLCDText ? kUseLCD_DistanceFieldEffectFlag : 0;
302 flags |= fUseLCDText && ctm.rectStaysRect() ?
303 kRectToRect_DistanceFieldEffectFlag : 0;
reedb2d77e42014-10-14 08:26:33 -0700304 bool useBGR = SkPixelGeometryIsBGR(fDeviceProperties.pixelGeometry());
jvanverth0fedb192014-10-08 09:07:27 -0700305 flags |= fUseLCDText && useBGR ? kBGR_DistanceFieldEffectFlag : 0;
306
307 // see if we need to create a new effect
308 if (textureUniqueID != fEffectTextureUniqueID ||
309 filteredColor != fEffectColor ||
310 flags != fEffectFlags) {
311 if (fUseLCDText) {
312 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
313 fCachedGeometryProcessor.reset(GrDistanceFieldLCDTextureEffect::Create(fCurrTexture,
314 params,
315 fGammaTexture,
316 gammaParams,
317 colorNoPreMul,
318 flags));
319 } else {
320#ifdef SK_GAMMA_APPLY_TO_A8
reedb2d77e42014-10-14 08:26:33 -0700321 U8CPU lum = SkColorSpaceLuminance::computeLuminance(fDeviceProperties.gamma(),
jvanverth0fedb192014-10-08 09:07:27 -0700322 filteredColor);
323 fCachedGeometryProcessor.reset(GrDistanceFieldTextureEffect::Create(fCurrTexture,
324 params,
325 fGammaTexture,
326 gammaParams,
327 lum/255.f,
328 flags));
329#else
330 fCachedGeometryProcessor.reset(GrDistanceFieldTextureEffect::Create(fCurrTexture,
331 params, flags));
332#endif
333 }
334 fEffectTextureUniqueID = textureUniqueID;
335 fEffectColor = filteredColor;
336 fEffectFlags = flags;
337 }
338
339}
340
341void GrDistanceFieldTextContext::appendGlyph(GrGlyph::PackedID packed,
342 SkFixed vx, SkFixed vy,
343 GrFontScaler* scaler) {
344 if (NULL == fDrawTarget) {
345 return;
346 }
347
348 if (NULL == fStrike) {
349 fStrike = fContext->getFontCache()->getStrike(scaler, true);
350 }
351
352 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
353 if (NULL == glyph || glyph->fBounds.isEmpty()) {
354 return;
355 }
356
jvanverth294c3262014-10-10 11:36:12 -0700357 // TODO: support color glyphs
358 if (kA8_GrMaskFormat != glyph->fMaskFormat) {
359 return;
360 }
361
jvanverth0fedb192014-10-08 09:07:27 -0700362 SkScalar sx = SkFixedToScalar(vx);
363 SkScalar sy = SkFixedToScalar(vy);
364/*
365 // not valid, need to find a different solution for this
366 vx += SkIntToFixed(glyph->fBounds.fLeft);
367 vy += SkIntToFixed(glyph->fBounds.fTop);
368
369 // keep them as ints until we've done the clip-test
370 GrFixed width = glyph->fBounds.width();
371 GrFixed height = glyph->fBounds.height();
372
373 // check if we clipped out
374 if (true || NULL == glyph->fPlot) {
375 int x = vx >> 16;
376 int y = vy >> 16;
377 if (fClipRect.quickReject(x, y, x + width, y + height)) {
378// SkCLZ(3); // so we can set a break-point in the debugger
379 return;
380 }
381 }
382*/
383 if (NULL == glyph->fPlot) {
384 if (!fStrike->glyphTooLargeForAtlas(glyph)) {
385 if (fStrike->addGlyphToAtlas(glyph, scaler)) {
386 goto HAS_ATLAS;
387 }
388
389 // try to clear out an unused plot before we flush
jvanverth294c3262014-10-10 11:36:12 -0700390 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
jvanverth0fedb192014-10-08 09:07:27 -0700391 fStrike->addGlyphToAtlas(glyph, scaler)) {
392 goto HAS_ATLAS;
393 }
394
395 if (c_DumpFontCache) {
396#ifdef SK_DEVELOPER
397 fContext->getFontCache()->dump();
398#endif
399 }
400
401 // before we purge the cache, we must flush any accumulated draws
402 this->flush();
403 fContext->flush();
404
405 // we should have an unused plot now
jvanverth294c3262014-10-10 11:36:12 -0700406 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
jvanverth0fedb192014-10-08 09:07:27 -0700407 fStrike->addGlyphToAtlas(glyph, scaler)) {
408 goto HAS_ATLAS;
409 }
410 }
411
412 if (NULL == glyph->fPath) {
413 SkPath* path = SkNEW(SkPath);
414 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
415 // flag the glyph as being dead?
416 delete path;
417 return;
418 }
419 glyph->fPath = path;
420 }
421
bsalomonec87dc62014-10-14 10:52:00 -0700422 // flush any accumulated draws before drawing this glyph as a path.
423 this->flush();
424
jvanverth0fedb192014-10-08 09:07:27 -0700425 GrContext::AutoMatrix am;
426 SkMatrix ctm;
427 ctm.setScale(fTextRatio, fTextRatio);
428 ctm.postTranslate(sx, sy);
429 GrPaint tmpPaint(fPaint);
430 am.setPreConcat(fContext, ctm, &tmpPaint);
431 GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle);
432 fContext->drawPath(tmpPaint, *glyph->fPath, strokeInfo);
433 return;
434 }
435
436HAS_ATLAS:
437 SkASSERT(glyph->fPlot);
438 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
439 glyph->fPlot->setDrawToken(drawToken);
440
441 GrTexture* texture = glyph->fPlot->texture();
442 SkASSERT(texture);
443
444 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
445 this->flush();
446 fCurrTexture = texture;
447 fCurrTexture->ref();
448 }
449
450 bool useColorVerts = !fUseLCDText;
451
452 if (NULL == fVertices) {
453 // If we need to reserve vertices allow the draw target to suggest
454 // a number of verts to reserve and whether to perform a flush.
455 fMaxVertices = kMinRequestedVerts;
456 if (useColorVerts) {
457 fDrawTarget->drawState()->setVertexAttribs<gTextVertexWithColorAttribs>(
458 SK_ARRAY_COUNT(gTextVertexWithColorAttribs), kTextVAColorSize);
459 } else {
460 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
461 SK_ARRAY_COUNT(gTextVertexAttribs), kTextVASize);
462 }
463 bool flush = fDrawTarget->geometryHints(&fMaxVertices, NULL);
464 if (flush) {
465 this->flush();
466 fContext->flush();
467 if (useColorVerts) {
468 fDrawTarget->drawState()->setVertexAttribs<gTextVertexWithColorAttribs>(
469 SK_ARRAY_COUNT(gTextVertexWithColorAttribs), kTextVAColorSize);
470 } else {
471 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
472 SK_ARRAY_COUNT(gTextVertexAttribs), kTextVASize);
473 }
474 }
475 fMaxVertices = kDefaultRequestedVerts;
476 // ignore return, no point in flushing again.
477 fDrawTarget->geometryHints(&fMaxVertices, NULL);
478
479 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
480 if (fMaxVertices < kMinRequestedVerts) {
481 fMaxVertices = kDefaultRequestedVerts;
482 } else if (fMaxVertices > maxQuadVertices) {
483 // don't exceed the limit of the index buffer
484 fMaxVertices = maxQuadVertices;
485 }
486 bool success = fDrawTarget->reserveVertexAndIndexSpace(fMaxVertices,
487 0,
488 &fVertices,
489 NULL);
490 GrAlwaysAssert(success);
491 }
492
493 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
494 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
495 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2*SK_DistanceFieldInset);
496 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2*SK_DistanceFieldInset);
497
498 SkScalar scale = fTextRatio;
499 dx *= scale;
500 dy *= scale;
501 sx += dx;
502 sy += dy;
503 width *= scale;
504 height *= scale;
505
506 SkFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX + SK_DistanceFieldInset);
507 SkFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY + SK_DistanceFieldInset);
508 SkFixed tw = SkIntToFixed(glyph->fBounds.width() - 2*SK_DistanceFieldInset);
509 SkFixed th = SkIntToFixed(glyph->fBounds.height() - 2*SK_DistanceFieldInset);
510
511 SkRect r;
512 r.fLeft = sx;
513 r.fTop = sy;
514 r.fRight = sx + width;
515 r.fBottom = sy + height;
516
517 fVertexBounds.joinNonEmptyArg(r);
518
519 size_t vertSize = fUseLCDText ? (2 * sizeof(SkPoint))
520 : (2 * sizeof(SkPoint) + sizeof(GrColor));
521
522 SkASSERT(vertSize == fDrawTarget->getDrawState().getVertexStride());
523
524 SkPoint* positions = reinterpret_cast<SkPoint*>(
525 reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVertex);
526 positions->setRectFan(r.fLeft, r.fTop, r.fRight, r.fBottom, vertSize);
527
528 // The texture coords are last in both the with and without color vertex layouts.
529 SkPoint* textureCoords = reinterpret_cast<SkPoint*>(
530 reinterpret_cast<intptr_t>(positions) + vertSize - sizeof(SkPoint));
531 textureCoords->setRectFan(SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx)),
532 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty)),
533 SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx + tw)),
534 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty + th)),
535 vertSize);
536 if (useColorVerts) {
537 if (0xFF == GrColorUnpackA(fPaint.getColor())) {
538 fDrawTarget->drawState()->setHint(GrDrawState::kVertexColorsAreOpaque_Hint, true);
539 }
540 // color comes after position.
541 GrColor* colors = reinterpret_cast<GrColor*>(positions + 1);
542 for (int i = 0; i < 4; ++i) {
543 *colors = fPaint.getColor();
544 colors = reinterpret_cast<GrColor*>(reinterpret_cast<intptr_t>(colors) + vertSize);
545 }
546 }
547
548 fCurrVertex += 4;
549}
550
551void GrDistanceFieldTextContext::flush() {
552 if (NULL == fDrawTarget) {
553 return;
554 }
555
556 GrDrawState* drawState = fDrawTarget->drawState();
557 GrDrawState::AutoRestoreEffects are(drawState);
558
559 drawState->setFromPaint(fPaint, fContext->getMatrix(), fContext->getRenderTarget());
560
561 if (fCurrVertex > 0) {
562 // setup our sampler state for our text texture/atlas
563 SkASSERT(SkIsAlign4(fCurrVertex));
564
565 // get our current color
566 SkColor filteredColor;
567 SkColorFilter* colorFilter = fSkPaint.getColorFilter();
568 if (colorFilter) {
569 filteredColor = colorFilter->filterColor(fSkPaint.getColor());
570 } else {
571 filteredColor = fSkPaint.getColor();
572 }
573 this->setupCoverageEffect(filteredColor);
574
575 // Effects could be stored with one of the cache objects (atlas?)
576 drawState->setGeometryProcessor(fCachedGeometryProcessor.get());
577
578 // Set draw state
579 if (fUseLCDText) {
580 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
581 if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
582 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
583 fPaint.numColorStages()) {
584 GrPrintf("LCD Text will not draw correctly.\n");
585 }
586 SkASSERT(!drawState->hasColorVertexAttribute());
587 // We don't use the GrPaint's color in this case because it's been premultiplied by
588 // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by
589 // the mask texture color. The end result is that we get
590 // mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor
591 int a = SkColorGetA(fSkPaint.getColor());
592 // paintAlpha
593 drawState->setColor(SkColorSetARGB(a, a, a, a));
594 // paintColor
595 drawState->setBlendConstant(colorNoPreMul);
596 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
597 } else {
598 // set back to normal in case we took LCD path previously.
599 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
600 // We're using per-vertex color.
601 SkASSERT(drawState->hasColorVertexAttribute());
602 }
603 int nGlyphs = fCurrVertex / 4;
604 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
605 fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
606 nGlyphs,
607 4, 6, &fVertexBounds);
608 fDrawTarget->resetVertexSource();
609 fVertices = NULL;
610 fMaxVertices = 0;
611 fCurrVertex = 0;
612 SkSafeSetNull(fCurrTexture);
613 fVertexBounds.setLargestInverted();
614 }
615}
616
617inline void GrDistanceFieldTextContext::finish() {
618 this->flush();
619
620 GrTextContext::finish();
621}
622