blob: dc0ea954ec189f50cd47f058acd6a35497a3c2a0 [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)
Mike Klein6a25bd02014-08-29 10:03:59 -040063 : GrTextContext(context, properties) {
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000064#if SK_FORCE_DISTANCEFIELD_FONTS
Mike Klein6a25bd02014-08-29 10:03:59 -040065 fEnableDFRendering = true;
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000066#else
Mike Klein6a25bd02014-08-29 10:03:59 -040067 fEnableDFRendering = enable;
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000068#endif
Mike Klein6a25bd02014-08-29 10:03:59 -040069 fStrike = NULL;
70 fGammaTexture = NULL;
71
72 fCurrTexture = NULL;
73 fCurrVertex = 0;
74 fEffectTextureUniqueID = SK_InvalidUniqueID;
75 fEffectColor = GrColor_ILLEGAL;
76 fEffectFlags = 0;
77
78 fVertices = NULL;
79 fMaxVertices = 0;
80
jvanverth1723bfc2014-07-30 09:16:33 -070081 fVertexBounds.setLargestInverted();
jvanverth@google.comd830d132013-11-11 20:54:09 +000082}
83
84GrDistanceFieldTextContext::~GrDistanceFieldTextContext() {
85 this->flushGlyphs();
jvanverth2d2a68c2014-06-10 06:42:56 -070086 SkSafeSetNull(fGammaTexture);
jvanverth@google.comd830d132013-11-11 20:54:09 +000087}
88
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000089bool GrDistanceFieldTextContext::canDraw(const SkPaint& paint) {
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000090 if (!fEnableDFRendering && !paint.isDistanceFieldTextTEMP()) {
commit-bot@chromium.orgeefd8a02014-04-08 20:14:32 +000091 return false;
92 }
93
skia.committer@gmail.come1d94432014-04-09 03:04:11 +000094 // rasterizers and mask filters modify alpha, which doesn't
commit-bot@chromium.orgeefd8a02014-04-08 20:14:32 +000095 // translate well to distance
96 if (paint.getRasterizer() || paint.getMaskFilter() ||
97 !fContext->getTextTarget()->caps()->shaderDerivativeSupport()) {
98 return false;
99 }
100
101 // TODO: add some stroking support
102 if (paint.getStyle() != SkPaint::kFill_Style) {
103 return false;
104 }
105
106 // TODO: choose an appropriate maximum scale for distance fields and
107 // enable perspective
108 if (SkDraw::ShouldDrawTextAsPaths(paint, fContext->getMatrix())) {
109 return false;
110 }
111
112 // distance fields cannot represent color fonts
113 SkScalerContext::Rec rec;
114 SkScalerContext::MakeRec(paint, &fDeviceProperties, NULL, &rec);
115 return rec.getFormat() != SkMask::kARGB32_Format;
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000116}
117
jvanverth@google.comd830d132013-11-11 20:54:09 +0000118static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
119 unsigned r = SkColorGetR(c);
120 unsigned g = SkColorGetG(c);
121 unsigned b = SkColorGetB(c);
122 return GrColorPackRGBA(r, g, b, 0xff);
123}
124
jvanverth78f07182014-07-30 06:17:59 -0700125void GrDistanceFieldTextContext::setupCoverageEffect(const SkColor& filteredColor) {
126 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode);
127 GrTextureParams gammaParams(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
128
Mike Klein6a25bd02014-08-29 10:03:59 -0400129 uint32_t textureUniqueID = fCurrTexture->getUniqueID();
jvanverth78f07182014-07-30 06:17:59 -0700130
131 // set up any flags
132 uint32_t flags = 0;
133 flags |= fContext->getMatrix().isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
134 flags |= fUseLCDText ? kUseLCD_DistanceFieldEffectFlag : 0;
135 flags |= fUseLCDText && fContext->getMatrix().rectStaysRect() ?
136 kRectToRect_DistanceFieldEffectFlag : 0;
137 bool useBGR = SkDeviceProperties::Geometry::kBGR_Layout ==
138 fDeviceProperties.fGeometry.getLayout();
139 flags |= fUseLCDText && useBGR ? kBGR_DistanceFieldEffectFlag : 0;
140
141 // see if we need to create a new effect
142 if (textureUniqueID != fEffectTextureUniqueID ||
143 filteredColor != fEffectColor ||
144 flags != fEffectFlags) {
145 if (fUseLCDText) {
146 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
Mike Klein6a25bd02014-08-29 10:03:59 -0400147 fCachedEffect.reset(GrDistanceFieldLCDTextureEffect::Create(fCurrTexture,
jvanverth78f07182014-07-30 06:17:59 -0700148 params,
149 fGammaTexture,
150 gammaParams,
151 colorNoPreMul,
152 flags));
153 } else {
154#ifdef SK_GAMMA_APPLY_TO_A8
155 U8CPU lum = SkColorSpaceLuminance::computeLuminance(fDeviceProperties.fGamma,
156 filteredColor);
Mike Klein6a25bd02014-08-29 10:03:59 -0400157 fCachedEffect.reset(GrDistanceFieldTextureEffect::Create(fCurrTexture,
jvanverth78f07182014-07-30 06:17:59 -0700158 params,
159 fGammaTexture,
160 gammaParams,
161 lum/255.f,
162 flags));
163#else
Mike Klein6a25bd02014-08-29 10:03:59 -0400164 fCachedEffect.reset(GrDistanceFieldTextureEffect::Create(fCurrTexture,
jvanverth78f07182014-07-30 06:17:59 -0700165 params, flags));
166#endif
167 }
168 fEffectTextureUniqueID = textureUniqueID;
169 fEffectColor = filteredColor;
170 fEffectFlags = flags;
171 }
172
173}
174
jvanverth@google.comd830d132013-11-11 20:54:09 +0000175void GrDistanceFieldTextContext::flushGlyphs() {
176 if (NULL == fDrawTarget) {
177 return;
178 }
179
180 GrDrawState* drawState = fDrawTarget->drawState();
181 GrDrawState::AutoRestoreEffects are(drawState);
182 drawState->setFromPaint(fPaint, fContext->getMatrix(), fContext->getRenderTarget());
183
184 if (fCurrVertex > 0) {
185 // setup our sampler state for our text texture/atlas
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000186 SkASSERT(SkIsAlign4(fCurrVertex));
jvanverth@google.comd830d132013-11-11 20:54:09 +0000187
jvanverth78f07182014-07-30 06:17:59 -0700188 // get our current color
jvanverth2d2a68c2014-06-10 06:42:56 -0700189 SkColor filteredColor;
190 SkColorFilter* colorFilter = fSkPaint.getColorFilter();
191 if (NULL != colorFilter) {
192 filteredColor = colorFilter->filterColor(fSkPaint.getColor());
193 } else {
194 filteredColor = fSkPaint.getColor();
195 }
jvanverth78f07182014-07-30 06:17:59 -0700196 this->setupCoverageEffect(filteredColor);
197
198 // Effects could be stored with one of the cache objects (atlas?)
199 int coordsIdx = drawState->hasColorVertexAttribute() ? kGlyphCoordsWithColorAttributeIndex :
200 kGlyphCoordsNoColorAttributeIndex;
201 drawState->addCoverageEffect(fCachedEffect.get(), coordsIdx);
202
203 // Set draw state
commit-bot@chromium.org609ced42014-04-03 18:25:48 +0000204 if (fUseLCDText) {
jvanverth2d2a68c2014-06-10 06:42:56 -0700205 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
jvanverth@google.comd830d132013-11-11 20:54:09 +0000206 if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
207 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
208 fPaint.numColorStages()) {
209 GrPrintf("LCD Text will not draw correctly.\n");
210 }
jvanverthfeceba52014-07-25 19:03:34 -0700211 SkASSERT(!drawState->hasColorVertexAttribute());
jvanverth@google.comd830d132013-11-11 20:54:09 +0000212 // We don't use the GrPaint's color in this case because it's been premultiplied by
213 // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by
214 // the mask texture color. The end result is that we get
215 // mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000216 int a = SkColorGetA(fSkPaint.getColor());
jvanverth@google.comd830d132013-11-11 20:54:09 +0000217 // paintAlpha
218 drawState->setColor(SkColorSetARGB(a, a, a, a));
219 // paintColor
jvanverth2d2a68c2014-06-10 06:42:56 -0700220 drawState->setBlendConstant(colorNoPreMul);
jvanverth@google.comd830d132013-11-11 20:54:09 +0000221 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
222 } else {
223 // set back to normal in case we took LCD path previously.
224 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
jvanverthfeceba52014-07-25 19:03:34 -0700225 // We're using per-vertex color.
226 SkASSERT(drawState->hasColorVertexAttribute());
jvanverth@google.comd830d132013-11-11 20:54:09 +0000227 }
jvanverth@google.comd830d132013-11-11 20:54:09 +0000228 int nGlyphs = fCurrVertex / 4;
229 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
230 fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
231 nGlyphs,
jvanverth1723bfc2014-07-30 09:16:33 -0700232 4, 6, &fVertexBounds);
Mike Klein6a25bd02014-08-29 10:03:59 -0400233 fDrawTarget->resetVertexSource();
234 fVertices = NULL;
235 fMaxVertices = 0;
jvanverth@google.comd830d132013-11-11 20:54:09 +0000236 fCurrVertex = 0;
Mike Klein6a25bd02014-08-29 10:03:59 -0400237 SkSafeSetNull(fCurrTexture);
jvanverth1723bfc2014-07-30 09:16:33 -0700238 fVertexBounds.setLargestInverted();
jvanverth@google.comd830d132013-11-11 20:54:09 +0000239 }
240}
241
jvanverth@google.comd830d132013-11-11 20:54:09 +0000242void GrDistanceFieldTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000243 SkFixed vx, SkFixed vy,
jvanverth@google.comd830d132013-11-11 20:54:09 +0000244 GrFontScaler* scaler) {
Mike Klein6a25bd02014-08-29 10:03:59 -0400245 if (NULL == fDrawTarget) {
246 return;
247 }
248
249 if (NULL == fStrike) {
250 fStrike = fContext->getFontCache()->getStrike(scaler, true);
251 }
252
jvanverth@google.comd830d132013-11-11 20:54:09 +0000253 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
254 if (NULL == glyph || glyph->fBounds.isEmpty()) {
255 return;
256 }
257
258 SkScalar sx = SkFixedToScalar(vx);
259 SkScalar sy = SkFixedToScalar(vy);
260/*
261 // not valid, need to find a different solution for this
262 vx += SkIntToFixed(glyph->fBounds.fLeft);
263 vy += SkIntToFixed(glyph->fBounds.fTop);
skia.committer@gmail.com11a253b2013-11-12 07:02:05 +0000264
jvanverth@google.comd830d132013-11-11 20:54:09 +0000265 // keep them as ints until we've done the clip-test
266 GrFixed width = glyph->fBounds.width();
267 GrFixed height = glyph->fBounds.height();
268
269 // check if we clipped out
270 if (true || NULL == glyph->fPlot) {
271 int x = vx >> 16;
272 int y = vy >> 16;
273 if (fClipRect.quickReject(x, y, x + width, y + height)) {
274// SkCLZ(3); // so we can set a break-point in the debugger
275 return;
276 }
277 }
278*/
279 if (NULL == glyph->fPlot) {
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000280 if (fStrike->addGlyphToAtlas(glyph, scaler)) {
jvanverth@google.comd830d132013-11-11 20:54:09 +0000281 goto HAS_ATLAS;
282 }
283
284 // try to clear out an unused plot before we flush
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000285 if (fContext->getFontCache()->freeUnusedPlot(fStrike) &&
286 fStrike->addGlyphToAtlas(glyph, scaler)) {
jvanverth@google.comd830d132013-11-11 20:54:09 +0000287 goto HAS_ATLAS;
288 }
289
290 if (c_DumpFontCache) {
291#ifdef SK_DEVELOPER
292 fContext->getFontCache()->dump();
293#endif
294 }
295
Mike Klein6a25bd02014-08-29 10:03:59 -0400296 // before we purge the cache, we must flush any accumulated draws
jvanverth@google.comd830d132013-11-11 20:54:09 +0000297 this->flushGlyphs();
298 fContext->flush();
299
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000300 // we should have an unused plot now
301 if (fContext->getFontCache()->freeUnusedPlot(fStrike) &&
302 fStrike->addGlyphToAtlas(glyph, scaler)) {
jvanverth@google.comd830d132013-11-11 20:54:09 +0000303 goto HAS_ATLAS;
304 }
305
306 if (NULL == glyph->fPath) {
307 SkPath* path = SkNEW(SkPath);
308 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
309 // flag the glyph as being dead?
310 delete path;
311 return;
312 }
313 glyph->fPath = path;
314 }
315
316 GrContext::AutoMatrix am;
commit-bot@chromium.org762cd802014-04-14 22:05:07 +0000317 SkMatrix ctm;
318 ctm.setScale(fTextRatio, fTextRatio);
319 ctm.postTranslate(sx, sy);
jvanverth@google.comd830d132013-11-11 20:54:09 +0000320 GrPaint tmpPaint(fPaint);
commit-bot@chromium.org762cd802014-04-14 22:05:07 +0000321 am.setPreConcat(fContext, ctm, &tmpPaint);
egdanield58a0ba2014-06-11 10:30:05 -0700322 GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle);
323 fContext->drawPath(tmpPaint, *glyph->fPath, strokeInfo);
jvanverth@google.comd830d132013-11-11 20:54:09 +0000324 return;
325 }
326
327HAS_ATLAS:
328 SkASSERT(glyph->fPlot);
329 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
330 glyph->fPlot->setDrawToken(drawToken);
331
332 GrTexture* texture = glyph->fPlot->texture();
333 SkASSERT(texture);
334
Mike Klein6a25bd02014-08-29 10:03:59 -0400335 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
336 this->flushGlyphs();
337 fCurrTexture = texture;
338 fCurrTexture->ref();
339 }
340
341 bool useColorVerts = !fUseLCDText;
342
343 if (NULL == fVertices) {
344 // If we need to reserve vertices allow the draw target to suggest
345 // a number of verts to reserve and whether to perform a flush.
346 fMaxVertices = kMinRequestedVerts;
347 if (useColorVerts) {
348 fDrawTarget->drawState()->setVertexAttribs<gTextVertexWithColorAttribs>(
349 SK_ARRAY_COUNT(gTextVertexWithColorAttribs),
350 kTextVAColorSize);
351 } else {
352 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
353 SK_ARRAY_COUNT(gTextVertexAttribs),
354 kTextVASize);
355 }
356 bool flush = fDrawTarget->geometryHints(&fMaxVertices, NULL);
357 if (flush) {
358 this->flushGlyphs();
359 fContext->flush();
360 if (useColorVerts) {
361 fDrawTarget->drawState()->setVertexAttribs<gTextVertexWithColorAttribs>(
362 SK_ARRAY_COUNT(gTextVertexWithColorAttribs),
363 kTextVAColorSize);
364 } else {
365 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
366 SK_ARRAY_COUNT(gTextVertexAttribs),
367 kTextVASize);
368 }
369 }
370 fMaxVertices = kDefaultRequestedVerts;
371 // ignore return, no point in flushing again.
372 fDrawTarget->geometryHints(&fMaxVertices, NULL);
373
374 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
375 if (fMaxVertices < kMinRequestedVerts) {
376 fMaxVertices = kDefaultRequestedVerts;
377 } else if (fMaxVertices > maxQuadVertices) {
378 // don't exceed the limit of the index buffer
379 fMaxVertices = maxQuadVertices;
380 }
381 bool success = fDrawTarget->reserveVertexAndIndexSpace(fMaxVertices,
382 0,
383 &fVertices,
384 NULL);
385 GrAlwaysAssert(success);
386 }
387
commit-bot@chromium.org64b08a12014-04-15 17:53:21 +0000388 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
389 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
390 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2*SK_DistanceFieldInset);
391 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2*SK_DistanceFieldInset);
jvanverth@google.comd830d132013-11-11 20:54:09 +0000392
393 SkScalar scale = fTextRatio;
394 dx *= scale;
395 dy *= scale;
396 sx += dx;
397 sy += dy;
398 width *= scale;
399 height *= scale;
Mike Klein6a25bd02014-08-29 10:03:59 -0400400
commit-bot@chromium.org64b08a12014-04-15 17:53:21 +0000401 SkFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX + SK_DistanceFieldInset);
402 SkFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY + SK_DistanceFieldInset);
403 SkFixed tw = SkIntToFixed(glyph->fBounds.width() - 2*SK_DistanceFieldInset);
404 SkFixed th = SkIntToFixed(glyph->fBounds.height() - 2*SK_DistanceFieldInset);
jvanverth@google.comd830d132013-11-11 20:54:09 +0000405
jvanverth1723bfc2014-07-30 09:16:33 -0700406 SkRect r;
jvanverth9bcd23b2014-08-01 14:05:19 -0700407 r.fLeft = sx;
408 r.fTop = sy;
409 r.fRight = sx + width;
410 r.fBottom = sy + height;
jvanverth1723bfc2014-07-30 09:16:33 -0700411
412 fVertexBounds.growToInclude(r);
413
jvanverthfeceba52014-07-25 19:03:34 -0700414 size_t vertSize = fUseLCDText ? (2 * sizeof(SkPoint))
415 : (2 * sizeof(SkPoint) + sizeof(GrColor));
jvanverth1723bfc2014-07-30 09:16:33 -0700416
egdaniel7b3d5ee2014-08-28 05:41:14 -0700417 SkASSERT(vertSize == fDrawTarget->getDrawState().getVertexStride());
jvanverth1723bfc2014-07-30 09:16:33 -0700418
jvanverthf17bc6c2014-07-25 16:46:53 -0700419 SkPoint* positions = reinterpret_cast<SkPoint*>(
jvanverthfeceba52014-07-25 19:03:34 -0700420 reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVertex);
jvanverth1723bfc2014-07-30 09:16:33 -0700421 positions->setRectFan(r.fLeft, r.fTop, r.fRight, r.fBottom, vertSize);
422
jvanverthfeceba52014-07-25 19:03:34 -0700423 // The texture coords are last in both the with and without color vertex layouts.
jvanverthf17bc6c2014-07-25 16:46:53 -0700424 SkPoint* textureCoords = reinterpret_cast<SkPoint*>(
jvanverthfeceba52014-07-25 19:03:34 -0700425 reinterpret_cast<intptr_t>(positions) + vertSize - sizeof(SkPoint));
jvanverthf17bc6c2014-07-25 16:46:53 -0700426 textureCoords->setRectFan(SkFixedToFloat(texture->normalizeFixedX(tx)),
jvanverthfeceba52014-07-25 19:03:34 -0700427 SkFixedToFloat(texture->normalizeFixedY(ty)),
428 SkFixedToFloat(texture->normalizeFixedX(tx + tw)),
429 SkFixedToFloat(texture->normalizeFixedY(ty + th)),
430 vertSize);
Mike Klein6a25bd02014-08-29 10:03:59 -0400431 if (useColorVerts) {
bsalomon62c447d2014-08-08 08:08:50 -0700432 if (0xFF == GrColorUnpackA(fPaint.getColor())) {
433 fDrawTarget->drawState()->setHint(GrDrawState::kVertexColorsAreOpaque_Hint, true);
434 }
jvanverthfeceba52014-07-25 19:03:34 -0700435 // color comes after position.
436 GrColor* colors = reinterpret_cast<GrColor*>(positions + 1);
437 for (int i = 0; i < 4; ++i) {
438 *colors = fPaint.getColor();
439 colors = reinterpret_cast<GrColor*>(reinterpret_cast<intptr_t>(colors) + vertSize);
440 }
441 }
jvanverth1723bfc2014-07-30 09:16:33 -0700442
jvanverth@google.comd830d132013-11-11 20:54:09 +0000443 fCurrVertex += 4;
444}
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000445
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000446inline void GrDistanceFieldTextContext::init(const GrPaint& paint, const SkPaint& skPaint) {
447 GrTextContext::init(paint, skPaint);
448
449 fStrike = NULL;
450
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000451 fCurrVertex = 0;
452
453 fVertices = NULL;
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000454
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000455 if (fSkPaint.getTextSize() <= kSmallDFFontLimit) {
456 fTextRatio = fSkPaint.getTextSize()/kSmallDFFontSize;
457 fSkPaint.setTextSize(SkIntToScalar(kSmallDFFontSize));
458 } else if (fSkPaint.getTextSize() <= kMediumDFFontLimit) {
459 fTextRatio = fSkPaint.getTextSize()/kMediumDFFontSize;
460 fSkPaint.setTextSize(SkIntToScalar(kMediumDFFontSize));
461 } else {
462 fTextRatio = fSkPaint.getTextSize()/kLargeDFFontSize;
463 fSkPaint.setTextSize(SkIntToScalar(kLargeDFFontSize));
464 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000465
commit-bot@chromium.org609ced42014-04-03 18:25:48 +0000466 fUseLCDText = fSkPaint.isLCDRenderText();
skia.committer@gmail.com221b9112014-04-04 03:04:32 +0000467
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000468 fSkPaint.setLCDRenderText(false);
469 fSkPaint.setAutohinted(false);
jvanverth2d2a68c2014-06-10 06:42:56 -0700470 fSkPaint.setHinting(SkPaint::kNormal_Hinting);
commit-bot@chromium.org0bed43c2014-03-14 21:22:38 +0000471 fSkPaint.setSubpixelText(true);
jvanverth2d2a68c2014-06-10 06:42:56 -0700472
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000473}
474
475inline void GrDistanceFieldTextContext::finish() {
jvanverthfeceba52014-07-25 19:03:34 -0700476 this->flushGlyphs();
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000477
478 GrTextContext::finish();
479}
480
jvanverth2d2a68c2014-06-10 06:42:56 -0700481static void setup_gamma_texture(GrContext* context, const SkGlyphCache* cache,
482 const SkDeviceProperties& deviceProperties,
483 GrTexture** gammaTexture) {
484 if (NULL == *gammaTexture) {
485 int width, height;
486 size_t size;
487
488#ifdef SK_GAMMA_CONTRAST
489 SkScalar contrast = SK_GAMMA_CONTRAST;
490#else
491 SkScalar contrast = 0.5f;
492#endif
493 SkScalar paintGamma = deviceProperties.fGamma;
494 SkScalar deviceGamma = deviceProperties.fGamma;
495
496 size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
497 &width, &height);
498
499 SkAutoTArray<uint8_t> data((int)size);
500 SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
501
502 // TODO: Update this to use the cache rather than directly creating a texture.
503 GrTextureDesc desc;
504 desc.fFlags = kDynamicUpdate_GrTextureFlagBit;
505 desc.fWidth = width;
506 desc.fHeight = height;
507 desc.fConfig = kAlpha_8_GrPixelConfig;
508
509 *gammaTexture = context->getGpu()->createTexture(desc, NULL, 0);
510 if (NULL == *gammaTexture) {
511 return;
512 }
513
514 context->writeTexturePixels(*gammaTexture,
515 0, 0, width, height,
516 (*gammaTexture)->config(), data.get(), 0,
517 GrContext::kDontFlush_PixelOpsFlag);
518 }
519}
520
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000521void GrDistanceFieldTextContext::drawText(const GrPaint& paint, const SkPaint& skPaint,
522 const char text[], size_t byteLength,
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000523 SkScalar x, SkScalar y) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000524 SkASSERT(byteLength == 0 || text != NULL);
525
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000526 // nothing to draw or can't draw
527 if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/
528 || fSkPaint.getRasterizer()) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000529 return;
530 }
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000531
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000532 this->init(paint, skPaint);
533
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000534 SkScalar sizeRatio = fTextRatio;
535
536 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
537
jvanverth2d2a68c2014-06-10 06:42:56 -0700538 SkAutoGlyphCacheNoGamma autoCache(fSkPaint, &fDeviceProperties, NULL);
539 SkGlyphCache* cache = autoCache.getCache();
540 GrFontScaler* fontScaler = GetGrFontScaler(cache);
541
542 setup_gamma_texture(fContext, cache, fDeviceProperties, &fGammaTexture);
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000543
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000544 // need to measure first
545 // TODO - generate positions and pre-load cache as well?
546 const char* stop = text + byteLength;
547 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
548 SkFixed stopX = 0;
549 SkFixed stopY = 0;
550
551 const char* textPtr = text;
552 while (textPtr < stop) {
553 // don't need x, y here, since all subpixel variants will have the
554 // same advance
555 const SkGlyph& glyph = glyphCacheProc(cache, &textPtr, 0, 0);
556
557 stopX += glyph.fAdvanceX;
558 stopY += glyph.fAdvanceY;
559 }
560 SkASSERT(textPtr == stop);
561
562 SkScalar alignX = SkFixedToScalar(stopX)*sizeRatio;
563 SkScalar alignY = SkFixedToScalar(stopY)*sizeRatio;
564
565 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
566 alignX = SkScalarHalf(alignX);
567 alignY = SkScalarHalf(alignY);
568 }
569
570 x -= alignX;
571 y -= alignY;
572 }
573
commit-bot@chromium.org5408f8f2014-05-21 19:44:24 +0000574 SkFixed fx = SkScalarToFixed(x);
575 SkFixed fy = SkScalarToFixed(y);
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000576 SkFixed fixedScale = SkScalarToFixed(sizeRatio);
577 while (text < stop) {
commit-bot@chromium.orga9dae712014-03-24 18:34:04 +0000578 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000579
580 if (glyph.fWidth) {
581 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
582 glyph.getSubXFixed(),
583 glyph.getSubYFixed()),
commit-bot@chromium.org5408f8f2014-05-21 19:44:24 +0000584 fx,
585 fy,
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000586 fontScaler);
587 }
588
589 fx += SkFixedMul_portable(glyph.fAdvanceX, fixedScale);
590 fy += SkFixedMul_portable(glyph.fAdvanceY, fixedScale);
591 }
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000592
593 this->finish();
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000594}
595
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000596void GrDistanceFieldTextContext::drawPosText(const GrPaint& paint, const SkPaint& skPaint,
597 const char text[], size_t byteLength,
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000598 const SkScalar pos[], SkScalar constY,
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000599 int scalarsPerPosition) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000600
601 SkASSERT(byteLength == 0 || text != NULL);
602 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
603
604 // nothing to draw
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000605 if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000606 return;
607 }
608
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000609 this->init(paint, skPaint);
610
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000611 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
612
jvanverth2d2a68c2014-06-10 06:42:56 -0700613 SkAutoGlyphCacheNoGamma autoCache(fSkPaint, &fDeviceProperties, NULL);
614 SkGlyphCache* cache = autoCache.getCache();
615 GrFontScaler* fontScaler = GetGrFontScaler(cache);
616
617 setup_gamma_texture(fContext, cache, fDeviceProperties, &fGammaTexture);
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000618
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000619 const char* stop = text + byteLength;
620
621 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
622 while (text < stop) {
623 // the last 2 parameters are ignored
624 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
625
626 if (glyph.fWidth) {
627 SkScalar x = pos[0];
628 SkScalar y = scalarsPerPosition == 1 ? constY : pos[1];
629
630 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
631 glyph.getSubXFixed(),
632 glyph.getSubYFixed()),
commit-bot@chromium.org5408f8f2014-05-21 19:44:24 +0000633 SkScalarToFixed(x),
634 SkScalarToFixed(y),
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000635 fontScaler);
636 }
637 pos += scalarsPerPosition;
638 }
639 } else {
640 int alignShift = SkPaint::kCenter_Align == fSkPaint.getTextAlign() ? 1 : 0;
641 while (text < stop) {
642 // the last 2 parameters are ignored
643 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
644
645 if (glyph.fWidth) {
646 SkScalar x = pos[0];
647 SkScalar y = scalarsPerPosition == 1 ? constY : pos[1];
skia.committer@gmail.com22e96722013-12-20 07:01:36 +0000648
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000649 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
650 glyph.getSubXFixed(),
651 glyph.getSubYFixed()),
commit-bot@chromium.org5408f8f2014-05-21 19:44:24 +0000652 SkScalarToFixed(x) - (glyph.fAdvanceX >> alignShift),
653 SkScalarToFixed(y) - (glyph.fAdvanceY >> alignShift),
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000654 fontScaler);
655 }
656 pos += scalarsPerPosition;
657 }
658 }
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000659
660 this->finish();
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000661}