blob: 07e9a2e21d7e2ed25025fec286ab284319c488d2 [file] [log] [blame]
jvanverth@google.comd830d132013-11-11 20:54:09 +00001/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "GrDistanceFieldTextContext.h"
9#include "GrAtlas.h"
10#include "GrDrawTarget.h"
commit-bot@chromium.org6c89c342014-02-14 21:48:29 +000011#include "GrDrawTargetCaps.h"
jvanverth@google.comd830d132013-11-11 20:54:09 +000012#include "GrFontScaler.h"
jvanverth2d2a68c2014-06-10 06:42:56 -070013#include "GrGpu.h"
jvanverth@google.comd830d132013-11-11 20:54:09 +000014#include "GrIndexBuffer.h"
egdanield58a0ba2014-06-11 10:30:05 -070015#include "GrStrokeInfo.h"
bsalomonafbf2d62014-09-30 12:18:44 -070016#include "GrTexturePriv.h"
jvanverth@google.comd830d132013-11-11 20:54:09 +000017#include "GrTextStrike.h"
18#include "GrTextStrike_impl.h"
bsalomonafbf2d62014-09-30 12:18:44 -070019
20#include "SkColorFilter.h"
commit-bot@chromium.org64b08a12014-04-15 17:53:21 +000021#include "SkDistanceFieldGen.h"
commit-bot@chromium.org9f94b912014-01-30 15:22:54 +000022#include "SkDraw.h"
bsalomonafbf2d62014-09-30 12:18:44 -070023#include "SkGlyphCache.h"
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +000024#include "SkGpuDevice.h"
jvanverth@google.comd830d132013-11-11 20:54:09 +000025#include "SkPath.h"
26#include "SkRTConf.h"
27#include "SkStrokeRec.h"
28#include "effects/GrDistanceFieldTextureEffect.h"
29
jvanverthfeceba52014-07-25 19:03:34 -070030SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
31 "Dump the contents of the font cache before every purge.");
32
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +000033static const int kSmallDFFontSize = 32;
34static const int kSmallDFFontLimit = 32;
35static const int kMediumDFFontSize = 64;
36static const int kMediumDFFontLimit = 64;
37static const int kLargeDFFontSize = 128;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +000038
jvanverthfeceba52014-07-25 19:03:34 -070039namespace {
40// position + texture coord
41extern const GrVertexAttrib gTextVertexAttribs[] = {
42 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
joshualittb0a8a372014-09-23 09:50:21 -070043 {kVec2f_GrVertexAttribType, sizeof(SkPoint) , kGeometryProcessor_GrVertexAttribBinding}
jvanverthfeceba52014-07-25 19:03:34 -070044};
45
egdaniel7b3d5ee2014-08-28 05:41:14 -070046static const size_t kTextVASize = 2 * sizeof(SkPoint);
47
jvanverthfeceba52014-07-25 19:03:34 -070048// position + color + texture coord
49extern const GrVertexAttrib gTextVertexWithColorAttribs[] = {
50 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
51 {kVec4ub_GrVertexAttribType, sizeof(SkPoint), kColor_GrVertexAttribBinding},
joshualittb0a8a372014-09-23 09:50:21 -070052 {kVec2f_GrVertexAttribType, sizeof(SkPoint) + sizeof(GrColor), kGeometryProcessor_GrVertexAttribBinding}
jvanverthfeceba52014-07-25 19:03:34 -070053};
54
egdaniel7b3d5ee2014-08-28 05:41:14 -070055static const size_t kTextVAColorSize = 2 * sizeof(SkPoint) + sizeof(GrColor);
56
jvanverthfeceba52014-07-25 19:03:34 -070057};
jvanverth@google.comd830d132013-11-11 20:54:09 +000058
skia.committer@gmail.come5d70152014-01-29 07:01:48 +000059GrDistanceFieldTextContext::GrDistanceFieldTextContext(GrContext* context,
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000060 const SkDeviceProperties& properties,
61 bool enable)
Mike Klein6a25bd02014-08-29 10:03:59 -040062 : GrTextContext(context, properties) {
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000063#if SK_FORCE_DISTANCEFIELD_FONTS
Mike Klein6a25bd02014-08-29 10:03:59 -040064 fEnableDFRendering = true;
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000065#else
Mike Klein6a25bd02014-08-29 10:03:59 -040066 fEnableDFRendering = enable;
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000067#endif
Mike Klein6a25bd02014-08-29 10:03:59 -040068 fStrike = NULL;
69 fGammaTexture = NULL;
70
71 fCurrTexture = NULL;
72 fCurrVertex = 0;
73 fEffectTextureUniqueID = SK_InvalidUniqueID;
74 fEffectColor = GrColor_ILLEGAL;
75 fEffectFlags = 0;
76
77 fVertices = NULL;
78 fMaxVertices = 0;
79
jvanverth1723bfc2014-07-30 09:16:33 -070080 fVertexBounds.setLargestInverted();
jvanverth@google.comd830d132013-11-11 20:54:09 +000081}
82
83GrDistanceFieldTextContext::~GrDistanceFieldTextContext() {
84 this->flushGlyphs();
jvanverth2d2a68c2014-06-10 06:42:56 -070085 SkSafeSetNull(fGammaTexture);
jvanverth@google.comd830d132013-11-11 20:54:09 +000086}
87
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000088bool GrDistanceFieldTextContext::canDraw(const SkPaint& paint) {
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000089 if (!fEnableDFRendering && !paint.isDistanceFieldTextTEMP()) {
commit-bot@chromium.orgeefd8a02014-04-08 20:14:32 +000090 return false;
91 }
92
skia.committer@gmail.come1d94432014-04-09 03:04:11 +000093 // rasterizers and mask filters modify alpha, which doesn't
commit-bot@chromium.orgeefd8a02014-04-08 20:14:32 +000094 // translate well to distance
95 if (paint.getRasterizer() || paint.getMaskFilter() ||
96 !fContext->getTextTarget()->caps()->shaderDerivativeSupport()) {
97 return false;
98 }
99
100 // TODO: add some stroking support
101 if (paint.getStyle() != SkPaint::kFill_Style) {
102 return false;
103 }
104
105 // TODO: choose an appropriate maximum scale for distance fields and
106 // enable perspective
107 if (SkDraw::ShouldDrawTextAsPaths(paint, fContext->getMatrix())) {
108 return false;
109 }
110
111 // distance fields cannot represent color fonts
112 SkScalerContext::Rec rec;
113 SkScalerContext::MakeRec(paint, &fDeviceProperties, NULL, &rec);
114 return rec.getFormat() != SkMask::kARGB32_Format;
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000115}
116
jvanverth@google.comd830d132013-11-11 20:54:09 +0000117static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
118 unsigned r = SkColorGetR(c);
119 unsigned g = SkColorGetG(c);
120 unsigned b = SkColorGetB(c);
121 return GrColorPackRGBA(r, g, b, 0xff);
122}
123
jvanverth78f07182014-07-30 06:17:59 -0700124void GrDistanceFieldTextContext::setupCoverageEffect(const SkColor& filteredColor) {
125 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode);
126 GrTextureParams gammaParams(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
127
Mike Klein6a25bd02014-08-29 10:03:59 -0400128 uint32_t textureUniqueID = fCurrTexture->getUniqueID();
jvanverth76ce81e2014-09-22 14:26:53 -0700129 const SkMatrix& ctm = fContext->getMatrix();
jvanverth78f07182014-07-30 06:17:59 -0700130
131 // set up any flags
132 uint32_t flags = 0;
jvanverth76ce81e2014-09-22 14:26:53 -0700133 flags |= ctm.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
jvanverth78f07182014-07-30 06:17:59 -0700134 flags |= fUseLCDText ? kUseLCD_DistanceFieldEffectFlag : 0;
jvanverth76ce81e2014-09-22 14:26:53 -0700135 flags |= fUseLCDText && ctm.rectStaysRect() ?
jvanverth78f07182014-07-30 06:17:59 -0700136 kRectToRect_DistanceFieldEffectFlag : 0;
reed4a8126e2014-09-22 07:29:03 -0700137 bool useBGR = SkPixelGeometryIsBGR(fDeviceProperties.fPixelGeometry);
jvanverth78f07182014-07-30 06:17:59 -0700138 flags |= fUseLCDText && useBGR ? kBGR_DistanceFieldEffectFlag : 0;
139
140 // see if we need to create a new effect
141 if (textureUniqueID != fEffectTextureUniqueID ||
142 filteredColor != fEffectColor ||
143 flags != fEffectFlags) {
144 if (fUseLCDText) {
145 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
joshualittb0a8a372014-09-23 09:50:21 -0700146 fCachedGeometryProcessor.reset(
147 GrDistanceFieldLCDTextureEffect::Create(fCurrTexture,
148 params,
149 fGammaTexture,
150 gammaParams,
151 colorNoPreMul,
152 flags));
jvanverth78f07182014-07-30 06:17:59 -0700153 } else {
154#ifdef SK_GAMMA_APPLY_TO_A8
reed4a8126e2014-09-22 07:29:03 -0700155 U8CPU lum = SkColorSpaceLuminance::computeLuminance(fDeviceProperties.getGamma(),
jvanverth78f07182014-07-30 06:17:59 -0700156 filteredColor);
joshualittb0a8a372014-09-23 09:50:21 -0700157 fCachedGeometryProcessor.reset(
158 GrDistanceFieldTextureEffect::Create(fCurrTexture,
159 params,
160 fGammaTexture,
161 gammaParams,
162 lum/255.f,
163 flags));
jvanverth78f07182014-07-30 06:17:59 -0700164#else
joshualittb0a8a372014-09-23 09:50:21 -0700165 fCachedGeometryProcessor.reset(GrDistanceFieldTextureEffect::Create(fCurrTexture,
166 params, flags));
jvanverth78f07182014-07-30 06:17:59 -0700167#endif
168 }
169 fEffectTextureUniqueID = textureUniqueID;
170 fEffectColor = filteredColor;
171 fEffectFlags = flags;
172 }
173
174}
175
jvanverth@google.comd830d132013-11-11 20:54:09 +0000176void GrDistanceFieldTextContext::flushGlyphs() {
177 if (NULL == fDrawTarget) {
178 return;
179 }
180
181 GrDrawState* drawState = fDrawTarget->drawState();
182 GrDrawState::AutoRestoreEffects are(drawState);
jvanverth9564ce62014-09-16 05:45:19 -0700183
jvanverth76ce81e2014-09-22 14:26:53 -0700184 drawState->setFromPaint(fPaint, fContext->getMatrix(), fContext->getRenderTarget());
jvanverth@google.comd830d132013-11-11 20:54:09 +0000185
186 if (fCurrVertex > 0) {
187 // setup our sampler state for our text texture/atlas
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000188 SkASSERT(SkIsAlign4(fCurrVertex));
jvanverth@google.comd830d132013-11-11 20:54:09 +0000189
jvanverth78f07182014-07-30 06:17:59 -0700190 // get our current color
jvanverth2d2a68c2014-06-10 06:42:56 -0700191 SkColor filteredColor;
192 SkColorFilter* colorFilter = fSkPaint.getColorFilter();
bsalomon49f085d2014-09-05 13:34:00 -0700193 if (colorFilter) {
jvanverth2d2a68c2014-06-10 06:42:56 -0700194 filteredColor = colorFilter->filterColor(fSkPaint.getColor());
195 } else {
196 filteredColor = fSkPaint.getColor();
197 }
jvanverth78f07182014-07-30 06:17:59 -0700198 this->setupCoverageEffect(filteredColor);
199
200 // Effects could be stored with one of the cache objects (atlas?)
joshualittb0a8a372014-09-23 09:50:21 -0700201 drawState->setGeometryProcessor(fCachedGeometryProcessor.get());
jvanverth78f07182014-07-30 06:17:59 -0700202
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) {
jvanverth681e65b2014-09-19 13:07:38 -0700280 if (!fStrike->glyphTooLargeForAtlas(glyph)) {
281 if (fStrike->addGlyphToAtlas(glyph, scaler)) {
282 goto HAS_ATLAS;
283 }
jvanverth@google.comd830d132013-11-11 20:54:09 +0000284
jvanverth681e65b2014-09-19 13:07:38 -0700285 // try to clear out an unused plot before we flush
286 if (fContext->getFontCache()->freeUnusedPlot(fStrike) &&
287 fStrike->addGlyphToAtlas(glyph, scaler)) {
288 goto HAS_ATLAS;
289 }
jvanverth@google.comd830d132013-11-11 20:54:09 +0000290
jvanverth681e65b2014-09-19 13:07:38 -0700291 if (c_DumpFontCache) {
jvanverth@google.comd830d132013-11-11 20:54:09 +0000292#ifdef SK_DEVELOPER
jvanverth681e65b2014-09-19 13:07:38 -0700293 fContext->getFontCache()->dump();
jvanverth@google.comd830d132013-11-11 20:54:09 +0000294#endif
jvanverth681e65b2014-09-19 13:07:38 -0700295 }
jvanverth@google.comd830d132013-11-11 20:54:09 +0000296
jvanverth681e65b2014-09-19 13:07:38 -0700297 // before we purge the cache, we must flush any accumulated draws
298 this->flushGlyphs();
299 fContext->flush();
jvanverth@google.comd830d132013-11-11 20:54:09 +0000300
jvanverth681e65b2014-09-19 13:07:38 -0700301 // we should have an unused plot now
302 if (fContext->getFontCache()->freeUnusedPlot(fStrike) &&
303 fStrike->addGlyphToAtlas(glyph, scaler)) {
304 goto HAS_ATLAS;
305 }
jvanverth@google.comd830d132013-11-11 20:54:09 +0000306 }
307
308 if (NULL == glyph->fPath) {
309 SkPath* path = SkNEW(SkPath);
310 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
311 // flag the glyph as being dead?
312 delete path;
313 return;
314 }
315 glyph->fPath = path;
316 }
317
318 GrContext::AutoMatrix am;
commit-bot@chromium.org762cd802014-04-14 22:05:07 +0000319 SkMatrix ctm;
320 ctm.setScale(fTextRatio, fTextRatio);
321 ctm.postTranslate(sx, sy);
jvanverth@google.comd830d132013-11-11 20:54:09 +0000322 GrPaint tmpPaint(fPaint);
commit-bot@chromium.org762cd802014-04-14 22:05:07 +0000323 am.setPreConcat(fContext, ctm, &tmpPaint);
egdanield58a0ba2014-06-11 10:30:05 -0700324 GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle);
325 fContext->drawPath(tmpPaint, *glyph->fPath, strokeInfo);
jvanverth@google.comd830d132013-11-11 20:54:09 +0000326 return;
327 }
328
329HAS_ATLAS:
330 SkASSERT(glyph->fPlot);
331 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
332 glyph->fPlot->setDrawToken(drawToken);
333
334 GrTexture* texture = glyph->fPlot->texture();
335 SkASSERT(texture);
336
Mike Klein6a25bd02014-08-29 10:03:59 -0400337 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
338 this->flushGlyphs();
339 fCurrTexture = texture;
340 fCurrTexture->ref();
341 }
342
343 bool useColorVerts = !fUseLCDText;
344
345 if (NULL == fVertices) {
346 // If we need to reserve vertices allow the draw target to suggest
347 // a number of verts to reserve and whether to perform a flush.
348 fMaxVertices = kMinRequestedVerts;
349 if (useColorVerts) {
350 fDrawTarget->drawState()->setVertexAttribs<gTextVertexWithColorAttribs>(
351 SK_ARRAY_COUNT(gTextVertexWithColorAttribs),
352 kTextVAColorSize);
353 } else {
354 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
355 SK_ARRAY_COUNT(gTextVertexAttribs),
356 kTextVASize);
357 }
358 bool flush = fDrawTarget->geometryHints(&fMaxVertices, NULL);
359 if (flush) {
360 this->flushGlyphs();
361 fContext->flush();
362 if (useColorVerts) {
363 fDrawTarget->drawState()->setVertexAttribs<gTextVertexWithColorAttribs>(
364 SK_ARRAY_COUNT(gTextVertexWithColorAttribs),
365 kTextVAColorSize);
366 } else {
367 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
368 SK_ARRAY_COUNT(gTextVertexAttribs),
369 kTextVASize);
370 }
371 }
372 fMaxVertices = kDefaultRequestedVerts;
373 // ignore return, no point in flushing again.
374 fDrawTarget->geometryHints(&fMaxVertices, NULL);
375
376 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
377 if (fMaxVertices < kMinRequestedVerts) {
378 fMaxVertices = kDefaultRequestedVerts;
379 } else if (fMaxVertices > maxQuadVertices) {
380 // don't exceed the limit of the index buffer
381 fMaxVertices = maxQuadVertices;
382 }
383 bool success = fDrawTarget->reserveVertexAndIndexSpace(fMaxVertices,
384 0,
385 &fVertices,
386 NULL);
387 GrAlwaysAssert(success);
388 }
389
commit-bot@chromium.org64b08a12014-04-15 17:53:21 +0000390 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
391 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
392 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2*SK_DistanceFieldInset);
393 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2*SK_DistanceFieldInset);
jvanverth@google.comd830d132013-11-11 20:54:09 +0000394
395 SkScalar scale = fTextRatio;
396 dx *= scale;
397 dy *= scale;
398 sx += dx;
399 sy += dy;
400 width *= scale;
401 height *= scale;
Mike Klein6a25bd02014-08-29 10:03:59 -0400402
commit-bot@chromium.org64b08a12014-04-15 17:53:21 +0000403 SkFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX + SK_DistanceFieldInset);
404 SkFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY + SK_DistanceFieldInset);
405 SkFixed tw = SkIntToFixed(glyph->fBounds.width() - 2*SK_DistanceFieldInset);
406 SkFixed th = SkIntToFixed(glyph->fBounds.height() - 2*SK_DistanceFieldInset);
jvanverth@google.comd830d132013-11-11 20:54:09 +0000407
jvanverth1723bfc2014-07-30 09:16:33 -0700408 SkRect r;
jvanverth9bcd23b2014-08-01 14:05:19 -0700409 r.fLeft = sx;
410 r.fTop = sy;
411 r.fRight = sx + width;
412 r.fBottom = sy + height;
jvanverth1723bfc2014-07-30 09:16:33 -0700413
414 fVertexBounds.growToInclude(r);
415
jvanverthfeceba52014-07-25 19:03:34 -0700416 size_t vertSize = fUseLCDText ? (2 * sizeof(SkPoint))
417 : (2 * sizeof(SkPoint) + sizeof(GrColor));
jvanverth1723bfc2014-07-30 09:16:33 -0700418
egdaniel7b3d5ee2014-08-28 05:41:14 -0700419 SkASSERT(vertSize == fDrawTarget->getDrawState().getVertexStride());
jvanverth1723bfc2014-07-30 09:16:33 -0700420
jvanverthf17bc6c2014-07-25 16:46:53 -0700421 SkPoint* positions = reinterpret_cast<SkPoint*>(
jvanverthfeceba52014-07-25 19:03:34 -0700422 reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVertex);
jvanverth1723bfc2014-07-30 09:16:33 -0700423 positions->setRectFan(r.fLeft, r.fTop, r.fRight, r.fBottom, vertSize);
424
jvanverthfeceba52014-07-25 19:03:34 -0700425 // The texture coords are last in both the with and without color vertex layouts.
jvanverthf17bc6c2014-07-25 16:46:53 -0700426 SkPoint* textureCoords = reinterpret_cast<SkPoint*>(
jvanverthfeceba52014-07-25 19:03:34 -0700427 reinterpret_cast<intptr_t>(positions) + vertSize - sizeof(SkPoint));
bsalomonafbf2d62014-09-30 12:18:44 -0700428 textureCoords->setRectFan(SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx)),
429 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty)),
430 SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx + tw)),
431 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty + th)),
jvanverthfeceba52014-07-25 19:03:34 -0700432 vertSize);
Mike Klein6a25bd02014-08-29 10:03:59 -0400433 if (useColorVerts) {
bsalomon62c447d2014-08-08 08:08:50 -0700434 if (0xFF == GrColorUnpackA(fPaint.getColor())) {
435 fDrawTarget->drawState()->setHint(GrDrawState::kVertexColorsAreOpaque_Hint, true);
436 }
jvanverthfeceba52014-07-25 19:03:34 -0700437 // color comes after position.
438 GrColor* colors = reinterpret_cast<GrColor*>(positions + 1);
439 for (int i = 0; i < 4; ++i) {
440 *colors = fPaint.getColor();
441 colors = reinterpret_cast<GrColor*>(reinterpret_cast<intptr_t>(colors) + vertSize);
442 }
443 }
jvanverth1723bfc2014-07-30 09:16:33 -0700444
jvanverth@google.comd830d132013-11-11 20:54:09 +0000445 fCurrVertex += 4;
446}
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000447
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000448inline void GrDistanceFieldTextContext::init(const GrPaint& paint, const SkPaint& skPaint) {
449 GrTextContext::init(paint, skPaint);
450
451 fStrike = NULL;
452
jvanverth76ce81e2014-09-22 14:26:53 -0700453 const SkMatrix& ctm = fContext->getMatrix();
jvanverth9564ce62014-09-16 05:45:19 -0700454
455 // getMaxScale doesn't support perspective, so neither do we at the moment
jvanverth76ce81e2014-09-22 14:26:53 -0700456 SkASSERT(!ctm.hasPerspective());
457 SkScalar maxScale = ctm.getMaxScale();
jvanverth9564ce62014-09-16 05:45:19 -0700458 SkScalar textSize = fSkPaint.getTextSize();
jvanverth76ce81e2014-09-22 14:26:53 -0700459 SkScalar scaledTextSize = textSize;
460 // if we have non-unity scale, we need to choose our base text size
461 // based on the SkPaint's text size multiplied by the max scale factor
jvanverth9564ce62014-09-16 05:45:19 -0700462 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
463 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
jvanverth76ce81e2014-09-22 14:26:53 -0700464 scaledTextSize *= maxScale;
jvanverth9564ce62014-09-16 05:45:19 -0700465 }
466
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000467 fCurrVertex = 0;
468
469 fVertices = NULL;
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000470
jvanverth76ce81e2014-09-22 14:26:53 -0700471 if (scaledTextSize <= kSmallDFFontLimit) {
jvanverth9564ce62014-09-16 05:45:19 -0700472 fTextRatio = textSize / kSmallDFFontSize;
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000473 fSkPaint.setTextSize(SkIntToScalar(kSmallDFFontSize));
jvanverth76ce81e2014-09-22 14:26:53 -0700474 } else if (scaledTextSize <= kMediumDFFontLimit) {
jvanverth9564ce62014-09-16 05:45:19 -0700475 fTextRatio = textSize / kMediumDFFontSize;
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000476 fSkPaint.setTextSize(SkIntToScalar(kMediumDFFontSize));
477 } else {
jvanverth9564ce62014-09-16 05:45:19 -0700478 fTextRatio = textSize / kLargeDFFontSize;
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000479 fSkPaint.setTextSize(SkIntToScalar(kLargeDFFontSize));
480 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000481
commit-bot@chromium.org609ced42014-04-03 18:25:48 +0000482 fUseLCDText = fSkPaint.isLCDRenderText();
skia.committer@gmail.com221b9112014-04-04 03:04:32 +0000483
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000484 fSkPaint.setLCDRenderText(false);
485 fSkPaint.setAutohinted(false);
jvanverth2d2a68c2014-06-10 06:42:56 -0700486 fSkPaint.setHinting(SkPaint::kNormal_Hinting);
commit-bot@chromium.org0bed43c2014-03-14 21:22:38 +0000487 fSkPaint.setSubpixelText(true);
jvanverth2d2a68c2014-06-10 06:42:56 -0700488
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000489}
490
491inline void GrDistanceFieldTextContext::finish() {
jvanverthfeceba52014-07-25 19:03:34 -0700492 this->flushGlyphs();
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000493
494 GrTextContext::finish();
495}
496
jvanverth2d2a68c2014-06-10 06:42:56 -0700497static void setup_gamma_texture(GrContext* context, const SkGlyphCache* cache,
498 const SkDeviceProperties& deviceProperties,
499 GrTexture** gammaTexture) {
500 if (NULL == *gammaTexture) {
501 int width, height;
502 size_t size;
503
504#ifdef SK_GAMMA_CONTRAST
505 SkScalar contrast = SK_GAMMA_CONTRAST;
506#else
507 SkScalar contrast = 0.5f;
508#endif
reed4a8126e2014-09-22 07:29:03 -0700509 SkScalar paintGamma = deviceProperties.getGamma();
510 SkScalar deviceGamma = deviceProperties.getGamma();
jvanverth2d2a68c2014-06-10 06:42:56 -0700511
512 size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
513 &width, &height);
514
515 SkAutoTArray<uint8_t> data((int)size);
516 SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
517
518 // TODO: Update this to use the cache rather than directly creating a texture.
519 GrTextureDesc desc;
520 desc.fFlags = kDynamicUpdate_GrTextureFlagBit;
521 desc.fWidth = width;
522 desc.fHeight = height;
523 desc.fConfig = kAlpha_8_GrPixelConfig;
524
525 *gammaTexture = context->getGpu()->createTexture(desc, NULL, 0);
526 if (NULL == *gammaTexture) {
527 return;
528 }
529
530 context->writeTexturePixels(*gammaTexture,
531 0, 0, width, height,
532 (*gammaTexture)->config(), data.get(), 0,
533 GrContext::kDontFlush_PixelOpsFlag);
534 }
535}
536
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000537void GrDistanceFieldTextContext::drawText(const GrPaint& paint, const SkPaint& skPaint,
538 const char text[], size_t byteLength,
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000539 SkScalar x, SkScalar y) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000540 SkASSERT(byteLength == 0 || text != NULL);
541
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000542 // nothing to draw or can't draw
543 if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/
544 || fSkPaint.getRasterizer()) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000545 return;
546 }
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000547
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000548 this->init(paint, skPaint);
549
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000550 SkScalar sizeRatio = fTextRatio;
551
552 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
553
jvanverth2d2a68c2014-06-10 06:42:56 -0700554 SkAutoGlyphCacheNoGamma autoCache(fSkPaint, &fDeviceProperties, NULL);
555 SkGlyphCache* cache = autoCache.getCache();
556 GrFontScaler* fontScaler = GetGrFontScaler(cache);
557
558 setup_gamma_texture(fContext, cache, fDeviceProperties, &fGammaTexture);
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000559
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000560 // need to measure first
561 // TODO - generate positions and pre-load cache as well?
562 const char* stop = text + byteLength;
563 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
564 SkFixed stopX = 0;
565 SkFixed stopY = 0;
566
567 const char* textPtr = text;
568 while (textPtr < stop) {
569 // don't need x, y here, since all subpixel variants will have the
570 // same advance
571 const SkGlyph& glyph = glyphCacheProc(cache, &textPtr, 0, 0);
572
573 stopX += glyph.fAdvanceX;
574 stopY += glyph.fAdvanceY;
575 }
576 SkASSERT(textPtr == stop);
577
578 SkScalar alignX = SkFixedToScalar(stopX)*sizeRatio;
579 SkScalar alignY = SkFixedToScalar(stopY)*sizeRatio;
580
581 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
582 alignX = SkScalarHalf(alignX);
583 alignY = SkScalarHalf(alignY);
584 }
585
586 x -= alignX;
587 y -= alignY;
588 }
589
commit-bot@chromium.org5408f8f2014-05-21 19:44:24 +0000590 SkFixed fx = SkScalarToFixed(x);
591 SkFixed fy = SkScalarToFixed(y);
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000592 SkFixed fixedScale = SkScalarToFixed(sizeRatio);
593 while (text < stop) {
commit-bot@chromium.orga9dae712014-03-24 18:34:04 +0000594 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000595
596 if (glyph.fWidth) {
597 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
598 glyph.getSubXFixed(),
599 glyph.getSubYFixed()),
commit-bot@chromium.org5408f8f2014-05-21 19:44:24 +0000600 fx,
601 fy,
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000602 fontScaler);
603 }
604
605 fx += SkFixedMul_portable(glyph.fAdvanceX, fixedScale);
606 fy += SkFixedMul_portable(glyph.fAdvanceY, fixedScale);
607 }
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000608
609 this->finish();
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000610}
611
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000612void GrDistanceFieldTextContext::drawPosText(const GrPaint& paint, const SkPaint& skPaint,
613 const char text[], size_t byteLength,
fmalita05c4a432014-09-29 06:29:53 -0700614 const SkScalar pos[], int scalarsPerPosition,
615 const SkPoint& offset) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000616
617 SkASSERT(byteLength == 0 || text != NULL);
618 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
619
620 // nothing to draw
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000621 if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000622 return;
623 }
624
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000625 this->init(paint, skPaint);
626
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000627 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
628
jvanverth2d2a68c2014-06-10 06:42:56 -0700629 SkAutoGlyphCacheNoGamma autoCache(fSkPaint, &fDeviceProperties, NULL);
630 SkGlyphCache* cache = autoCache.getCache();
631 GrFontScaler* fontScaler = GetGrFontScaler(cache);
632
633 setup_gamma_texture(fContext, cache, fDeviceProperties, &fGammaTexture);
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000634
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000635 const char* stop = text + byteLength;
636
637 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
638 while (text < stop) {
639 // the last 2 parameters are ignored
640 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
641
642 if (glyph.fWidth) {
fmalita05c4a432014-09-29 06:29:53 -0700643 SkScalar x = offset.x() + pos[0];
644 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000645
646 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
647 glyph.getSubXFixed(),
648 glyph.getSubYFixed()),
commit-bot@chromium.org5408f8f2014-05-21 19:44:24 +0000649 SkScalarToFixed(x),
650 SkScalarToFixed(y),
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000651 fontScaler);
652 }
653 pos += scalarsPerPosition;
654 }
655 } else {
656 int alignShift = SkPaint::kCenter_Align == fSkPaint.getTextAlign() ? 1 : 0;
657 while (text < stop) {
658 // the last 2 parameters are ignored
659 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
660
661 if (glyph.fWidth) {
fmalita05c4a432014-09-29 06:29:53 -0700662 SkScalar x = offset.x() + pos[0];
663 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
skia.committer@gmail.com22e96722013-12-20 07:01:36 +0000664
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000665 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
666 glyph.getSubXFixed(),
667 glyph.getSubYFixed()),
commit-bot@chromium.org5408f8f2014-05-21 19:44:24 +0000668 SkScalarToFixed(x) - (glyph.fAdvanceX >> alignShift),
669 SkScalarToFixed(y) - (glyph.fAdvanceY >> alignShift),
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000670 fontScaler);
671 }
672 pos += scalarsPerPosition;
673 }
674 }
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000675
676 this->finish();
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000677}