blob: dcaee488af1d820aa0cf32bcdba34915d198fcdb [file] [log] [blame]
jvanverth@google.comd830d132013-11-11 20:54:09 +00001/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "GrDistanceFieldTextContext.h"
9#include "GrAtlas.h"
jvanverth8c27a182014-10-14 08:45:50 -070010#include "GrBitmapTextContext.h"
jvanverth@google.comd830d132013-11-11 20:54:09 +000011#include "GrDrawTarget.h"
commit-bot@chromium.org6c89c342014-02-14 21:48:29 +000012#include "GrDrawTargetCaps.h"
jvanverth@google.comd830d132013-11-11 20:54:09 +000013#include "GrFontScaler.h"
jvanverth2d2a68c2014-06-10 06:42:56 -070014#include "GrGpu.h"
jvanverth@google.comd830d132013-11-11 20:54:09 +000015#include "GrIndexBuffer.h"
egdanield58a0ba2014-06-11 10:30:05 -070016#include "GrStrokeInfo.h"
bsalomonafbf2d62014-09-30 12:18:44 -070017#include "GrTexturePriv.h"
jvanverth@google.comd830d132013-11-11 20:54:09 +000018#include "GrTextStrike.h"
19#include "GrTextStrike_impl.h"
bsalomonafbf2d62014-09-30 12:18:44 -070020
jvanverth2b9dc1d2014-10-20 06:48:59 -070021#include "SkAutoKern.h"
bsalomonafbf2d62014-09-30 12:18:44 -070022#include "SkColorFilter.h"
commit-bot@chromium.org64b08a12014-04-15 17:53:21 +000023#include "SkDistanceFieldGen.h"
commit-bot@chromium.org9f94b912014-01-30 15:22:54 +000024#include "SkDraw.h"
bsalomonafbf2d62014-09-30 12:18:44 -070025#include "SkGlyphCache.h"
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +000026#include "SkGpuDevice.h"
jvanverth@google.comd830d132013-11-11 20:54:09 +000027#include "SkPath.h"
28#include "SkRTConf.h"
29#include "SkStrokeRec.h"
30#include "effects/GrDistanceFieldTextureEffect.h"
31
jvanverthfeceba52014-07-25 19:03:34 -070032SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
33 "Dump the contents of the font cache before every purge.");
34
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +000035static const int kSmallDFFontSize = 32;
36static const int kSmallDFFontLimit = 32;
37static const int kMediumDFFontSize = 64;
38static const int kMediumDFFontLimit = 64;
39static const int kLargeDFFontSize = 128;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +000040
jvanverthfeceba52014-07-25 19:03:34 -070041namespace {
42// position + texture coord
43extern const GrVertexAttrib gTextVertexAttribs[] = {
44 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
joshualittb0a8a372014-09-23 09:50:21 -070045 {kVec2f_GrVertexAttribType, sizeof(SkPoint) , kGeometryProcessor_GrVertexAttribBinding}
jvanverthfeceba52014-07-25 19:03:34 -070046};
47
egdaniel7b3d5ee2014-08-28 05:41:14 -070048static const size_t kTextVASize = 2 * sizeof(SkPoint);
49
jvanverthfeceba52014-07-25 19:03:34 -070050// position + color + texture coord
51extern const GrVertexAttrib gTextVertexWithColorAttribs[] = {
52 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
53 {kVec4ub_GrVertexAttribType, sizeof(SkPoint), kColor_GrVertexAttribBinding},
joshualittb0a8a372014-09-23 09:50:21 -070054 {kVec2f_GrVertexAttribType, sizeof(SkPoint) + sizeof(GrColor), kGeometryProcessor_GrVertexAttribBinding}
jvanverthfeceba52014-07-25 19:03:34 -070055};
56
egdaniel7b3d5ee2014-08-28 05:41:14 -070057static const size_t kTextVAColorSize = 2 * sizeof(SkPoint) + sizeof(GrColor);
58
jvanverthfeceba52014-07-25 19:03:34 -070059};
jvanverth@google.comd830d132013-11-11 20:54:09 +000060
skia.committer@gmail.come5d70152014-01-29 07:01:48 +000061GrDistanceFieldTextContext::GrDistanceFieldTextContext(GrContext* context,
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000062 const SkDeviceProperties& properties,
63 bool enable)
Mike Klein6a25bd02014-08-29 10:03:59 -040064 : GrTextContext(context, properties) {
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000065#if SK_FORCE_DISTANCEFIELD_FONTS
Mike Klein6a25bd02014-08-29 10:03:59 -040066 fEnableDFRendering = true;
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000067#else
Mike Klein6a25bd02014-08-29 10:03:59 -040068 fEnableDFRendering = enable;
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000069#endif
Mike Klein6a25bd02014-08-29 10:03:59 -040070 fStrike = NULL;
71 fGammaTexture = NULL;
72
73 fCurrTexture = NULL;
74 fCurrVertex = 0;
75 fEffectTextureUniqueID = SK_InvalidUniqueID;
76 fEffectColor = GrColor_ILLEGAL;
77 fEffectFlags = 0;
78
79 fVertices = NULL;
80 fMaxVertices = 0;
81
jvanverth1723bfc2014-07-30 09:16:33 -070082 fVertexBounds.setLargestInverted();
jvanverth@google.comd830d132013-11-11 20:54:09 +000083}
84
jvanverth8c27a182014-10-14 08:45:50 -070085GrDistanceFieldTextContext* GrDistanceFieldTextContext::Create(GrContext* context,
86 const SkDeviceProperties& props,
87 bool enable) {
88 GrDistanceFieldTextContext* textContext = SkNEW_ARGS(GrDistanceFieldTextContext,
89 (context, props, enable));
90 textContext->fFallbackTextContext = GrBitmapTextContext::Create(context, props);
91
92 return textContext;
93}
94
jvanverth@google.comd830d132013-11-11 20:54:09 +000095GrDistanceFieldTextContext::~GrDistanceFieldTextContext() {
jvanverth0fedb192014-10-08 09:07:27 -070096 this->flush();
jvanverth2d2a68c2014-06-10 06:42:56 -070097 SkSafeSetNull(fGammaTexture);
jvanverth@google.comd830d132013-11-11 20:54:09 +000098}
99
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000100bool GrDistanceFieldTextContext::canDraw(const SkPaint& paint) {
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +0000101 if (!fEnableDFRendering && !paint.isDistanceFieldTextTEMP()) {
commit-bot@chromium.orgeefd8a02014-04-08 20:14:32 +0000102 return false;
103 }
104
skia.committer@gmail.come1d94432014-04-09 03:04:11 +0000105 // rasterizers and mask filters modify alpha, which doesn't
commit-bot@chromium.orgeefd8a02014-04-08 20:14:32 +0000106 // translate well to distance
107 if (paint.getRasterizer() || paint.getMaskFilter() ||
108 !fContext->getTextTarget()->caps()->shaderDerivativeSupport()) {
109 return false;
110 }
111
112 // TODO: add some stroking support
113 if (paint.getStyle() != SkPaint::kFill_Style) {
114 return false;
115 }
116
117 // TODO: choose an appropriate maximum scale for distance fields and
118 // enable perspective
119 if (SkDraw::ShouldDrawTextAsPaths(paint, fContext->getMatrix())) {
120 return false;
121 }
122
123 // distance fields cannot represent color fonts
124 SkScalerContext::Rec rec;
125 SkScalerContext::MakeRec(paint, &fDeviceProperties, NULL, &rec);
126 return rec.getFormat() != SkMask::kARGB32_Format;
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000127}
128
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000129inline void GrDistanceFieldTextContext::init(const GrPaint& paint, const SkPaint& skPaint) {
130 GrTextContext::init(paint, skPaint);
131
132 fStrike = NULL;
133
jvanverth76ce81e2014-09-22 14:26:53 -0700134 const SkMatrix& ctm = fContext->getMatrix();
jvanverth9564ce62014-09-16 05:45:19 -0700135
136 // getMaxScale doesn't support perspective, so neither do we at the moment
jvanverth76ce81e2014-09-22 14:26:53 -0700137 SkASSERT(!ctm.hasPerspective());
138 SkScalar maxScale = ctm.getMaxScale();
jvanverth9564ce62014-09-16 05:45:19 -0700139 SkScalar textSize = fSkPaint.getTextSize();
jvanverth76ce81e2014-09-22 14:26:53 -0700140 SkScalar scaledTextSize = textSize;
141 // if we have non-unity scale, we need to choose our base text size
142 // based on the SkPaint's text size multiplied by the max scale factor
jvanverth9564ce62014-09-16 05:45:19 -0700143 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
144 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
jvanverth76ce81e2014-09-22 14:26:53 -0700145 scaledTextSize *= maxScale;
jvanverth9564ce62014-09-16 05:45:19 -0700146 }
147
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000148 fCurrVertex = 0;
149
150 fVertices = NULL;
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000151
jvanverth76ce81e2014-09-22 14:26:53 -0700152 if (scaledTextSize <= kSmallDFFontLimit) {
jvanverth9564ce62014-09-16 05:45:19 -0700153 fTextRatio = textSize / kSmallDFFontSize;
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000154 fSkPaint.setTextSize(SkIntToScalar(kSmallDFFontSize));
jvanverth76ce81e2014-09-22 14:26:53 -0700155 } else if (scaledTextSize <= kMediumDFFontLimit) {
jvanverth9564ce62014-09-16 05:45:19 -0700156 fTextRatio = textSize / kMediumDFFontSize;
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000157 fSkPaint.setTextSize(SkIntToScalar(kMediumDFFontSize));
158 } else {
jvanverth9564ce62014-09-16 05:45:19 -0700159 fTextRatio = textSize / kLargeDFFontSize;
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000160 fSkPaint.setTextSize(SkIntToScalar(kLargeDFFontSize));
161 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000162
commit-bot@chromium.org609ced42014-04-03 18:25:48 +0000163 fUseLCDText = fSkPaint.isLCDRenderText();
skia.committer@gmail.com221b9112014-04-04 03:04:32 +0000164
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000165 fSkPaint.setLCDRenderText(false);
166 fSkPaint.setAutohinted(false);
jvanverth2d2a68c2014-06-10 06:42:56 -0700167 fSkPaint.setHinting(SkPaint::kNormal_Hinting);
commit-bot@chromium.org0bed43c2014-03-14 21:22:38 +0000168 fSkPaint.setSubpixelText(true);
jvanverth2d2a68c2014-06-10 06:42:56 -0700169
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000170}
171
jvanverth2d2a68c2014-06-10 06:42:56 -0700172static void setup_gamma_texture(GrContext* context, const SkGlyphCache* cache,
173 const SkDeviceProperties& deviceProperties,
174 GrTexture** gammaTexture) {
175 if (NULL == *gammaTexture) {
176 int width, height;
177 size_t size;
178
179#ifdef SK_GAMMA_CONTRAST
180 SkScalar contrast = SK_GAMMA_CONTRAST;
181#else
182 SkScalar contrast = 0.5f;
183#endif
reedb2d77e42014-10-14 08:26:33 -0700184 SkScalar paintGamma = deviceProperties.gamma();
185 SkScalar deviceGamma = deviceProperties.gamma();
jvanverth2d2a68c2014-06-10 06:42:56 -0700186
187 size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
188 &width, &height);
189
190 SkAutoTArray<uint8_t> data((int)size);
191 SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
192
193 // TODO: Update this to use the cache rather than directly creating a texture.
194 GrTextureDesc desc;
195 desc.fFlags = kDynamicUpdate_GrTextureFlagBit;
196 desc.fWidth = width;
197 desc.fHeight = height;
198 desc.fConfig = kAlpha_8_GrPixelConfig;
199
200 *gammaTexture = context->getGpu()->createTexture(desc, NULL, 0);
201 if (NULL == *gammaTexture) {
202 return;
203 }
204
bsalomon81beccc2014-10-13 12:32:55 -0700205 (*gammaTexture)->writePixels(0, 0, width, height,
206 (*gammaTexture)->config(), data.get(), 0,
207 GrContext::kDontFlush_PixelOpsFlag);
jvanverth2d2a68c2014-06-10 06:42:56 -0700208 }
209}
210
jvanverthaab626c2014-10-16 08:04:39 -0700211void GrDistanceFieldTextContext::onDrawText(const GrPaint& paint, const SkPaint& skPaint,
212 const char text[], size_t byteLength,
213 SkScalar x, SkScalar y) {
214 SkASSERT(byteLength == 0 || text != NULL);
215
jvanverth2b9dc1d2014-10-20 06:48:59 -0700216 // nothing to draw
217 if (text == NULL || byteLength == 0) {
jvanverthaab626c2014-10-16 08:04:39 -0700218 return;
219 }
220
jvanverth2b9dc1d2014-10-20 06:48:59 -0700221 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
222 SkAutoGlyphCache autoCache(skPaint, &fDeviceProperties, NULL);
223 SkGlyphCache* cache = autoCache.getCache();
jvanverthaab626c2014-10-16 08:04:39 -0700224
jvanverth2b9dc1d2014-10-20 06:48:59 -0700225 SkTArray<SkScalar> positions;
jvanverthaab626c2014-10-16 08:04:39 -0700226
jvanverth2b9dc1d2014-10-20 06:48:59 -0700227 const char* textPtr = text;
228 SkFixed stopX = 0;
229 SkFixed stopY = 0;
230 SkFixed origin;
231 switch (skPaint.getTextAlign()) {
232 case SkPaint::kRight_Align: origin = SK_Fixed1; break;
233 case SkPaint::kCenter_Align: origin = SK_FixedHalf; break;
234 case SkPaint::kLeft_Align: origin = 0; break;
235 default: SkFAIL("Invalid paint origin"); return;
236 }
jvanverthaab626c2014-10-16 08:04:39 -0700237
jvanverth2b9dc1d2014-10-20 06:48:59 -0700238 SkAutoKern autokern;
jvanverthaab626c2014-10-16 08:04:39 -0700239 const char* stop = text + byteLength;
jvanverth2b9dc1d2014-10-20 06:48:59 -0700240 while (textPtr < stop) {
241 // don't need x, y here, since all subpixel variants will have the
242 // same advance
243 const SkGlyph& glyph = glyphCacheProc(cache, &textPtr, 0, 0);
jvanverthaab626c2014-10-16 08:04:39 -0700244
jvanverth2b9dc1d2014-10-20 06:48:59 -0700245 SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph);
246 positions.push_back(SkFixedToScalar(stopX + SkFixedMul_portable(origin, width)));
jvanverthaab626c2014-10-16 08:04:39 -0700247
jvanverth2b9dc1d2014-10-20 06:48:59 -0700248 SkFixed height = glyph.fAdvanceY;
249 positions.push_back(SkFixedToScalar(stopY + SkFixedMul_portable(origin, height)));
jvanverthaab626c2014-10-16 08:04:39 -0700250
jvanverth2b9dc1d2014-10-20 06:48:59 -0700251 stopX += width;
252 stopY += height;
jvanverthaab626c2014-10-16 08:04:39 -0700253 }
jvanverth2b9dc1d2014-10-20 06:48:59 -0700254 SkASSERT(textPtr == stop);
jvanverthaab626c2014-10-16 08:04:39 -0700255
jvanverth2b9dc1d2014-10-20 06:48:59 -0700256 // now adjust starting point depending on alignment
257 SkScalar alignX = SkFixedToScalar(stopX);
258 SkScalar alignY = SkFixedToScalar(stopY);
259 if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
260 alignX = SkScalarHalf(alignX);
261 alignY = SkScalarHalf(alignY);
262 } else if (skPaint.getTextAlign() == SkPaint::kLeft_Align) {
263 alignX = 0;
264 alignY = 0;
jvanverthaab626c2014-10-16 08:04:39 -0700265 }
jvanverth2b9dc1d2014-10-20 06:48:59 -0700266 x -= alignX;
267 y -= alignY;
268 SkPoint offset = SkPoint::Make(x, y);
jvanverthaab626c2014-10-16 08:04:39 -0700269
jvanverth2b9dc1d2014-10-20 06:48:59 -0700270 this->drawPosText(paint, skPaint, text, byteLength, positions.begin(), 2, offset);
jvanverthaab626c2014-10-16 08:04:39 -0700271}
272
jvanverth8c27a182014-10-14 08:45:50 -0700273void GrDistanceFieldTextContext::onDrawPosText(const GrPaint& paint, const SkPaint& skPaint,
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000274 const char text[], size_t byteLength,
fmalita05c4a432014-09-29 06:29:53 -0700275 const SkScalar pos[], int scalarsPerPosition,
276 const SkPoint& offset) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000277
278 SkASSERT(byteLength == 0 || text != NULL);
279 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
280
281 // nothing to draw
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000282 if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000283 return;
284 }
285
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000286 this->init(paint, skPaint);
287
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000288 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
289
jvanverth2d2a68c2014-06-10 06:42:56 -0700290 SkAutoGlyphCacheNoGamma autoCache(fSkPaint, &fDeviceProperties, NULL);
291 SkGlyphCache* cache = autoCache.getCache();
292 GrFontScaler* fontScaler = GetGrFontScaler(cache);
293
294 setup_gamma_texture(fContext, cache, fDeviceProperties, &fGammaTexture);
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000295
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000296 const char* stop = text + byteLength;
jvanverthfca302c2014-10-20 13:12:54 -0700297 SkTArray<char> fallbackTxt;
298 SkTArray<SkScalar> fallbackPos;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000299
300 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
301 while (text < stop) {
jvanverthfca302c2014-10-20 13:12:54 -0700302 const char* lastText = text;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000303 // the last 2 parameters are ignored
304 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
305
306 if (glyph.fWidth) {
fmalita05c4a432014-09-29 06:29:53 -0700307 SkScalar x = offset.x() + pos[0];
308 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000309
jvanverthfca302c2014-10-20 13:12:54 -0700310 if (!this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
311 glyph.getSubXFixed(),
312 glyph.getSubYFixed()),
313 SkScalarToFixed(x),
314 SkScalarToFixed(y),
315 fontScaler)) {
316 // couldn't append, send to fallback
317 fallbackTxt.push_back_n(text-lastText, lastText);
318 fallbackPos.push_back(pos[0]);
319 if (2 == scalarsPerPosition) {
320 fallbackPos.push_back(pos[1]);
321 }
322 }
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000323 }
324 pos += scalarsPerPosition;
325 }
326 } else {
jvanverth2b9dc1d2014-10-20 06:48:59 -0700327 SkScalar alignMul = SkPaint::kCenter_Align == fSkPaint.getTextAlign() ? SK_ScalarHalf
328 : SK_Scalar1;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000329 while (text < stop) {
jvanverthfca302c2014-10-20 13:12:54 -0700330 const char* lastText = text;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000331 // the last 2 parameters are ignored
332 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
333
334 if (glyph.fWidth) {
fmalita05c4a432014-09-29 06:29:53 -0700335 SkScalar x = offset.x() + pos[0];
336 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
skia.committer@gmail.com22e96722013-12-20 07:01:36 +0000337
jvanverth2b9dc1d2014-10-20 06:48:59 -0700338 SkScalar advanceX = SkFixedToScalar(glyph.fAdvanceX)*alignMul*fTextRatio;
339 SkScalar advanceY = SkFixedToScalar(glyph.fAdvanceY)*alignMul*fTextRatio;
340
jvanverthfca302c2014-10-20 13:12:54 -0700341 if (!this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
342 glyph.getSubXFixed(),
343 glyph.getSubYFixed()),
344 SkScalarToFixed(x - advanceX),
345 SkScalarToFixed(y - advanceY),
346 fontScaler)) {
347 // couldn't append, send to fallback
348 fallbackTxt.push_back_n(text-lastText, lastText);
349 fallbackPos.push_back(pos[0]);
350 if (2 == scalarsPerPosition) {
351 fallbackPos.push_back(pos[1]);
352 }
353 }
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000354 }
355 pos += scalarsPerPosition;
356 }
357 }
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000358
359 this->finish();
jvanverthfca302c2014-10-20 13:12:54 -0700360
361 if (fallbackTxt.count() > 0) {
362 fFallbackTextContext->drawPosText(paint, skPaint, fallbackTxt.begin(), fallbackTxt.count(),
363 fallbackPos.begin(), scalarsPerPosition, offset);
364 }
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000365}
jvanverth0fedb192014-10-08 09:07:27 -0700366
367static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
368 unsigned r = SkColorGetR(c);
369 unsigned g = SkColorGetG(c);
370 unsigned b = SkColorGetB(c);
371 return GrColorPackRGBA(r, g, b, 0xff);
372}
373
374void GrDistanceFieldTextContext::setupCoverageEffect(const SkColor& filteredColor) {
375 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode);
376 GrTextureParams gammaParams(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
377
378 uint32_t textureUniqueID = fCurrTexture->getUniqueID();
379 const SkMatrix& ctm = fContext->getMatrix();
380
381 // set up any flags
382 uint32_t flags = 0;
383 flags |= ctm.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
384 flags |= fUseLCDText ? kUseLCD_DistanceFieldEffectFlag : 0;
385 flags |= fUseLCDText && ctm.rectStaysRect() ?
386 kRectToRect_DistanceFieldEffectFlag : 0;
reedb2d77e42014-10-14 08:26:33 -0700387 bool useBGR = SkPixelGeometryIsBGR(fDeviceProperties.pixelGeometry());
jvanverth0fedb192014-10-08 09:07:27 -0700388 flags |= fUseLCDText && useBGR ? kBGR_DistanceFieldEffectFlag : 0;
389
390 // see if we need to create a new effect
391 if (textureUniqueID != fEffectTextureUniqueID ||
392 filteredColor != fEffectColor ||
393 flags != fEffectFlags) {
394 if (fUseLCDText) {
395 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
396 fCachedGeometryProcessor.reset(GrDistanceFieldLCDTextureEffect::Create(fCurrTexture,
397 params,
398 fGammaTexture,
399 gammaParams,
400 colorNoPreMul,
401 flags));
402 } else {
403#ifdef SK_GAMMA_APPLY_TO_A8
reedb2d77e42014-10-14 08:26:33 -0700404 U8CPU lum = SkColorSpaceLuminance::computeLuminance(fDeviceProperties.gamma(),
jvanverth0fedb192014-10-08 09:07:27 -0700405 filteredColor);
406 fCachedGeometryProcessor.reset(GrDistanceFieldTextureEffect::Create(fCurrTexture,
407 params,
408 fGammaTexture,
409 gammaParams,
410 lum/255.f,
411 flags));
412#else
413 fCachedGeometryProcessor.reset(GrDistanceFieldTextureEffect::Create(fCurrTexture,
414 params, flags));
415#endif
416 }
417 fEffectTextureUniqueID = textureUniqueID;
418 fEffectColor = filteredColor;
419 fEffectFlags = flags;
420 }
421
422}
423
jvanverthfca302c2014-10-20 13:12:54 -0700424// Returns true if this method handled the glyph, false if needs to be passed to fallback
425//
426bool GrDistanceFieldTextContext::appendGlyph(GrGlyph::PackedID packed,
jvanverth0fedb192014-10-08 09:07:27 -0700427 SkFixed vx, SkFixed vy,
428 GrFontScaler* scaler) {
429 if (NULL == fDrawTarget) {
jvanverthfca302c2014-10-20 13:12:54 -0700430 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700431 }
432
433 if (NULL == fStrike) {
434 fStrike = fContext->getFontCache()->getStrike(scaler, true);
435 }
436
437 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
438 if (NULL == glyph || glyph->fBounds.isEmpty()) {
jvanverthfca302c2014-10-20 13:12:54 -0700439 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700440 }
441
jvanverthfca302c2014-10-20 13:12:54 -0700442 // fallback to color glyph support
jvanverth294c3262014-10-10 11:36:12 -0700443 if (kA8_GrMaskFormat != glyph->fMaskFormat) {
jvanverthfca302c2014-10-20 13:12:54 -0700444 return false;
jvanverth294c3262014-10-10 11:36:12 -0700445 }
446
jvanverth0fedb192014-10-08 09:07:27 -0700447 SkScalar sx = SkFixedToScalar(vx);
448 SkScalar sy = SkFixedToScalar(vy);
449/*
450 // not valid, need to find a different solution for this
451 vx += SkIntToFixed(glyph->fBounds.fLeft);
452 vy += SkIntToFixed(glyph->fBounds.fTop);
453
454 // keep them as ints until we've done the clip-test
455 GrFixed width = glyph->fBounds.width();
456 GrFixed height = glyph->fBounds.height();
457
458 // check if we clipped out
459 if (true || NULL == glyph->fPlot) {
460 int x = vx >> 16;
461 int y = vy >> 16;
462 if (fClipRect.quickReject(x, y, x + width, y + height)) {
463// SkCLZ(3); // so we can set a break-point in the debugger
464 return;
465 }
466 }
467*/
468 if (NULL == glyph->fPlot) {
469 if (!fStrike->glyphTooLargeForAtlas(glyph)) {
470 if (fStrike->addGlyphToAtlas(glyph, scaler)) {
471 goto HAS_ATLAS;
472 }
473
474 // try to clear out an unused plot before we flush
jvanverth294c3262014-10-10 11:36:12 -0700475 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
jvanverth0fedb192014-10-08 09:07:27 -0700476 fStrike->addGlyphToAtlas(glyph, scaler)) {
477 goto HAS_ATLAS;
478 }
479
480 if (c_DumpFontCache) {
481#ifdef SK_DEVELOPER
482 fContext->getFontCache()->dump();
483#endif
484 }
485
486 // before we purge the cache, we must flush any accumulated draws
487 this->flush();
488 fContext->flush();
489
490 // we should have an unused plot now
jvanverth294c3262014-10-10 11:36:12 -0700491 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
jvanverth0fedb192014-10-08 09:07:27 -0700492 fStrike->addGlyphToAtlas(glyph, scaler)) {
493 goto HAS_ATLAS;
494 }
495 }
496
497 if (NULL == glyph->fPath) {
498 SkPath* path = SkNEW(SkPath);
499 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
500 // flag the glyph as being dead?
501 delete path;
jvanverthfca302c2014-10-20 13:12:54 -0700502 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700503 }
504 glyph->fPath = path;
505 }
506
bsalomonec87dc62014-10-14 10:52:00 -0700507 // flush any accumulated draws before drawing this glyph as a path.
508 this->flush();
509
jvanverth0fedb192014-10-08 09:07:27 -0700510 GrContext::AutoMatrix am;
511 SkMatrix ctm;
512 ctm.setScale(fTextRatio, fTextRatio);
513 ctm.postTranslate(sx, sy);
514 GrPaint tmpPaint(fPaint);
515 am.setPreConcat(fContext, ctm, &tmpPaint);
516 GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle);
517 fContext->drawPath(tmpPaint, *glyph->fPath, strokeInfo);
jvanverthfca302c2014-10-20 13:12:54 -0700518 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700519 }
520
521HAS_ATLAS:
522 SkASSERT(glyph->fPlot);
523 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
524 glyph->fPlot->setDrawToken(drawToken);
525
526 GrTexture* texture = glyph->fPlot->texture();
527 SkASSERT(texture);
528
529 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
530 this->flush();
531 fCurrTexture = texture;
532 fCurrTexture->ref();
533 }
534
535 bool useColorVerts = !fUseLCDText;
536
537 if (NULL == fVertices) {
538 // If we need to reserve vertices allow the draw target to suggest
539 // a number of verts to reserve and whether to perform a flush.
540 fMaxVertices = kMinRequestedVerts;
541 if (useColorVerts) {
542 fDrawTarget->drawState()->setVertexAttribs<gTextVertexWithColorAttribs>(
543 SK_ARRAY_COUNT(gTextVertexWithColorAttribs), kTextVAColorSize);
544 } else {
545 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
546 SK_ARRAY_COUNT(gTextVertexAttribs), kTextVASize);
547 }
548 bool flush = fDrawTarget->geometryHints(&fMaxVertices, NULL);
549 if (flush) {
550 this->flush();
551 fContext->flush();
552 if (useColorVerts) {
553 fDrawTarget->drawState()->setVertexAttribs<gTextVertexWithColorAttribs>(
554 SK_ARRAY_COUNT(gTextVertexWithColorAttribs), kTextVAColorSize);
555 } else {
556 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
557 SK_ARRAY_COUNT(gTextVertexAttribs), kTextVASize);
558 }
559 }
560 fMaxVertices = kDefaultRequestedVerts;
561 // ignore return, no point in flushing again.
562 fDrawTarget->geometryHints(&fMaxVertices, NULL);
563
564 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
565 if (fMaxVertices < kMinRequestedVerts) {
566 fMaxVertices = kDefaultRequestedVerts;
567 } else if (fMaxVertices > maxQuadVertices) {
568 // don't exceed the limit of the index buffer
569 fMaxVertices = maxQuadVertices;
570 }
571 bool success = fDrawTarget->reserveVertexAndIndexSpace(fMaxVertices,
572 0,
573 &fVertices,
574 NULL);
575 GrAlwaysAssert(success);
576 }
577
578 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
579 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
580 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2*SK_DistanceFieldInset);
581 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2*SK_DistanceFieldInset);
582
583 SkScalar scale = fTextRatio;
584 dx *= scale;
585 dy *= scale;
586 sx += dx;
587 sy += dy;
588 width *= scale;
589 height *= scale;
590
591 SkFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX + SK_DistanceFieldInset);
592 SkFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY + SK_DistanceFieldInset);
593 SkFixed tw = SkIntToFixed(glyph->fBounds.width() - 2*SK_DistanceFieldInset);
594 SkFixed th = SkIntToFixed(glyph->fBounds.height() - 2*SK_DistanceFieldInset);
595
596 SkRect r;
597 r.fLeft = sx;
598 r.fTop = sy;
599 r.fRight = sx + width;
600 r.fBottom = sy + height;
601
602 fVertexBounds.joinNonEmptyArg(r);
603
604 size_t vertSize = fUseLCDText ? (2 * sizeof(SkPoint))
605 : (2 * sizeof(SkPoint) + sizeof(GrColor));
606
607 SkASSERT(vertSize == fDrawTarget->getDrawState().getVertexStride());
608
609 SkPoint* positions = reinterpret_cast<SkPoint*>(
610 reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVertex);
611 positions->setRectFan(r.fLeft, r.fTop, r.fRight, r.fBottom, vertSize);
612
613 // The texture coords are last in both the with and without color vertex layouts.
614 SkPoint* textureCoords = reinterpret_cast<SkPoint*>(
615 reinterpret_cast<intptr_t>(positions) + vertSize - sizeof(SkPoint));
616 textureCoords->setRectFan(SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx)),
617 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty)),
618 SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx + tw)),
619 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty + th)),
620 vertSize);
621 if (useColorVerts) {
622 if (0xFF == GrColorUnpackA(fPaint.getColor())) {
623 fDrawTarget->drawState()->setHint(GrDrawState::kVertexColorsAreOpaque_Hint, true);
624 }
625 // color comes after position.
626 GrColor* colors = reinterpret_cast<GrColor*>(positions + 1);
627 for (int i = 0; i < 4; ++i) {
628 *colors = fPaint.getColor();
629 colors = reinterpret_cast<GrColor*>(reinterpret_cast<intptr_t>(colors) + vertSize);
630 }
631 }
632
633 fCurrVertex += 4;
jvanverthfca302c2014-10-20 13:12:54 -0700634
635 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700636}
637
638void GrDistanceFieldTextContext::flush() {
639 if (NULL == fDrawTarget) {
640 return;
641 }
642
643 GrDrawState* drawState = fDrawTarget->drawState();
644 GrDrawState::AutoRestoreEffects are(drawState);
645
646 drawState->setFromPaint(fPaint, fContext->getMatrix(), fContext->getRenderTarget());
647
648 if (fCurrVertex > 0) {
649 // setup our sampler state for our text texture/atlas
650 SkASSERT(SkIsAlign4(fCurrVertex));
651
652 // get our current color
653 SkColor filteredColor;
654 SkColorFilter* colorFilter = fSkPaint.getColorFilter();
655 if (colorFilter) {
656 filteredColor = colorFilter->filterColor(fSkPaint.getColor());
657 } else {
658 filteredColor = fSkPaint.getColor();
659 }
660 this->setupCoverageEffect(filteredColor);
661
662 // Effects could be stored with one of the cache objects (atlas?)
663 drawState->setGeometryProcessor(fCachedGeometryProcessor.get());
664
665 // Set draw state
666 if (fUseLCDText) {
667 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
668 if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
669 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
670 fPaint.numColorStages()) {
671 GrPrintf("LCD Text will not draw correctly.\n");
672 }
673 SkASSERT(!drawState->hasColorVertexAttribute());
674 // We don't use the GrPaint's color in this case because it's been premultiplied by
675 // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by
676 // the mask texture color. The end result is that we get
677 // mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor
678 int a = SkColorGetA(fSkPaint.getColor());
679 // paintAlpha
680 drawState->setColor(SkColorSetARGB(a, a, a, a));
681 // paintColor
682 drawState->setBlendConstant(colorNoPreMul);
683 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
684 } else {
685 // set back to normal in case we took LCD path previously.
686 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
687 // We're using per-vertex color.
688 SkASSERT(drawState->hasColorVertexAttribute());
689 }
690 int nGlyphs = fCurrVertex / 4;
691 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
692 fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
693 nGlyphs,
694 4, 6, &fVertexBounds);
695 fDrawTarget->resetVertexSource();
696 fVertices = NULL;
697 fMaxVertices = 0;
698 fCurrVertex = 0;
699 SkSafeSetNull(fCurrTexture);
700 fVertexBounds.setLargestInverted();
701 }
702}
703
704inline void GrDistanceFieldTextContext::finish() {
705 this->flush();
706
707 GrTextContext::finish();
708}
709