blob: 429897d6c3d1d470d79ec6f5b1740cd83ebf145b [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
31static const int kGlyphCoordsNoColorAttributeIndex = 1;
32static const int kGlyphCoordsWithColorAttributeIndex = 2;
jvanverth@google.comd830d132013-11-11 20:54:09 +000033
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +000034static const int kSmallDFFontSize = 32;
35static const int kSmallDFFontLimit = 32;
36static const int kMediumDFFontSize = 64;
37static const int kMediumDFFontLimit = 64;
38static const int kLargeDFFontSize = 128;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +000039
jvanverthfeceba52014-07-25 19:03:34 -070040namespace {
41// position + texture coord
42extern const GrVertexAttrib gTextVertexAttribs[] = {
43 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
44 {kVec2f_GrVertexAttribType, sizeof(SkPoint) , kEffect_GrVertexAttribBinding}
45};
46
egdaniel7b3d5ee2014-08-28 05:41:14 -070047static const size_t kTextVASize = 2 * sizeof(SkPoint);
48
jvanverthfeceba52014-07-25 19:03:34 -070049// position + color + texture coord
50extern const GrVertexAttrib gTextVertexWithColorAttribs[] = {
51 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
52 {kVec4ub_GrVertexAttribType, sizeof(SkPoint), kColor_GrVertexAttribBinding},
53 {kVec2f_GrVertexAttribType, sizeof(SkPoint) + sizeof(GrColor), kEffect_GrVertexAttribBinding}
54};
55
egdaniel7b3d5ee2014-08-28 05:41:14 -070056static const size_t kTextVAColorSize = 2 * sizeof(SkPoint) + sizeof(GrColor);
57
jvanverthfeceba52014-07-25 19:03:34 -070058};
jvanverth@google.comd830d132013-11-11 20:54:09 +000059
skia.committer@gmail.come5d70152014-01-29 07:01:48 +000060GrDistanceFieldTextContext::GrDistanceFieldTextContext(GrContext* context,
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000061 const SkDeviceProperties& properties,
62 bool enable)
jvanverth63b9dc82014-08-28 10:39:40 -070063 : GrTextContext(context, properties)
64 , fStrike(NULL)
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000065#if SK_FORCE_DISTANCEFIELD_FONTS
jvanverth63b9dc82014-08-28 10:39:40 -070066 , fEnableDFRendering(true)
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000067#else
jvanverth63b9dc82014-08-28 10:39:40 -070068 , fEnableDFRendering(enable)
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000069#endif
jvanverth63b9dc82014-08-28 10:39:40 -070070 , fEffectTextureUniqueID(SK_InvalidUniqueID)
71 , fEffectColor(GrColor_ILLEGAL)
72 , fEffectFlags(0)
73 , fGammaTexture(NULL)
74 , fVertices(NULL)
75 , fVertexCount(0)
76 , fCurrVertex(0) {
jvanverth1723bfc2014-07-30 09:16:33 -070077 fVertexBounds.setLargestInverted();
jvanverth@google.comd830d132013-11-11 20:54:09 +000078}
79
80GrDistanceFieldTextContext::~GrDistanceFieldTextContext() {
81 this->flushGlyphs();
jvanverth2d2a68c2014-06-10 06:42:56 -070082 SkSafeSetNull(fGammaTexture);
jvanverth@google.comd830d132013-11-11 20:54:09 +000083}
84
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000085bool GrDistanceFieldTextContext::canDraw(const SkPaint& paint) {
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000086 if (!fEnableDFRendering && !paint.isDistanceFieldTextTEMP()) {
commit-bot@chromium.orgeefd8a02014-04-08 20:14:32 +000087 return false;
88 }
89
skia.committer@gmail.come1d94432014-04-09 03:04:11 +000090 // rasterizers and mask filters modify alpha, which doesn't
commit-bot@chromium.orgeefd8a02014-04-08 20:14:32 +000091 // translate well to distance
92 if (paint.getRasterizer() || paint.getMaskFilter() ||
93 !fContext->getTextTarget()->caps()->shaderDerivativeSupport()) {
94 return false;
95 }
96
97 // TODO: add some stroking support
98 if (paint.getStyle() != SkPaint::kFill_Style) {
99 return false;
100 }
101
102 // TODO: choose an appropriate maximum scale for distance fields and
103 // enable perspective
104 if (SkDraw::ShouldDrawTextAsPaths(paint, fContext->getMatrix())) {
105 return false;
106 }
107
108 // distance fields cannot represent color fonts
109 SkScalerContext::Rec rec;
110 SkScalerContext::MakeRec(paint, &fDeviceProperties, NULL, &rec);
111 return rec.getFormat() != SkMask::kARGB32_Format;
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000112}
113
jvanverth@google.comd830d132013-11-11 20:54:09 +0000114static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
115 unsigned r = SkColorGetR(c);
116 unsigned g = SkColorGetG(c);
117 unsigned b = SkColorGetB(c);
118 return GrColorPackRGBA(r, g, b, 0xff);
119}
120
jvanverth78f07182014-07-30 06:17:59 -0700121void GrDistanceFieldTextContext::setupCoverageEffect(const SkColor& filteredColor) {
122 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode);
123 GrTextureParams gammaParams(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
124
jvanverth63b9dc82014-08-28 10:39:40 -0700125 GrTexture* currTexture = fStrike->getTexture();
126 SkASSERT(currTexture);
127 uint32_t textureUniqueID = currTexture->getUniqueID();
jvanverth78f07182014-07-30 06:17:59 -0700128
129 // set up any flags
130 uint32_t flags = 0;
131 flags |= fContext->getMatrix().isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
132 flags |= fUseLCDText ? kUseLCD_DistanceFieldEffectFlag : 0;
133 flags |= fUseLCDText && fContext->getMatrix().rectStaysRect() ?
134 kRectToRect_DistanceFieldEffectFlag : 0;
135 bool useBGR = SkDeviceProperties::Geometry::kBGR_Layout ==
136 fDeviceProperties.fGeometry.getLayout();
137 flags |= fUseLCDText && useBGR ? kBGR_DistanceFieldEffectFlag : 0;
138
139 // see if we need to create a new effect
140 if (textureUniqueID != fEffectTextureUniqueID ||
141 filteredColor != fEffectColor ||
142 flags != fEffectFlags) {
143 if (fUseLCDText) {
144 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
jvanverth63b9dc82014-08-28 10:39:40 -0700145 fCachedEffect.reset(GrDistanceFieldLCDTextureEffect::Create(currTexture,
jvanverth78f07182014-07-30 06:17:59 -0700146 params,
147 fGammaTexture,
148 gammaParams,
149 colorNoPreMul,
150 flags));
151 } else {
152#ifdef SK_GAMMA_APPLY_TO_A8
153 U8CPU lum = SkColorSpaceLuminance::computeLuminance(fDeviceProperties.fGamma,
154 filteredColor);
jvanverth63b9dc82014-08-28 10:39:40 -0700155 fCachedEffect.reset(GrDistanceFieldTextureEffect::Create(currTexture,
jvanverth78f07182014-07-30 06:17:59 -0700156 params,
157 fGammaTexture,
158 gammaParams,
159 lum/255.f,
160 flags));
161#else
jvanverth63b9dc82014-08-28 10:39:40 -0700162 fCachedEffect.reset(GrDistanceFieldTextureEffect::Create(currTexture,
jvanverth78f07182014-07-30 06:17:59 -0700163 params, flags));
164#endif
165 }
166 fEffectTextureUniqueID = textureUniqueID;
167 fEffectColor = filteredColor;
168 fEffectFlags = flags;
169 }
170
171}
172
jvanverth@google.comd830d132013-11-11 20:54:09 +0000173void GrDistanceFieldTextContext::flushGlyphs() {
174 if (NULL == fDrawTarget) {
175 return;
176 }
177
178 GrDrawState* drawState = fDrawTarget->drawState();
179 GrDrawState::AutoRestoreEffects are(drawState);
180 drawState->setFromPaint(fPaint, fContext->getMatrix(), fContext->getRenderTarget());
181
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();
189 if (NULL != colorFilter) {
190 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?)
197 int coordsIdx = drawState->hasColorVertexAttribute() ? kGlyphCoordsWithColorAttributeIndex :
198 kGlyphCoordsNoColorAttributeIndex;
199 drawState->addCoverageEffect(fCachedEffect.get(), coordsIdx);
200
201 // Set draw state
commit-bot@chromium.org609ced42014-04-03 18:25:48 +0000202 if (fUseLCDText) {
jvanverth2d2a68c2014-06-10 06:42:56 -0700203 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
jvanverth@google.comd830d132013-11-11 20:54:09 +0000204 if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
205 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
206 fPaint.numColorStages()) {
207 GrPrintf("LCD Text will not draw correctly.\n");
208 }
jvanverthfeceba52014-07-25 19:03:34 -0700209 SkASSERT(!drawState->hasColorVertexAttribute());
jvanverth@google.comd830d132013-11-11 20:54:09 +0000210 // We don't use the GrPaint's color in this case because it's been premultiplied by
211 // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by
212 // the mask texture color. The end result is that we get
213 // mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000214 int a = SkColorGetA(fSkPaint.getColor());
jvanverth@google.comd830d132013-11-11 20:54:09 +0000215 // paintAlpha
216 drawState->setColor(SkColorSetARGB(a, a, a, a));
217 // paintColor
jvanverth2d2a68c2014-06-10 06:42:56 -0700218 drawState->setBlendConstant(colorNoPreMul);
jvanverth@google.comd830d132013-11-11 20:54:09 +0000219 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
220 } else {
221 // set back to normal in case we took LCD path previously.
222 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
jvanverthfeceba52014-07-25 19:03:34 -0700223 // We're using per-vertex color.
224 SkASSERT(drawState->hasColorVertexAttribute());
jvanverth@google.comd830d132013-11-11 20:54:09 +0000225 }
jvanverth@google.comd830d132013-11-11 20:54:09 +0000226 int nGlyphs = fCurrVertex / 4;
227 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
228 fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
229 nGlyphs,
jvanverth1723bfc2014-07-30 09:16:33 -0700230 4, 6, &fVertexBounds);
jvanverth@google.comd830d132013-11-11 20:54:09 +0000231 fCurrVertex = 0;
jvanverth1723bfc2014-07-30 09:16:33 -0700232 fVertexBounds.setLargestInverted();
jvanverth@google.comd830d132013-11-11 20:54:09 +0000233 }
jvanverth63b9dc82014-08-28 10:39:40 -0700234
235 fDrawTarget->resetVertexSource();
236 fVertices = NULL;
jvanverth@google.comd830d132013-11-11 20:54:09 +0000237}
238
jvanverth@google.comd830d132013-11-11 20:54:09 +0000239void GrDistanceFieldTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000240 SkFixed vx, SkFixed vy,
jvanverth@google.comd830d132013-11-11 20:54:09 +0000241 GrFontScaler* scaler) {
jvanverth@google.comd830d132013-11-11 20:54:09 +0000242 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
243 if (NULL == glyph || glyph->fBounds.isEmpty()) {
244 return;
245 }
246
247 SkScalar sx = SkFixedToScalar(vx);
248 SkScalar sy = SkFixedToScalar(vy);
249/*
250 // not valid, need to find a different solution for this
251 vx += SkIntToFixed(glyph->fBounds.fLeft);
252 vy += SkIntToFixed(glyph->fBounds.fTop);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000253
jvanverth@google.comd830d132013-11-11 20:54:09 +0000254 // keep them as ints until we've done the clip-test
255 GrFixed width = glyph->fBounds.width();
256 GrFixed height = glyph->fBounds.height();
257
258 // check if we clipped out
259 if (true || NULL == glyph->fPlot) {
260 int x = vx >> 16;
261 int y = vy >> 16;
262 if (fClipRect.quickReject(x, y, x + width, y + height)) {
263// SkCLZ(3); // so we can set a break-point in the debugger
264 return;
265 }
266 }
267*/
268 if (NULL == glyph->fPlot) {
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000269 if (fStrike->addGlyphToAtlas(glyph, scaler)) {
jvanverth@google.comd830d132013-11-11 20:54:09 +0000270 goto HAS_ATLAS;
271 }
272
273 // try to clear out an unused plot before we flush
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000274 if (fContext->getFontCache()->freeUnusedPlot(fStrike) &&
275 fStrike->addGlyphToAtlas(glyph, scaler)) {
jvanverth@google.comd830d132013-11-11 20:54:09 +0000276 goto HAS_ATLAS;
277 }
278
279 if (c_DumpFontCache) {
280#ifdef SK_DEVELOPER
281 fContext->getFontCache()->dump();
282#endif
283 }
284
jvanverth63b9dc82014-08-28 10:39:40 -0700285 // flush any accumulated draws to allow us to free up a plot
286 int remainingVertexCount = fVertexCount - fCurrVertex;
jvanverth@google.comd830d132013-11-11 20:54:09 +0000287 this->flushGlyphs();
288 fContext->flush();
289
jvanverth63b9dc82014-08-28 10:39:40 -0700290 // need to reallocate the vertex buffer for the remaining glyphs
291 fVertexCount = remainingVertexCount;
292 bool success = fDrawTarget->reserveVertexAndIndexSpace(fVertexCount,
293 0,
294 &fVertices,
295 NULL);
296 GrAlwaysAssert(success);
297
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000298 // we should have an unused plot now
299 if (fContext->getFontCache()->freeUnusedPlot(fStrike) &&
300 fStrike->addGlyphToAtlas(glyph, scaler)) {
jvanverth@google.comd830d132013-11-11 20:54:09 +0000301 goto HAS_ATLAS;
302 }
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
commit-bot@chromium.org64b08a12014-04-15 17:53:21 +0000333 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
334 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
335 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2*SK_DistanceFieldInset);
336 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2*SK_DistanceFieldInset);
jvanverth@google.comd830d132013-11-11 20:54:09 +0000337
338 SkScalar scale = fTextRatio;
339 dx *= scale;
340 dy *= scale;
341 sx += dx;
342 sy += dy;
343 width *= scale;
344 height *= scale;
jvanverth63b9dc82014-08-28 10:39:40 -0700345
commit-bot@chromium.org64b08a12014-04-15 17:53:21 +0000346 SkFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX + SK_DistanceFieldInset);
347 SkFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY + SK_DistanceFieldInset);
348 SkFixed tw = SkIntToFixed(glyph->fBounds.width() - 2*SK_DistanceFieldInset);
349 SkFixed th = SkIntToFixed(glyph->fBounds.height() - 2*SK_DistanceFieldInset);
jvanverth@google.comd830d132013-11-11 20:54:09 +0000350
jvanverth1723bfc2014-07-30 09:16:33 -0700351 SkRect r;
jvanverth9bcd23b2014-08-01 14:05:19 -0700352 r.fLeft = sx;
353 r.fTop = sy;
354 r.fRight = sx + width;
355 r.fBottom = sy + height;
jvanverth1723bfc2014-07-30 09:16:33 -0700356
357 fVertexBounds.growToInclude(r);
358
jvanverthfeceba52014-07-25 19:03:34 -0700359 size_t vertSize = fUseLCDText ? (2 * sizeof(SkPoint))
360 : (2 * sizeof(SkPoint) + sizeof(GrColor));
jvanverth1723bfc2014-07-30 09:16:33 -0700361
egdaniel7b3d5ee2014-08-28 05:41:14 -0700362 SkASSERT(vertSize == fDrawTarget->getDrawState().getVertexStride());
jvanverth1723bfc2014-07-30 09:16:33 -0700363
jvanverthf17bc6c2014-07-25 16:46:53 -0700364 SkPoint* positions = reinterpret_cast<SkPoint*>(
jvanverthfeceba52014-07-25 19:03:34 -0700365 reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVertex);
jvanverth1723bfc2014-07-30 09:16:33 -0700366 positions->setRectFan(r.fLeft, r.fTop, r.fRight, r.fBottom, vertSize);
367
jvanverthfeceba52014-07-25 19:03:34 -0700368 // The texture coords are last in both the with and without color vertex layouts.
jvanverthf17bc6c2014-07-25 16:46:53 -0700369 SkPoint* textureCoords = reinterpret_cast<SkPoint*>(
jvanverthfeceba52014-07-25 19:03:34 -0700370 reinterpret_cast<intptr_t>(positions) + vertSize - sizeof(SkPoint));
jvanverthf17bc6c2014-07-25 16:46:53 -0700371 textureCoords->setRectFan(SkFixedToFloat(texture->normalizeFixedX(tx)),
jvanverthfeceba52014-07-25 19:03:34 -0700372 SkFixedToFloat(texture->normalizeFixedY(ty)),
373 SkFixedToFloat(texture->normalizeFixedX(tx + tw)),
374 SkFixedToFloat(texture->normalizeFixedY(ty + th)),
375 vertSize);
jvanverth63b9dc82014-08-28 10:39:40 -0700376 if (!fUseLCDText) {
bsalomon62c447d2014-08-08 08:08:50 -0700377 if (0xFF == GrColorUnpackA(fPaint.getColor())) {
378 fDrawTarget->drawState()->setHint(GrDrawState::kVertexColorsAreOpaque_Hint, true);
379 }
jvanverthfeceba52014-07-25 19:03:34 -0700380 // color comes after position.
381 GrColor* colors = reinterpret_cast<GrColor*>(positions + 1);
382 for (int i = 0; i < 4; ++i) {
383 *colors = fPaint.getColor();
384 colors = reinterpret_cast<GrColor*>(reinterpret_cast<intptr_t>(colors) + vertSize);
385 }
386 }
jvanverth1723bfc2014-07-30 09:16:33 -0700387
jvanverth@google.comd830d132013-11-11 20:54:09 +0000388 fCurrVertex += 4;
389}
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000390
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000391inline void GrDistanceFieldTextContext::init(const GrPaint& paint, const SkPaint& skPaint) {
392 GrTextContext::init(paint, skPaint);
393
394 fStrike = NULL;
395
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000396 fCurrVertex = 0;
397
398 fVertices = NULL;
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000399
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000400 if (fSkPaint.getTextSize() <= kSmallDFFontLimit) {
401 fTextRatio = fSkPaint.getTextSize()/kSmallDFFontSize;
402 fSkPaint.setTextSize(SkIntToScalar(kSmallDFFontSize));
403 } else if (fSkPaint.getTextSize() <= kMediumDFFontLimit) {
404 fTextRatio = fSkPaint.getTextSize()/kMediumDFFontSize;
405 fSkPaint.setTextSize(SkIntToScalar(kMediumDFFontSize));
406 } else {
407 fTextRatio = fSkPaint.getTextSize()/kLargeDFFontSize;
408 fSkPaint.setTextSize(SkIntToScalar(kLargeDFFontSize));
409 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000410
commit-bot@chromium.org609ced42014-04-03 18:25:48 +0000411 fUseLCDText = fSkPaint.isLCDRenderText();
skia.committer@gmail.com221b9112014-04-04 03:04:32 +0000412
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000413 fSkPaint.setLCDRenderText(false);
414 fSkPaint.setAutohinted(false);
jvanverth2d2a68c2014-06-10 06:42:56 -0700415 fSkPaint.setHinting(SkPaint::kNormal_Hinting);
commit-bot@chromium.org0bed43c2014-03-14 21:22:38 +0000416 fSkPaint.setSubpixelText(true);
jvanverth2d2a68c2014-06-10 06:42:56 -0700417
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000418}
419
420inline void GrDistanceFieldTextContext::finish() {
jvanverthfeceba52014-07-25 19:03:34 -0700421 this->flushGlyphs();
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000422
423 GrTextContext::finish();
424}
425
jvanverth2d2a68c2014-06-10 06:42:56 -0700426static void setup_gamma_texture(GrContext* context, const SkGlyphCache* cache,
427 const SkDeviceProperties& deviceProperties,
428 GrTexture** gammaTexture) {
429 if (NULL == *gammaTexture) {
430 int width, height;
431 size_t size;
432
433#ifdef SK_GAMMA_CONTRAST
434 SkScalar contrast = SK_GAMMA_CONTRAST;
435#else
436 SkScalar contrast = 0.5f;
437#endif
438 SkScalar paintGamma = deviceProperties.fGamma;
439 SkScalar deviceGamma = deviceProperties.fGamma;
440
441 size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
442 &width, &height);
443
444 SkAutoTArray<uint8_t> data((int)size);
445 SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
446
447 // TODO: Update this to use the cache rather than directly creating a texture.
448 GrTextureDesc desc;
449 desc.fFlags = kDynamicUpdate_GrTextureFlagBit;
450 desc.fWidth = width;
451 desc.fHeight = height;
452 desc.fConfig = kAlpha_8_GrPixelConfig;
453
454 *gammaTexture = context->getGpu()->createTexture(desc, NULL, 0);
455 if (NULL == *gammaTexture) {
456 return;
457 }
458
459 context->writeTexturePixels(*gammaTexture,
460 0, 0, width, height,
461 (*gammaTexture)->config(), data.get(), 0,
462 GrContext::kDontFlush_PixelOpsFlag);
463 }
464}
465
jvanverth63b9dc82014-08-28 10:39:40 -0700466void GrDistanceFieldTextContext::allocateVertices(const char text[], size_t byteLength) {
467 SkASSERT(NULL == fVertices);
468 if (!fUseLCDText) {
469 fDrawTarget->drawState()->setVertexAttribs<gTextVertexWithColorAttribs>(
470 SK_ARRAY_COUNT(gTextVertexWithColorAttribs),
471 kTextVAColorSize);
472 } else {
473 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
474 SK_ARRAY_COUNT(gTextVertexAttribs),
475 kTextVASize);
476 }
477 fVertexCount = 4*fSkPaint.textToGlyphs(text, byteLength, NULL);
478 bool success = fDrawTarget->reserveVertexAndIndexSpace(fVertexCount,
479 0,
480 &fVertices,
481 NULL);
482 GrAlwaysAssert(success);
483}
484
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000485void GrDistanceFieldTextContext::drawText(const GrPaint& paint, const SkPaint& skPaint,
486 const char text[], size_t byteLength,
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000487 SkScalar x, SkScalar y) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000488 SkASSERT(byteLength == 0 || text != NULL);
489
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000490 // nothing to draw or can't draw
491 if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/
492 || fSkPaint.getRasterizer()) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000493 return;
494 }
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000495
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000496 this->init(paint, skPaint);
497
jvanverth63b9dc82014-08-28 10:39:40 -0700498 if (NULL == fDrawTarget) {
499 return;
500 }
501
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000502 SkScalar sizeRatio = fTextRatio;
503
504 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
505
jvanverth2d2a68c2014-06-10 06:42:56 -0700506 SkAutoGlyphCacheNoGamma autoCache(fSkPaint, &fDeviceProperties, NULL);
507 SkGlyphCache* cache = autoCache.getCache();
508 GrFontScaler* fontScaler = GetGrFontScaler(cache);
jvanverth63b9dc82014-08-28 10:39:40 -0700509 if (NULL == fStrike) {
510 fStrike = fContext->getFontCache()->getStrike(fontScaler, true);
511 }
jvanverth2d2a68c2014-06-10 06:42:56 -0700512
513 setup_gamma_texture(fContext, cache, fDeviceProperties, &fGammaTexture);
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000514
jvanverth63b9dc82014-08-28 10:39:40 -0700515 this->allocateVertices(text, byteLength);
516
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000517 // need to measure first
518 // TODO - generate positions and pre-load cache as well?
519 const char* stop = text + byteLength;
520 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
521 SkFixed stopX = 0;
522 SkFixed stopY = 0;
523
524 const char* textPtr = text;
525 while (textPtr < stop) {
526 // don't need x, y here, since all subpixel variants will have the
527 // same advance
528 const SkGlyph& glyph = glyphCacheProc(cache, &textPtr, 0, 0);
529
530 stopX += glyph.fAdvanceX;
531 stopY += glyph.fAdvanceY;
532 }
533 SkASSERT(textPtr == stop);
534
535 SkScalar alignX = SkFixedToScalar(stopX)*sizeRatio;
536 SkScalar alignY = SkFixedToScalar(stopY)*sizeRatio;
537
538 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
539 alignX = SkScalarHalf(alignX);
540 alignY = SkScalarHalf(alignY);
541 }
542
543 x -= alignX;
544 y -= alignY;
545 }
546
commit-bot@chromium.org5408f8f2014-05-21 19:44:24 +0000547 SkFixed fx = SkScalarToFixed(x);
548 SkFixed fy = SkScalarToFixed(y);
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000549 SkFixed fixedScale = SkScalarToFixed(sizeRatio);
550 while (text < stop) {
commit-bot@chromium.orga9dae712014-03-24 18:34:04 +0000551 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000552
553 if (glyph.fWidth) {
554 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
555 glyph.getSubXFixed(),
556 glyph.getSubYFixed()),
commit-bot@chromium.org5408f8f2014-05-21 19:44:24 +0000557 fx,
558 fy,
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000559 fontScaler);
560 }
561
562 fx += SkFixedMul_portable(glyph.fAdvanceX, fixedScale);
563 fy += SkFixedMul_portable(glyph.fAdvanceY, fixedScale);
564 }
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000565
566 this->finish();
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000567}
568
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000569void GrDistanceFieldTextContext::drawPosText(const GrPaint& paint, const SkPaint& skPaint,
570 const char text[], size_t byteLength,
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000571 const SkScalar pos[], SkScalar constY,
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000572 int scalarsPerPosition) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000573
574 SkASSERT(byteLength == 0 || text != NULL);
575 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
576
577 // nothing to draw
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000578 if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000579 return;
580 }
581
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000582 this->init(paint, skPaint);
583
jvanverth63b9dc82014-08-28 10:39:40 -0700584 if (NULL == fDrawTarget) {
585 return;
586 }
587
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000588 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
589
jvanverth2d2a68c2014-06-10 06:42:56 -0700590 SkAutoGlyphCacheNoGamma autoCache(fSkPaint, &fDeviceProperties, NULL);
591 SkGlyphCache* cache = autoCache.getCache();
592 GrFontScaler* fontScaler = GetGrFontScaler(cache);
jvanverth63b9dc82014-08-28 10:39:40 -0700593 if (NULL == fStrike) {
594 fStrike = fContext->getFontCache()->getStrike(fontScaler, true);
595 }
jvanverth2d2a68c2014-06-10 06:42:56 -0700596
597 setup_gamma_texture(fContext, cache, fDeviceProperties, &fGammaTexture);
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000598
jvanverth63b9dc82014-08-28 10:39:40 -0700599 this->allocateVertices(text, byteLength);
600
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000601 const char* stop = text + byteLength;
602
603 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
604 while (text < stop) {
605 // the last 2 parameters are ignored
606 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
607
608 if (glyph.fWidth) {
609 SkScalar x = pos[0];
610 SkScalar y = scalarsPerPosition == 1 ? constY : pos[1];
611
612 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
613 glyph.getSubXFixed(),
614 glyph.getSubYFixed()),
commit-bot@chromium.org5408f8f2014-05-21 19:44:24 +0000615 SkScalarToFixed(x),
616 SkScalarToFixed(y),
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000617 fontScaler);
618 }
619 pos += scalarsPerPosition;
620 }
621 } else {
622 int alignShift = SkPaint::kCenter_Align == fSkPaint.getTextAlign() ? 1 : 0;
623 while (text < stop) {
624 // the last 2 parameters are ignored
625 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
626
627 if (glyph.fWidth) {
628 SkScalar x = pos[0];
629 SkScalar y = scalarsPerPosition == 1 ? constY : pos[1];
skia.committer@gmail.com22e96722013-12-20 07:01:36 +0000630
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000631 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
632 glyph.getSubXFixed(),
633 glyph.getSubYFixed()),
commit-bot@chromium.org5408f8f2014-05-21 19:44:24 +0000634 SkScalarToFixed(x) - (glyph.fAdvanceX >> alignShift),
635 SkScalarToFixed(y) - (glyph.fAdvanceY >> alignShift),
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000636 fontScaler);
637 }
638 pos += scalarsPerPosition;
639 }
640 }
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000641
642 this->finish();
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000643}