blob: 3b5bd304d3036035840563e7c7f7b08b9a782d79 [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"
jvanverth2d2a68c2014-06-10 06:42:56 -070010#include "SkColorFilter.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"
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +000014#include "SkGlyphCache.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"
jvanverth@google.comd830d132013-11-11 20:54:09 +000018#include "GrTextStrike.h"
19#include "GrTextStrike_impl.h"
commit-bot@chromium.org64b08a12014-04-15 17:53:21 +000020#include "SkDistanceFieldGen.h"
commit-bot@chromium.org9f94b912014-01-30 15:22:54 +000021#include "SkDraw.h"
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +000022#include "SkGpuDevice.h"
jvanverth@google.comd830d132013-11-11 20:54:09 +000023#include "SkPath.h"
24#include "SkRTConf.h"
25#include "SkStrokeRec.h"
26#include "effects/GrDistanceFieldTextureEffect.h"
27
jvanverthfeceba52014-07-25 19:03:34 -070028SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
29 "Dump the contents of the font cache before every purge.");
30
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +000031static const int kSmallDFFontSize = 32;
32static const int kSmallDFFontLimit = 32;
33static const int kMediumDFFontSize = 64;
34static const int kMediumDFFontLimit = 64;
35static const int kLargeDFFontSize = 128;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +000036
jvanverthfeceba52014-07-25 19:03:34 -070037namespace {
38// position + texture coord
39extern const GrVertexAttrib gTextVertexAttribs[] = {
40 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
41 {kVec2f_GrVertexAttribType, sizeof(SkPoint) , kEffect_GrVertexAttribBinding}
42};
43
egdaniel7b3d5ee2014-08-28 05:41:14 -070044static const size_t kTextVASize = 2 * sizeof(SkPoint);
45
jvanverthfeceba52014-07-25 19:03:34 -070046// position + color + texture coord
47extern const GrVertexAttrib gTextVertexWithColorAttribs[] = {
48 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
49 {kVec4ub_GrVertexAttribType, sizeof(SkPoint), kColor_GrVertexAttribBinding},
50 {kVec2f_GrVertexAttribType, sizeof(SkPoint) + sizeof(GrColor), kEffect_GrVertexAttribBinding}
51};
52
egdaniel7b3d5ee2014-08-28 05:41:14 -070053static const size_t kTextVAColorSize = 2 * sizeof(SkPoint) + sizeof(GrColor);
54
jvanverthfeceba52014-07-25 19:03:34 -070055};
jvanverth@google.comd830d132013-11-11 20:54:09 +000056
skia.committer@gmail.come5d70152014-01-29 07:01:48 +000057GrDistanceFieldTextContext::GrDistanceFieldTextContext(GrContext* context,
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000058 const SkDeviceProperties& properties,
59 bool enable)
Mike Klein6a25bd02014-08-29 10:03:59 -040060 : GrTextContext(context, properties) {
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000061#if SK_FORCE_DISTANCEFIELD_FONTS
Mike Klein6a25bd02014-08-29 10:03:59 -040062 fEnableDFRendering = true;
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000063#else
Mike Klein6a25bd02014-08-29 10:03:59 -040064 fEnableDFRendering = enable;
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000065#endif
Mike Klein6a25bd02014-08-29 10:03:59 -040066 fStrike = NULL;
67 fGammaTexture = NULL;
68
69 fCurrTexture = NULL;
70 fCurrVertex = 0;
71 fEffectTextureUniqueID = SK_InvalidUniqueID;
72 fEffectColor = GrColor_ILLEGAL;
73 fEffectFlags = 0;
74
75 fVertices = NULL;
76 fMaxVertices = 0;
77
jvanverth1723bfc2014-07-30 09:16:33 -070078 fVertexBounds.setLargestInverted();
jvanverth@google.comd830d132013-11-11 20:54:09 +000079}
80
81GrDistanceFieldTextContext::~GrDistanceFieldTextContext() {
82 this->flushGlyphs();
jvanverth2d2a68c2014-06-10 06:42:56 -070083 SkSafeSetNull(fGammaTexture);
jvanverth@google.comd830d132013-11-11 20:54:09 +000084}
85
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000086bool GrDistanceFieldTextContext::canDraw(const SkPaint& paint) {
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000087 if (!fEnableDFRendering && !paint.isDistanceFieldTextTEMP()) {
commit-bot@chromium.orgeefd8a02014-04-08 20:14:32 +000088 return false;
89 }
90
skia.committer@gmail.come1d94432014-04-09 03:04:11 +000091 // rasterizers and mask filters modify alpha, which doesn't
commit-bot@chromium.orgeefd8a02014-04-08 20:14:32 +000092 // translate well to distance
93 if (paint.getRasterizer() || paint.getMaskFilter() ||
94 !fContext->getTextTarget()->caps()->shaderDerivativeSupport()) {
95 return false;
96 }
97
98 // TODO: add some stroking support
99 if (paint.getStyle() != SkPaint::kFill_Style) {
100 return false;
101 }
102
103 // TODO: choose an appropriate maximum scale for distance fields and
104 // enable perspective
105 if (SkDraw::ShouldDrawTextAsPaths(paint, fContext->getMatrix())) {
106 return false;
107 }
108
109 // distance fields cannot represent color fonts
110 SkScalerContext::Rec rec;
111 SkScalerContext::MakeRec(paint, &fDeviceProperties, NULL, &rec);
112 return rec.getFormat() != SkMask::kARGB32_Format;
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000113}
114
jvanverth@google.comd830d132013-11-11 20:54:09 +0000115static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
116 unsigned r = SkColorGetR(c);
117 unsigned g = SkColorGetG(c);
118 unsigned b = SkColorGetB(c);
119 return GrColorPackRGBA(r, g, b, 0xff);
120}
121
jvanverth78f07182014-07-30 06:17:59 -0700122void GrDistanceFieldTextContext::setupCoverageEffect(const SkColor& filteredColor) {
123 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode);
124 GrTextureParams gammaParams(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
125
Mike Klein6a25bd02014-08-29 10:03:59 -0400126 uint32_t textureUniqueID = fCurrTexture->getUniqueID();
jvanverth76ce81e2014-09-22 14:26:53 -0700127 const SkMatrix& ctm = fContext->getMatrix();
jvanverth78f07182014-07-30 06:17:59 -0700128
129 // set up any flags
130 uint32_t flags = 0;
jvanverth76ce81e2014-09-22 14:26:53 -0700131 flags |= ctm.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
jvanverth78f07182014-07-30 06:17:59 -0700132 flags |= fUseLCDText ? kUseLCD_DistanceFieldEffectFlag : 0;
jvanverth76ce81e2014-09-22 14:26:53 -0700133 flags |= fUseLCDText && ctm.rectStaysRect() ?
jvanverth78f07182014-07-30 06:17:59 -0700134 kRectToRect_DistanceFieldEffectFlag : 0;
reed4a8126e2014-09-22 07:29:03 -0700135 bool useBGR = SkPixelGeometryIsBGR(fDeviceProperties.fPixelGeometry);
jvanverth78f07182014-07-30 06:17:59 -0700136 flags |= fUseLCDText && useBGR ? kBGR_DistanceFieldEffectFlag : 0;
137
138 // see if we need to create a new effect
139 if (textureUniqueID != fEffectTextureUniqueID ||
140 filteredColor != fEffectColor ||
141 flags != fEffectFlags) {
142 if (fUseLCDText) {
143 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
Mike Klein6a25bd02014-08-29 10:03:59 -0400144 fCachedEffect.reset(GrDistanceFieldLCDTextureEffect::Create(fCurrTexture,
jvanverth78f07182014-07-30 06:17:59 -0700145 params,
146 fGammaTexture,
147 gammaParams,
148 colorNoPreMul,
149 flags));
150 } else {
151#ifdef SK_GAMMA_APPLY_TO_A8
reed4a8126e2014-09-22 07:29:03 -0700152 U8CPU lum = SkColorSpaceLuminance::computeLuminance(fDeviceProperties.getGamma(),
jvanverth78f07182014-07-30 06:17:59 -0700153 filteredColor);
Mike Klein6a25bd02014-08-29 10:03:59 -0400154 fCachedEffect.reset(GrDistanceFieldTextureEffect::Create(fCurrTexture,
jvanverth78f07182014-07-30 06:17:59 -0700155 params,
156 fGammaTexture,
157 gammaParams,
158 lum/255.f,
159 flags));
160#else
Mike Klein6a25bd02014-08-29 10:03:59 -0400161 fCachedEffect.reset(GrDistanceFieldTextureEffect::Create(fCurrTexture,
jvanverth78f07182014-07-30 06:17:59 -0700162 params, flags));
163#endif
164 }
165 fEffectTextureUniqueID = textureUniqueID;
166 fEffectColor = filteredColor;
167 fEffectFlags = flags;
168 }
169
170}
171
jvanverth@google.comd830d132013-11-11 20:54:09 +0000172void GrDistanceFieldTextContext::flushGlyphs() {
173 if (NULL == fDrawTarget) {
174 return;
175 }
176
177 GrDrawState* drawState = fDrawTarget->drawState();
178 GrDrawState::AutoRestoreEffects are(drawState);
jvanverth9564ce62014-09-16 05:45:19 -0700179
jvanverth76ce81e2014-09-22 14:26:53 -0700180 drawState->setFromPaint(fPaint, fContext->getMatrix(), fContext->getRenderTarget());
jvanverth@google.comd830d132013-11-11 20:54:09 +0000181
182 if (fCurrVertex > 0) {
183 // setup our sampler state for our text texture/atlas
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000184 SkASSERT(SkIsAlign4(fCurrVertex));
jvanverth@google.comd830d132013-11-11 20:54:09 +0000185
jvanverth78f07182014-07-30 06:17:59 -0700186 // get our current color
jvanverth2d2a68c2014-06-10 06:42:56 -0700187 SkColor filteredColor;
188 SkColorFilter* colorFilter = fSkPaint.getColorFilter();
bsalomon49f085d2014-09-05 13:34:00 -0700189 if (colorFilter) {
jvanverth2d2a68c2014-06-10 06:42:56 -0700190 filteredColor = colorFilter->filterColor(fSkPaint.getColor());
191 } else {
192 filteredColor = fSkPaint.getColor();
193 }
jvanverth78f07182014-07-30 06:17:59 -0700194 this->setupCoverageEffect(filteredColor);
195
196 // Effects could be stored with one of the cache objects (atlas?)
joshualitt249af152014-09-15 11:41:13 -0700197 drawState->setGeometryProcessor(fCachedEffect.get());
jvanverth78f07182014-07-30 06:17:59 -0700198
199 // Set draw state
commit-bot@chromium.org609ced42014-04-03 18:25:48 +0000200 if (fUseLCDText) {
jvanverth2d2a68c2014-06-10 06:42:56 -0700201 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
jvanverth@google.comd830d132013-11-11 20:54:09 +0000202 if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
203 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
204 fPaint.numColorStages()) {
205 GrPrintf("LCD Text will not draw correctly.\n");
206 }
jvanverthfeceba52014-07-25 19:03:34 -0700207 SkASSERT(!drawState->hasColorVertexAttribute());
jvanverth@google.comd830d132013-11-11 20:54:09 +0000208 // We don't use the GrPaint's color in this case because it's been premultiplied by
209 // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by
210 // the mask texture color. The end result is that we get
211 // mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000212 int a = SkColorGetA(fSkPaint.getColor());
jvanverth@google.comd830d132013-11-11 20:54:09 +0000213 // paintAlpha
214 drawState->setColor(SkColorSetARGB(a, a, a, a));
215 // paintColor
jvanverth2d2a68c2014-06-10 06:42:56 -0700216 drawState->setBlendConstant(colorNoPreMul);
jvanverth@google.comd830d132013-11-11 20:54:09 +0000217 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
218 } else {
219 // set back to normal in case we took LCD path previously.
220 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
jvanverthfeceba52014-07-25 19:03:34 -0700221 // We're using per-vertex color.
222 SkASSERT(drawState->hasColorVertexAttribute());
jvanverth@google.comd830d132013-11-11 20:54:09 +0000223 }
jvanverth@google.comd830d132013-11-11 20:54:09 +0000224 int nGlyphs = fCurrVertex / 4;
225 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
226 fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
227 nGlyphs,
jvanverth1723bfc2014-07-30 09:16:33 -0700228 4, 6, &fVertexBounds);
Mike Klein6a25bd02014-08-29 10:03:59 -0400229 fDrawTarget->resetVertexSource();
230 fVertices = NULL;
231 fMaxVertices = 0;
jvanverth@google.comd830d132013-11-11 20:54:09 +0000232 fCurrVertex = 0;
Mike Klein6a25bd02014-08-29 10:03:59 -0400233 SkSafeSetNull(fCurrTexture);
jvanverth1723bfc2014-07-30 09:16:33 -0700234 fVertexBounds.setLargestInverted();
jvanverth@google.comd830d132013-11-11 20:54:09 +0000235 }
236}
237
jvanverth@google.comd830d132013-11-11 20:54:09 +0000238void GrDistanceFieldTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000239 SkFixed vx, SkFixed vy,
jvanverth@google.comd830d132013-11-11 20:54:09 +0000240 GrFontScaler* scaler) {
Mike Klein6a25bd02014-08-29 10:03:59 -0400241 if (NULL == fDrawTarget) {
242 return;
243 }
244
245 if (NULL == fStrike) {
246 fStrike = fContext->getFontCache()->getStrike(scaler, true);
247 }
248
jvanverth@google.comd830d132013-11-11 20:54:09 +0000249 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
250 if (NULL == glyph || glyph->fBounds.isEmpty()) {
251 return;
252 }
253
254 SkScalar sx = SkFixedToScalar(vx);
255 SkScalar sy = SkFixedToScalar(vy);
256/*
257 // not valid, need to find a different solution for this
258 vx += SkIntToFixed(glyph->fBounds.fLeft);
259 vy += SkIntToFixed(glyph->fBounds.fTop);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000260
jvanverth@google.comd830d132013-11-11 20:54:09 +0000261 // keep them as ints until we've done the clip-test
262 GrFixed width = glyph->fBounds.width();
263 GrFixed height = glyph->fBounds.height();
264
265 // check if we clipped out
266 if (true || NULL == glyph->fPlot) {
267 int x = vx >> 16;
268 int y = vy >> 16;
269 if (fClipRect.quickReject(x, y, x + width, y + height)) {
270// SkCLZ(3); // so we can set a break-point in the debugger
271 return;
272 }
273 }
274*/
275 if (NULL == glyph->fPlot) {
jvanverth681e65b2014-09-19 13:07:38 -0700276 if (!fStrike->glyphTooLargeForAtlas(glyph)) {
277 if (fStrike->addGlyphToAtlas(glyph, scaler)) {
278 goto HAS_ATLAS;
279 }
jvanverth@google.comd830d132013-11-11 20:54:09 +0000280
jvanverth681e65b2014-09-19 13:07:38 -0700281 // try to clear out an unused plot before we flush
282 if (fContext->getFontCache()->freeUnusedPlot(fStrike) &&
283 fStrike->addGlyphToAtlas(glyph, scaler)) {
284 goto HAS_ATLAS;
285 }
jvanverth@google.comd830d132013-11-11 20:54:09 +0000286
jvanverth681e65b2014-09-19 13:07:38 -0700287 if (c_DumpFontCache) {
jvanverth@google.comd830d132013-11-11 20:54:09 +0000288#ifdef SK_DEVELOPER
jvanverth681e65b2014-09-19 13:07:38 -0700289 fContext->getFontCache()->dump();
jvanverth@google.comd830d132013-11-11 20:54:09 +0000290#endif
jvanverth681e65b2014-09-19 13:07:38 -0700291 }
jvanverth@google.comd830d132013-11-11 20:54:09 +0000292
jvanverth681e65b2014-09-19 13:07:38 -0700293 // before we purge the cache, we must flush any accumulated draws
294 this->flushGlyphs();
295 fContext->flush();
jvanverth@google.comd830d132013-11-11 20:54:09 +0000296
jvanverth681e65b2014-09-19 13:07:38 -0700297 // we should have an unused plot now
298 if (fContext->getFontCache()->freeUnusedPlot(fStrike) &&
299 fStrike->addGlyphToAtlas(glyph, scaler)) {
300 goto HAS_ATLAS;
301 }
jvanverth@google.comd830d132013-11-11 20:54:09 +0000302 }
303
304 if (NULL == glyph->fPath) {
305 SkPath* path = SkNEW(SkPath);
306 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
307 // flag the glyph as being dead?
308 delete path;
309 return;
310 }
311 glyph->fPath = path;
312 }
313
314 GrContext::AutoMatrix am;
commit-bot@chromium.org762cd802014-04-14 22:05:07 +0000315 SkMatrix ctm;
316 ctm.setScale(fTextRatio, fTextRatio);
317 ctm.postTranslate(sx, sy);
jvanverth@google.comd830d132013-11-11 20:54:09 +0000318 GrPaint tmpPaint(fPaint);
commit-bot@chromium.org762cd802014-04-14 22:05:07 +0000319 am.setPreConcat(fContext, ctm, &tmpPaint);
egdanield58a0ba2014-06-11 10:30:05 -0700320 GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle);
321 fContext->drawPath(tmpPaint, *glyph->fPath, strokeInfo);
jvanverth@google.comd830d132013-11-11 20:54:09 +0000322 return;
323 }
324
325HAS_ATLAS:
326 SkASSERT(glyph->fPlot);
327 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
328 glyph->fPlot->setDrawToken(drawToken);
329
330 GrTexture* texture = glyph->fPlot->texture();
331 SkASSERT(texture);
332
Mike Klein6a25bd02014-08-29 10:03:59 -0400333 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
334 this->flushGlyphs();
335 fCurrTexture = texture;
336 fCurrTexture->ref();
337 }
338
339 bool useColorVerts = !fUseLCDText;
340
341 if (NULL == fVertices) {
342 // If we need to reserve vertices allow the draw target to suggest
343 // a number of verts to reserve and whether to perform a flush.
344 fMaxVertices = kMinRequestedVerts;
345 if (useColorVerts) {
346 fDrawTarget->drawState()->setVertexAttribs<gTextVertexWithColorAttribs>(
347 SK_ARRAY_COUNT(gTextVertexWithColorAttribs),
348 kTextVAColorSize);
349 } else {
350 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
351 SK_ARRAY_COUNT(gTextVertexAttribs),
352 kTextVASize);
353 }
354 bool flush = fDrawTarget->geometryHints(&fMaxVertices, NULL);
355 if (flush) {
356 this->flushGlyphs();
357 fContext->flush();
358 if (useColorVerts) {
359 fDrawTarget->drawState()->setVertexAttribs<gTextVertexWithColorAttribs>(
360 SK_ARRAY_COUNT(gTextVertexWithColorAttribs),
361 kTextVAColorSize);
362 } else {
363 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
364 SK_ARRAY_COUNT(gTextVertexAttribs),
365 kTextVASize);
366 }
367 }
368 fMaxVertices = kDefaultRequestedVerts;
369 // ignore return, no point in flushing again.
370 fDrawTarget->geometryHints(&fMaxVertices, NULL);
371
372 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
373 if (fMaxVertices < kMinRequestedVerts) {
374 fMaxVertices = kDefaultRequestedVerts;
375 } else if (fMaxVertices > maxQuadVertices) {
376 // don't exceed the limit of the index buffer
377 fMaxVertices = maxQuadVertices;
378 }
379 bool success = fDrawTarget->reserveVertexAndIndexSpace(fMaxVertices,
380 0,
381 &fVertices,
382 NULL);
383 GrAlwaysAssert(success);
384 }
385
commit-bot@chromium.org64b08a12014-04-15 17:53:21 +0000386 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
387 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
388 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2*SK_DistanceFieldInset);
389 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2*SK_DistanceFieldInset);
jvanverth@google.comd830d132013-11-11 20:54:09 +0000390
391 SkScalar scale = fTextRatio;
392 dx *= scale;
393 dy *= scale;
394 sx += dx;
395 sy += dy;
396 width *= scale;
397 height *= scale;
Mike Klein6a25bd02014-08-29 10:03:59 -0400398
commit-bot@chromium.org64b08a12014-04-15 17:53:21 +0000399 SkFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX + SK_DistanceFieldInset);
400 SkFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY + SK_DistanceFieldInset);
401 SkFixed tw = SkIntToFixed(glyph->fBounds.width() - 2*SK_DistanceFieldInset);
402 SkFixed th = SkIntToFixed(glyph->fBounds.height() - 2*SK_DistanceFieldInset);
jvanverth@google.comd830d132013-11-11 20:54:09 +0000403
jvanverth1723bfc2014-07-30 09:16:33 -0700404 SkRect r;
jvanverth9bcd23b2014-08-01 14:05:19 -0700405 r.fLeft = sx;
406 r.fTop = sy;
407 r.fRight = sx + width;
408 r.fBottom = sy + height;
jvanverth1723bfc2014-07-30 09:16:33 -0700409
410 fVertexBounds.growToInclude(r);
411
jvanverthfeceba52014-07-25 19:03:34 -0700412 size_t vertSize = fUseLCDText ? (2 * sizeof(SkPoint))
413 : (2 * sizeof(SkPoint) + sizeof(GrColor));
jvanverth1723bfc2014-07-30 09:16:33 -0700414
egdaniel7b3d5ee2014-08-28 05:41:14 -0700415 SkASSERT(vertSize == fDrawTarget->getDrawState().getVertexStride());
jvanverth1723bfc2014-07-30 09:16:33 -0700416
jvanverthf17bc6c2014-07-25 16:46:53 -0700417 SkPoint* positions = reinterpret_cast<SkPoint*>(
jvanverthfeceba52014-07-25 19:03:34 -0700418 reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVertex);
jvanverth1723bfc2014-07-30 09:16:33 -0700419 positions->setRectFan(r.fLeft, r.fTop, r.fRight, r.fBottom, vertSize);
420
jvanverthfeceba52014-07-25 19:03:34 -0700421 // The texture coords are last in both the with and without color vertex layouts.
jvanverthf17bc6c2014-07-25 16:46:53 -0700422 SkPoint* textureCoords = reinterpret_cast<SkPoint*>(
jvanverthfeceba52014-07-25 19:03:34 -0700423 reinterpret_cast<intptr_t>(positions) + vertSize - sizeof(SkPoint));
jvanverthf17bc6c2014-07-25 16:46:53 -0700424 textureCoords->setRectFan(SkFixedToFloat(texture->normalizeFixedX(tx)),
jvanverthfeceba52014-07-25 19:03:34 -0700425 SkFixedToFloat(texture->normalizeFixedY(ty)),
426 SkFixedToFloat(texture->normalizeFixedX(tx + tw)),
427 SkFixedToFloat(texture->normalizeFixedY(ty + th)),
428 vertSize);
Mike Klein6a25bd02014-08-29 10:03:59 -0400429 if (useColorVerts) {
bsalomon62c447d2014-08-08 08:08:50 -0700430 if (0xFF == GrColorUnpackA(fPaint.getColor())) {
431 fDrawTarget->drawState()->setHint(GrDrawState::kVertexColorsAreOpaque_Hint, true);
432 }
jvanverthfeceba52014-07-25 19:03:34 -0700433 // color comes after position.
434 GrColor* colors = reinterpret_cast<GrColor*>(positions + 1);
435 for (int i = 0; i < 4; ++i) {
436 *colors = fPaint.getColor();
437 colors = reinterpret_cast<GrColor*>(reinterpret_cast<intptr_t>(colors) + vertSize);
438 }
439 }
jvanverth1723bfc2014-07-30 09:16:33 -0700440
jvanverth@google.comd830d132013-11-11 20:54:09 +0000441 fCurrVertex += 4;
442}
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000443
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000444inline void GrDistanceFieldTextContext::init(const GrPaint& paint, const SkPaint& skPaint) {
445 GrTextContext::init(paint, skPaint);
446
447 fStrike = NULL;
448
jvanverth76ce81e2014-09-22 14:26:53 -0700449 const SkMatrix& ctm = fContext->getMatrix();
jvanverth9564ce62014-09-16 05:45:19 -0700450
451 // getMaxScale doesn't support perspective, so neither do we at the moment
jvanverth76ce81e2014-09-22 14:26:53 -0700452 SkASSERT(!ctm.hasPerspective());
453 SkScalar maxScale = ctm.getMaxScale();
jvanverth9564ce62014-09-16 05:45:19 -0700454 SkScalar textSize = fSkPaint.getTextSize();
jvanverth76ce81e2014-09-22 14:26:53 -0700455 SkScalar scaledTextSize = textSize;
456 // if we have non-unity scale, we need to choose our base text size
457 // based on the SkPaint's text size multiplied by the max scale factor
jvanverth9564ce62014-09-16 05:45:19 -0700458 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
459 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
jvanverth76ce81e2014-09-22 14:26:53 -0700460 scaledTextSize *= maxScale;
jvanverth9564ce62014-09-16 05:45:19 -0700461 }
462
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000463 fCurrVertex = 0;
464
465 fVertices = NULL;
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000466
jvanverth76ce81e2014-09-22 14:26:53 -0700467 if (scaledTextSize <= kSmallDFFontLimit) {
jvanverth9564ce62014-09-16 05:45:19 -0700468 fTextRatio = textSize / kSmallDFFontSize;
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000469 fSkPaint.setTextSize(SkIntToScalar(kSmallDFFontSize));
jvanverth76ce81e2014-09-22 14:26:53 -0700470 } else if (scaledTextSize <= kMediumDFFontLimit) {
jvanverth9564ce62014-09-16 05:45:19 -0700471 fTextRatio = textSize / kMediumDFFontSize;
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000472 fSkPaint.setTextSize(SkIntToScalar(kMediumDFFontSize));
473 } else {
jvanverth9564ce62014-09-16 05:45:19 -0700474 fTextRatio = textSize / kLargeDFFontSize;
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000475 fSkPaint.setTextSize(SkIntToScalar(kLargeDFFontSize));
476 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000477
commit-bot@chromium.org609ced42014-04-03 18:25:48 +0000478 fUseLCDText = fSkPaint.isLCDRenderText();
skia.committer@gmail.com221b9112014-04-04 03:04:32 +0000479
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000480 fSkPaint.setLCDRenderText(false);
481 fSkPaint.setAutohinted(false);
jvanverth2d2a68c2014-06-10 06:42:56 -0700482 fSkPaint.setHinting(SkPaint::kNormal_Hinting);
commit-bot@chromium.org0bed43c2014-03-14 21:22:38 +0000483 fSkPaint.setSubpixelText(true);
jvanverth2d2a68c2014-06-10 06:42:56 -0700484
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000485}
486
487inline void GrDistanceFieldTextContext::finish() {
jvanverthfeceba52014-07-25 19:03:34 -0700488 this->flushGlyphs();
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000489
490 GrTextContext::finish();
491}
492
jvanverth2d2a68c2014-06-10 06:42:56 -0700493static void setup_gamma_texture(GrContext* context, const SkGlyphCache* cache,
494 const SkDeviceProperties& deviceProperties,
495 GrTexture** gammaTexture) {
496 if (NULL == *gammaTexture) {
497 int width, height;
498 size_t size;
499
500#ifdef SK_GAMMA_CONTRAST
501 SkScalar contrast = SK_GAMMA_CONTRAST;
502#else
503 SkScalar contrast = 0.5f;
504#endif
reed4a8126e2014-09-22 07:29:03 -0700505 SkScalar paintGamma = deviceProperties.getGamma();
506 SkScalar deviceGamma = deviceProperties.getGamma();
jvanverth2d2a68c2014-06-10 06:42:56 -0700507
508 size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
509 &width, &height);
510
511 SkAutoTArray<uint8_t> data((int)size);
512 SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
513
514 // TODO: Update this to use the cache rather than directly creating a texture.
515 GrTextureDesc desc;
516 desc.fFlags = kDynamicUpdate_GrTextureFlagBit;
517 desc.fWidth = width;
518 desc.fHeight = height;
519 desc.fConfig = kAlpha_8_GrPixelConfig;
520
521 *gammaTexture = context->getGpu()->createTexture(desc, NULL, 0);
522 if (NULL == *gammaTexture) {
523 return;
524 }
525
526 context->writeTexturePixels(*gammaTexture,
527 0, 0, width, height,
528 (*gammaTexture)->config(), data.get(), 0,
529 GrContext::kDontFlush_PixelOpsFlag);
530 }
531}
532
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000533void GrDistanceFieldTextContext::drawText(const GrPaint& paint, const SkPaint& skPaint,
534 const char text[], size_t byteLength,
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000535 SkScalar x, SkScalar y) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000536 SkASSERT(byteLength == 0 || text != NULL);
537
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000538 // nothing to draw or can't draw
539 if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/
540 || fSkPaint.getRasterizer()) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000541 return;
542 }
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000543
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000544 this->init(paint, skPaint);
545
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000546 SkScalar sizeRatio = fTextRatio;
547
548 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
549
jvanverth2d2a68c2014-06-10 06:42:56 -0700550 SkAutoGlyphCacheNoGamma autoCache(fSkPaint, &fDeviceProperties, NULL);
551 SkGlyphCache* cache = autoCache.getCache();
552 GrFontScaler* fontScaler = GetGrFontScaler(cache);
553
554 setup_gamma_texture(fContext, cache, fDeviceProperties, &fGammaTexture);
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000555
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000556 // need to measure first
557 // TODO - generate positions and pre-load cache as well?
558 const char* stop = text + byteLength;
559 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
560 SkFixed stopX = 0;
561 SkFixed stopY = 0;
562
563 const char* textPtr = text;
564 while (textPtr < stop) {
565 // don't need x, y here, since all subpixel variants will have the
566 // same advance
567 const SkGlyph& glyph = glyphCacheProc(cache, &textPtr, 0, 0);
568
569 stopX += glyph.fAdvanceX;
570 stopY += glyph.fAdvanceY;
571 }
572 SkASSERT(textPtr == stop);
573
574 SkScalar alignX = SkFixedToScalar(stopX)*sizeRatio;
575 SkScalar alignY = SkFixedToScalar(stopY)*sizeRatio;
576
577 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
578 alignX = SkScalarHalf(alignX);
579 alignY = SkScalarHalf(alignY);
580 }
581
582 x -= alignX;
583 y -= alignY;
584 }
585
commit-bot@chromium.org5408f8f2014-05-21 19:44:24 +0000586 SkFixed fx = SkScalarToFixed(x);
587 SkFixed fy = SkScalarToFixed(y);
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000588 SkFixed fixedScale = SkScalarToFixed(sizeRatio);
589 while (text < stop) {
commit-bot@chromium.orga9dae712014-03-24 18:34:04 +0000590 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000591
592 if (glyph.fWidth) {
593 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
594 glyph.getSubXFixed(),
595 glyph.getSubYFixed()),
commit-bot@chromium.org5408f8f2014-05-21 19:44:24 +0000596 fx,
597 fy,
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000598 fontScaler);
599 }
600
601 fx += SkFixedMul_portable(glyph.fAdvanceX, fixedScale);
602 fy += SkFixedMul_portable(glyph.fAdvanceY, fixedScale);
603 }
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000604
605 this->finish();
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000606}
607
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000608void GrDistanceFieldTextContext::drawPosText(const GrPaint& paint, const SkPaint& skPaint,
609 const char text[], size_t byteLength,
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000610 const SkScalar pos[], SkScalar constY,
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000611 int scalarsPerPosition) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000612
613 SkASSERT(byteLength == 0 || text != NULL);
614 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
615
616 // nothing to draw
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000617 if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000618 return;
619 }
620
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000621 this->init(paint, skPaint);
622
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000623 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
624
jvanverth2d2a68c2014-06-10 06:42:56 -0700625 SkAutoGlyphCacheNoGamma autoCache(fSkPaint, &fDeviceProperties, NULL);
626 SkGlyphCache* cache = autoCache.getCache();
627 GrFontScaler* fontScaler = GetGrFontScaler(cache);
628
629 setup_gamma_texture(fContext, cache, fDeviceProperties, &fGammaTexture);
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000630
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000631 const char* stop = text + byteLength;
632
633 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
634 while (text < stop) {
635 // the last 2 parameters are ignored
636 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
637
638 if (glyph.fWidth) {
639 SkScalar x = pos[0];
640 SkScalar y = scalarsPerPosition == 1 ? constY : pos[1];
641
642 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
643 glyph.getSubXFixed(),
644 glyph.getSubYFixed()),
commit-bot@chromium.org5408f8f2014-05-21 19:44:24 +0000645 SkScalarToFixed(x),
646 SkScalarToFixed(y),
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000647 fontScaler);
648 }
649 pos += scalarsPerPosition;
650 }
651 } else {
652 int alignShift = SkPaint::kCenter_Align == fSkPaint.getTextAlign() ? 1 : 0;
653 while (text < stop) {
654 // the last 2 parameters are ignored
655 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
656
657 if (glyph.fWidth) {
658 SkScalar x = pos[0];
659 SkScalar y = scalarsPerPosition == 1 ? constY : pos[1];
skia.committer@gmail.com22e96722013-12-20 07:01:36 +0000660
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000661 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
662 glyph.getSubXFixed(),
663 glyph.getSubYFixed()),
commit-bot@chromium.org5408f8f2014-05-21 19:44:24 +0000664 SkScalarToFixed(x) - (glyph.fAdvanceX >> alignShift),
665 SkScalarToFixed(y) - (glyph.fAdvanceY >> alignShift),
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000666 fontScaler);
667 }
668 pos += scalarsPerPosition;
669 }
670 }
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000671
672 this->finish();
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000673}