blob: 5e23ba0683e1bbb7b99caa99f42add7eb0f38f4c [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
jvanverth73f10532014-10-23 11:57:12 -070059static const int kVerticesPerGlyph = 4;
60static const int kIndicesPerGlyph = 6;
jvanverthfeceba52014-07-25 19:03:34 -070061};
jvanverth@google.comd830d132013-11-11 20:54:09 +000062
skia.committer@gmail.come5d70152014-01-29 07:01:48 +000063GrDistanceFieldTextContext::GrDistanceFieldTextContext(GrContext* context,
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000064 const SkDeviceProperties& properties,
65 bool enable)
Mike Klein6a25bd02014-08-29 10:03:59 -040066 : GrTextContext(context, properties) {
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000067#if SK_FORCE_DISTANCEFIELD_FONTS
Mike Klein6a25bd02014-08-29 10:03:59 -040068 fEnableDFRendering = true;
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000069#else
Mike Klein6a25bd02014-08-29 10:03:59 -040070 fEnableDFRendering = enable;
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000071#endif
Mike Klein6a25bd02014-08-29 10:03:59 -040072 fStrike = NULL;
73 fGammaTexture = NULL;
74
Mike Klein6a25bd02014-08-29 10:03:59 -040075 fEffectTextureUniqueID = SK_InvalidUniqueID;
76 fEffectColor = GrColor_ILLEGAL;
jvanverth6d22eca2014-10-28 11:10:48 -070077 fEffectFlags = kInvalid_DistanceFieldEffectFlag;
Mike Klein6a25bd02014-08-29 10:03:59 -040078
79 fVertices = NULL;
jvanverth73f10532014-10-23 11:57:12 -070080 fCurrVertex = 0;
81 fAllocVertexCount = 0;
82 fTotalVertexCount = 0;
83 fCurrTexture = NULL;
Mike Klein6a25bd02014-08-29 10:03:59 -040084
jvanverth1723bfc2014-07-30 09:16:33 -070085 fVertexBounds.setLargestInverted();
jvanverth@google.comd830d132013-11-11 20:54:09 +000086}
87
jvanverth8c27a182014-10-14 08:45:50 -070088GrDistanceFieldTextContext* GrDistanceFieldTextContext::Create(GrContext* context,
89 const SkDeviceProperties& props,
90 bool enable) {
91 GrDistanceFieldTextContext* textContext = SkNEW_ARGS(GrDistanceFieldTextContext,
92 (context, props, enable));
93 textContext->fFallbackTextContext = GrBitmapTextContext::Create(context, props);
94
95 return textContext;
96}
97
jvanverth@google.comd830d132013-11-11 20:54:09 +000098GrDistanceFieldTextContext::~GrDistanceFieldTextContext() {
jvanverth73f10532014-10-23 11:57:12 -070099 this->finish();
jvanverth2d2a68c2014-06-10 06:42:56 -0700100 SkSafeSetNull(fGammaTexture);
jvanverth@google.comd830d132013-11-11 20:54:09 +0000101}
102
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000103bool GrDistanceFieldTextContext::canDraw(const SkPaint& paint) {
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +0000104 if (!fEnableDFRendering && !paint.isDistanceFieldTextTEMP()) {
commit-bot@chromium.orgeefd8a02014-04-08 20:14:32 +0000105 return false;
106 }
107
skia.committer@gmail.come1d94432014-04-09 03:04:11 +0000108 // rasterizers and mask filters modify alpha, which doesn't
commit-bot@chromium.orgeefd8a02014-04-08 20:14:32 +0000109 // translate well to distance
110 if (paint.getRasterizer() || paint.getMaskFilter() ||
111 !fContext->getTextTarget()->caps()->shaderDerivativeSupport()) {
112 return false;
113 }
114
115 // TODO: add some stroking support
116 if (paint.getStyle() != SkPaint::kFill_Style) {
117 return false;
118 }
119
120 // TODO: choose an appropriate maximum scale for distance fields and
121 // enable perspective
122 if (SkDraw::ShouldDrawTextAsPaths(paint, fContext->getMatrix())) {
123 return false;
124 }
125
126 // distance fields cannot represent color fonts
127 SkScalerContext::Rec rec;
128 SkScalerContext::MakeRec(paint, &fDeviceProperties, NULL, &rec);
129 return rec.getFormat() != SkMask::kARGB32_Format;
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000130}
131
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000132inline void GrDistanceFieldTextContext::init(const GrPaint& paint, const SkPaint& skPaint) {
133 GrTextContext::init(paint, skPaint);
134
135 fStrike = NULL;
136
jvanverth76ce81e2014-09-22 14:26:53 -0700137 const SkMatrix& ctm = fContext->getMatrix();
jvanverth9564ce62014-09-16 05:45:19 -0700138
139 // getMaxScale doesn't support perspective, so neither do we at the moment
jvanverth76ce81e2014-09-22 14:26:53 -0700140 SkASSERT(!ctm.hasPerspective());
141 SkScalar maxScale = ctm.getMaxScale();
jvanverth9564ce62014-09-16 05:45:19 -0700142 SkScalar textSize = fSkPaint.getTextSize();
jvanverth76ce81e2014-09-22 14:26:53 -0700143 SkScalar scaledTextSize = textSize;
144 // if we have non-unity scale, we need to choose our base text size
145 // based on the SkPaint's text size multiplied by the max scale factor
jvanverth9564ce62014-09-16 05:45:19 -0700146 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
147 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
jvanverth76ce81e2014-09-22 14:26:53 -0700148 scaledTextSize *= maxScale;
jvanverth9564ce62014-09-16 05:45:19 -0700149 }
150
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000151 fVertices = NULL;
jvanverth73f10532014-10-23 11:57:12 -0700152 fCurrVertex = 0;
153 fAllocVertexCount = 0;
154 fTotalVertexCount = 0;
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000155
jvanverth76ce81e2014-09-22 14:26:53 -0700156 if (scaledTextSize <= kSmallDFFontLimit) {
jvanverth9564ce62014-09-16 05:45:19 -0700157 fTextRatio = textSize / kSmallDFFontSize;
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000158 fSkPaint.setTextSize(SkIntToScalar(kSmallDFFontSize));
jvanverth76ce81e2014-09-22 14:26:53 -0700159 } else if (scaledTextSize <= kMediumDFFontLimit) {
jvanverth9564ce62014-09-16 05:45:19 -0700160 fTextRatio = textSize / kMediumDFFontSize;
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000161 fSkPaint.setTextSize(SkIntToScalar(kMediumDFFontSize));
162 } else {
jvanverth9564ce62014-09-16 05:45:19 -0700163 fTextRatio = textSize / kLargeDFFontSize;
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000164 fSkPaint.setTextSize(SkIntToScalar(kLargeDFFontSize));
165 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000166
commit-bot@chromium.org609ced42014-04-03 18:25:48 +0000167 fUseLCDText = fSkPaint.isLCDRenderText();
skia.committer@gmail.com221b9112014-04-04 03:04:32 +0000168
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000169 fSkPaint.setLCDRenderText(false);
170 fSkPaint.setAutohinted(false);
jvanverth2d2a68c2014-06-10 06:42:56 -0700171 fSkPaint.setHinting(SkPaint::kNormal_Hinting);
commit-bot@chromium.org0bed43c2014-03-14 21:22:38 +0000172 fSkPaint.setSubpixelText(true);
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000173}
174
jvanverth2d2a68c2014-06-10 06:42:56 -0700175static void setup_gamma_texture(GrContext* context, const SkGlyphCache* cache,
176 const SkDeviceProperties& deviceProperties,
177 GrTexture** gammaTexture) {
178 if (NULL == *gammaTexture) {
179 int width, height;
180 size_t size;
181
182#ifdef SK_GAMMA_CONTRAST
183 SkScalar contrast = SK_GAMMA_CONTRAST;
184#else
185 SkScalar contrast = 0.5f;
186#endif
reedb2d77e42014-10-14 08:26:33 -0700187 SkScalar paintGamma = deviceProperties.gamma();
188 SkScalar deviceGamma = deviceProperties.gamma();
jvanverth2d2a68c2014-06-10 06:42:56 -0700189
190 size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
191 &width, &height);
192
193 SkAutoTArray<uint8_t> data((int)size);
194 SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
195
196 // TODO: Update this to use the cache rather than directly creating a texture.
bsalomonf2703d82014-10-28 14:33:06 -0700197 GrSurfaceDesc desc;
198 desc.fFlags = kNone_GrSurfaceFlags;
jvanverth2d2a68c2014-06-10 06:42:56 -0700199 desc.fWidth = width;
200 desc.fHeight = height;
201 desc.fConfig = kAlpha_8_GrPixelConfig;
202
203 *gammaTexture = context->getGpu()->createTexture(desc, NULL, 0);
204 if (NULL == *gammaTexture) {
205 return;
206 }
207
bsalomon81beccc2014-10-13 12:32:55 -0700208 (*gammaTexture)->writePixels(0, 0, width, height,
209 (*gammaTexture)->config(), data.get(), 0,
210 GrContext::kDontFlush_PixelOpsFlag);
jvanverth2d2a68c2014-06-10 06:42:56 -0700211 }
212}
213
jvanverthaab626c2014-10-16 08:04:39 -0700214void GrDistanceFieldTextContext::onDrawText(const GrPaint& paint, const SkPaint& skPaint,
215 const char text[], size_t byteLength,
216 SkScalar x, SkScalar y) {
217 SkASSERT(byteLength == 0 || text != NULL);
218
jvanverth2b9dc1d2014-10-20 06:48:59 -0700219 // nothing to draw
220 if (text == NULL || byteLength == 0) {
jvanverthaab626c2014-10-16 08:04:39 -0700221 return;
222 }
223
jvanverth2b9dc1d2014-10-20 06:48:59 -0700224 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
225 SkAutoGlyphCache autoCache(skPaint, &fDeviceProperties, NULL);
226 SkGlyphCache* cache = autoCache.getCache();
jvanverthaab626c2014-10-16 08:04:39 -0700227
jvanverth2b9dc1d2014-10-20 06:48:59 -0700228 SkTArray<SkScalar> positions;
jvanverthaab626c2014-10-16 08:04:39 -0700229
jvanverth2b9dc1d2014-10-20 06:48:59 -0700230 const char* textPtr = text;
231 SkFixed stopX = 0;
232 SkFixed stopY = 0;
233 SkFixed origin;
234 switch (skPaint.getTextAlign()) {
235 case SkPaint::kRight_Align: origin = SK_Fixed1; break;
236 case SkPaint::kCenter_Align: origin = SK_FixedHalf; break;
237 case SkPaint::kLeft_Align: origin = 0; break;
238 default: SkFAIL("Invalid paint origin"); return;
239 }
jvanverthaab626c2014-10-16 08:04:39 -0700240
jvanverth2b9dc1d2014-10-20 06:48:59 -0700241 SkAutoKern autokern;
jvanverthaab626c2014-10-16 08:04:39 -0700242 const char* stop = text + byteLength;
jvanverth2b9dc1d2014-10-20 06:48:59 -0700243 while (textPtr < stop) {
244 // don't need x, y here, since all subpixel variants will have the
245 // same advance
246 const SkGlyph& glyph = glyphCacheProc(cache, &textPtr, 0, 0);
jvanverthaab626c2014-10-16 08:04:39 -0700247
jvanverth2b9dc1d2014-10-20 06:48:59 -0700248 SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph);
249 positions.push_back(SkFixedToScalar(stopX + SkFixedMul_portable(origin, width)));
jvanverthaab626c2014-10-16 08:04:39 -0700250
jvanverth2b9dc1d2014-10-20 06:48:59 -0700251 SkFixed height = glyph.fAdvanceY;
252 positions.push_back(SkFixedToScalar(stopY + SkFixedMul_portable(origin, height)));
jvanverthaab626c2014-10-16 08:04:39 -0700253
jvanverth2b9dc1d2014-10-20 06:48:59 -0700254 stopX += width;
255 stopY += height;
jvanverthaab626c2014-10-16 08:04:39 -0700256 }
jvanverth2b9dc1d2014-10-20 06:48:59 -0700257 SkASSERT(textPtr == stop);
jvanverthaab626c2014-10-16 08:04:39 -0700258
jvanverth2b9dc1d2014-10-20 06:48:59 -0700259 // now adjust starting point depending on alignment
260 SkScalar alignX = SkFixedToScalar(stopX);
261 SkScalar alignY = SkFixedToScalar(stopY);
262 if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
263 alignX = SkScalarHalf(alignX);
264 alignY = SkScalarHalf(alignY);
265 } else if (skPaint.getTextAlign() == SkPaint::kLeft_Align) {
266 alignX = 0;
267 alignY = 0;
jvanverthaab626c2014-10-16 08:04:39 -0700268 }
jvanverth2b9dc1d2014-10-20 06:48:59 -0700269 x -= alignX;
270 y -= alignY;
271 SkPoint offset = SkPoint::Make(x, y);
jvanverthaab626c2014-10-16 08:04:39 -0700272
jvanverth2b9dc1d2014-10-20 06:48:59 -0700273 this->drawPosText(paint, skPaint, text, byteLength, positions.begin(), 2, offset);
jvanverthaab626c2014-10-16 08:04:39 -0700274}
275
jvanverth8c27a182014-10-14 08:45:50 -0700276void GrDistanceFieldTextContext::onDrawPosText(const GrPaint& paint, const SkPaint& skPaint,
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000277 const char text[], size_t byteLength,
fmalita05c4a432014-09-29 06:29:53 -0700278 const SkScalar pos[], int scalarsPerPosition,
279 const SkPoint& offset) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000280
281 SkASSERT(byteLength == 0 || text != NULL);
282 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
283
284 // nothing to draw
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000285 if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000286 return;
287 }
288
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000289 this->init(paint, skPaint);
290
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000291 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
292
jvanverth2d2a68c2014-06-10 06:42:56 -0700293 SkAutoGlyphCacheNoGamma autoCache(fSkPaint, &fDeviceProperties, NULL);
294 SkGlyphCache* cache = autoCache.getCache();
295 GrFontScaler* fontScaler = GetGrFontScaler(cache);
296
297 setup_gamma_texture(fContext, cache, fDeviceProperties, &fGammaTexture);
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000298
jvanverth73f10532014-10-23 11:57:12 -0700299 int numGlyphs = fSkPaint.textToGlyphs(text, byteLength, NULL);
300 fTotalVertexCount = kVerticesPerGlyph*numGlyphs;
301
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000302 const char* stop = text + byteLength;
jvanverthfca302c2014-10-20 13:12:54 -0700303 SkTArray<char> fallbackTxt;
304 SkTArray<SkScalar> fallbackPos;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000305
306 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
307 while (text < stop) {
jvanverthfca302c2014-10-20 13:12:54 -0700308 const char* lastText = text;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000309 // the last 2 parameters are ignored
310 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
311
312 if (glyph.fWidth) {
fmalita05c4a432014-09-29 06:29:53 -0700313 SkScalar x = offset.x() + pos[0];
314 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000315
jvanverthfca302c2014-10-20 13:12:54 -0700316 if (!this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
317 glyph.getSubXFixed(),
318 glyph.getSubYFixed()),
djsollen058f01e2014-10-30 11:54:43 -0700319 x, y, fontScaler)) {
jvanverthfca302c2014-10-20 13:12:54 -0700320 // couldn't append, send to fallback
321 fallbackTxt.push_back_n(text-lastText, lastText);
322 fallbackPos.push_back(pos[0]);
323 if (2 == scalarsPerPosition) {
324 fallbackPos.push_back(pos[1]);
325 }
326 }
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000327 }
328 pos += scalarsPerPosition;
329 }
330 } else {
jvanverth2b9dc1d2014-10-20 06:48:59 -0700331 SkScalar alignMul = SkPaint::kCenter_Align == fSkPaint.getTextAlign() ? SK_ScalarHalf
332 : SK_Scalar1;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000333 while (text < stop) {
jvanverthfca302c2014-10-20 13:12:54 -0700334 const char* lastText = text;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000335 // the last 2 parameters are ignored
336 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
337
338 if (glyph.fWidth) {
fmalita05c4a432014-09-29 06:29:53 -0700339 SkScalar x = offset.x() + pos[0];
340 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
skia.committer@gmail.com22e96722013-12-20 07:01:36 +0000341
jvanverth2b9dc1d2014-10-20 06:48:59 -0700342 SkScalar advanceX = SkFixedToScalar(glyph.fAdvanceX)*alignMul*fTextRatio;
343 SkScalar advanceY = SkFixedToScalar(glyph.fAdvanceY)*alignMul*fTextRatio;
344
jvanverthfca302c2014-10-20 13:12:54 -0700345 if (!this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
346 glyph.getSubXFixed(),
347 glyph.getSubYFixed()),
djsollen058f01e2014-10-30 11:54:43 -0700348 x - advanceX, y - advanceY, fontScaler)) {
jvanverthfca302c2014-10-20 13:12:54 -0700349 // couldn't append, send to fallback
350 fallbackTxt.push_back_n(text-lastText, lastText);
351 fallbackPos.push_back(pos[0]);
352 if (2 == scalarsPerPosition) {
353 fallbackPos.push_back(pos[1]);
354 }
355 }
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000356 }
357 pos += scalarsPerPosition;
358 }
359 }
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000360
361 this->finish();
jvanverthfca302c2014-10-20 13:12:54 -0700362
363 if (fallbackTxt.count() > 0) {
364 fFallbackTextContext->drawPosText(paint, skPaint, fallbackTxt.begin(), fallbackTxt.count(),
365 fallbackPos.begin(), scalarsPerPosition, offset);
366 }
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000367}
jvanverth0fedb192014-10-08 09:07:27 -0700368
369static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
370 unsigned r = SkColorGetR(c);
371 unsigned g = SkColorGetG(c);
372 unsigned b = SkColorGetB(c);
373 return GrColorPackRGBA(r, g, b, 0xff);
374}
375
jvanverth73f10532014-10-23 11:57:12 -0700376static void* alloc_vertices(GrDrawTarget* drawTarget, int numVertices, bool useColorVerts) {
377 if (numVertices <= 0) {
378 return NULL;
379 }
380
381 // set up attributes
382 if (useColorVerts) {
383 drawTarget->drawState()->setVertexAttribs<gTextVertexWithColorAttribs>(
384 SK_ARRAY_COUNT(gTextVertexWithColorAttribs), kTextVAColorSize);
385 } else {
386 drawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
387 SK_ARRAY_COUNT(gTextVertexAttribs), kTextVASize);
388 }
389 void* vertices = NULL;
390 bool success = drawTarget->reserveVertexAndIndexSpace(numVertices,
391 0,
392 &vertices,
393 NULL);
394 GrAlwaysAssert(success);
395 return vertices;
396}
397
jvanverth0fedb192014-10-08 09:07:27 -0700398void GrDistanceFieldTextContext::setupCoverageEffect(const SkColor& filteredColor) {
399 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode);
400 GrTextureParams gammaParams(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
401
402 uint32_t textureUniqueID = fCurrTexture->getUniqueID();
403 const SkMatrix& ctm = fContext->getMatrix();
404
405 // set up any flags
406 uint32_t flags = 0;
407 flags |= ctm.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
408 flags |= fUseLCDText ? kUseLCD_DistanceFieldEffectFlag : 0;
409 flags |= fUseLCDText && ctm.rectStaysRect() ?
410 kRectToRect_DistanceFieldEffectFlag : 0;
reedb2d77e42014-10-14 08:26:33 -0700411 bool useBGR = SkPixelGeometryIsBGR(fDeviceProperties.pixelGeometry());
jvanverth0fedb192014-10-08 09:07:27 -0700412 flags |= fUseLCDText && useBGR ? kBGR_DistanceFieldEffectFlag : 0;
413
414 // see if we need to create a new effect
415 if (textureUniqueID != fEffectTextureUniqueID ||
416 filteredColor != fEffectColor ||
417 flags != fEffectFlags) {
418 if (fUseLCDText) {
419 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
420 fCachedGeometryProcessor.reset(GrDistanceFieldLCDTextureEffect::Create(fCurrTexture,
421 params,
422 fGammaTexture,
423 gammaParams,
424 colorNoPreMul,
425 flags));
426 } else {
427#ifdef SK_GAMMA_APPLY_TO_A8
reedb2d77e42014-10-14 08:26:33 -0700428 U8CPU lum = SkColorSpaceLuminance::computeLuminance(fDeviceProperties.gamma(),
jvanverth0fedb192014-10-08 09:07:27 -0700429 filteredColor);
430 fCachedGeometryProcessor.reset(GrDistanceFieldTextureEffect::Create(fCurrTexture,
431 params,
432 fGammaTexture,
433 gammaParams,
434 lum/255.f,
435 flags));
436#else
437 fCachedGeometryProcessor.reset(GrDistanceFieldTextureEffect::Create(fCurrTexture,
438 params, flags));
439#endif
440 }
441 fEffectTextureUniqueID = textureUniqueID;
442 fEffectColor = filteredColor;
443 fEffectFlags = flags;
444 }
445
446}
447
jvanverthfca302c2014-10-20 13:12:54 -0700448// Returns true if this method handled the glyph, false if needs to be passed to fallback
449//
450bool GrDistanceFieldTextContext::appendGlyph(GrGlyph::PackedID packed,
djsollen058f01e2014-10-30 11:54:43 -0700451 SkScalar sx, SkScalar sy,
jvanverth0fedb192014-10-08 09:07:27 -0700452 GrFontScaler* scaler) {
453 if (NULL == fDrawTarget) {
jvanverthfca302c2014-10-20 13:12:54 -0700454 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700455 }
456
457 if (NULL == fStrike) {
458 fStrike = fContext->getFontCache()->getStrike(scaler, true);
459 }
460
461 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
462 if (NULL == glyph || glyph->fBounds.isEmpty()) {
jvanverthfca302c2014-10-20 13:12:54 -0700463 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700464 }
465
jvanverthfca302c2014-10-20 13:12:54 -0700466 // fallback to color glyph support
jvanverth294c3262014-10-10 11:36:12 -0700467 if (kA8_GrMaskFormat != glyph->fMaskFormat) {
jvanverthfca302c2014-10-20 13:12:54 -0700468 return false;
jvanverth294c3262014-10-10 11:36:12 -0700469 }
470
jvanverth0fedb192014-10-08 09:07:27 -0700471/*
472 // not valid, need to find a different solution for this
473 vx += SkIntToFixed(glyph->fBounds.fLeft);
474 vy += SkIntToFixed(glyph->fBounds.fTop);
475
476 // keep them as ints until we've done the clip-test
477 GrFixed width = glyph->fBounds.width();
478 GrFixed height = glyph->fBounds.height();
479
480 // check if we clipped out
481 if (true || NULL == glyph->fPlot) {
482 int x = vx >> 16;
483 int y = vy >> 16;
484 if (fClipRect.quickReject(x, y, x + width, y + height)) {
485// SkCLZ(3); // so we can set a break-point in the debugger
486 return;
487 }
488 }
489*/
490 if (NULL == glyph->fPlot) {
491 if (!fStrike->glyphTooLargeForAtlas(glyph)) {
492 if (fStrike->addGlyphToAtlas(glyph, scaler)) {
493 goto HAS_ATLAS;
494 }
495
496 // try to clear out an unused plot before we flush
jvanverth294c3262014-10-10 11:36:12 -0700497 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
jvanverth0fedb192014-10-08 09:07:27 -0700498 fStrike->addGlyphToAtlas(glyph, scaler)) {
499 goto HAS_ATLAS;
500 }
501
502 if (c_DumpFontCache) {
503#ifdef SK_DEVELOPER
504 fContext->getFontCache()->dump();
505#endif
506 }
507
508 // before we purge the cache, we must flush any accumulated draws
509 this->flush();
510 fContext->flush();
511
512 // we should have an unused plot now
jvanverth294c3262014-10-10 11:36:12 -0700513 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
jvanverth0fedb192014-10-08 09:07:27 -0700514 fStrike->addGlyphToAtlas(glyph, scaler)) {
515 goto HAS_ATLAS;
516 }
517 }
518
519 if (NULL == glyph->fPath) {
520 SkPath* path = SkNEW(SkPath);
521 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
522 // flag the glyph as being dead?
523 delete path;
jvanverthfca302c2014-10-20 13:12:54 -0700524 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700525 }
526 glyph->fPath = path;
527 }
528
bsalomonec87dc62014-10-14 10:52:00 -0700529 // flush any accumulated draws before drawing this glyph as a path.
530 this->flush();
531
jvanverth0fedb192014-10-08 09:07:27 -0700532 GrContext::AutoMatrix am;
533 SkMatrix ctm;
534 ctm.setScale(fTextRatio, fTextRatio);
535 ctm.postTranslate(sx, sy);
536 GrPaint tmpPaint(fPaint);
537 am.setPreConcat(fContext, ctm, &tmpPaint);
538 GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle);
539 fContext->drawPath(tmpPaint, *glyph->fPath, strokeInfo);
jvanverth73f10532014-10-23 11:57:12 -0700540
541 // remove this glyph from the vertices we need to allocate
542 fTotalVertexCount -= kVerticesPerGlyph;
jvanverthfca302c2014-10-20 13:12:54 -0700543 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700544 }
545
546HAS_ATLAS:
547 SkASSERT(glyph->fPlot);
548 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
549 glyph->fPlot->setDrawToken(drawToken);
550
551 GrTexture* texture = glyph->fPlot->texture();
552 SkASSERT(texture);
553
jvanverth73f10532014-10-23 11:57:12 -0700554 if (fCurrTexture != texture || fCurrVertex + kVerticesPerGlyph > fTotalVertexCount) {
jvanverth0fedb192014-10-08 09:07:27 -0700555 this->flush();
556 fCurrTexture = texture;
557 fCurrTexture->ref();
558 }
559
560 bool useColorVerts = !fUseLCDText;
561
562 if (NULL == fVertices) {
jvanverth73f10532014-10-23 11:57:12 -0700563 int maxQuadVertices = kVerticesPerGlyph * fContext->getQuadIndexBuffer()->maxQuads();
564 fAllocVertexCount = SkMin32(fTotalVertexCount, maxQuadVertices);
565 fVertices = alloc_vertices(fDrawTarget, fAllocVertexCount, useColorVerts);
jvanverth0fedb192014-10-08 09:07:27 -0700566 }
567
568 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
569 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
570 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2*SK_DistanceFieldInset);
571 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2*SK_DistanceFieldInset);
572
573 SkScalar scale = fTextRatio;
574 dx *= scale;
575 dy *= scale;
576 sx += dx;
577 sy += dy;
578 width *= scale;
579 height *= scale;
580
581 SkFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX + SK_DistanceFieldInset);
582 SkFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY + SK_DistanceFieldInset);
583 SkFixed tw = SkIntToFixed(glyph->fBounds.width() - 2*SK_DistanceFieldInset);
584 SkFixed th = SkIntToFixed(glyph->fBounds.height() - 2*SK_DistanceFieldInset);
585
586 SkRect r;
587 r.fLeft = sx;
588 r.fTop = sy;
589 r.fRight = sx + width;
590 r.fBottom = sy + height;
591
592 fVertexBounds.joinNonEmptyArg(r);
593
594 size_t vertSize = fUseLCDText ? (2 * sizeof(SkPoint))
595 : (2 * sizeof(SkPoint) + sizeof(GrColor));
596
597 SkASSERT(vertSize == fDrawTarget->getDrawState().getVertexStride());
598
599 SkPoint* positions = reinterpret_cast<SkPoint*>(
600 reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVertex);
601 positions->setRectFan(r.fLeft, r.fTop, r.fRight, r.fBottom, vertSize);
602
603 // The texture coords are last in both the with and without color vertex layouts.
604 SkPoint* textureCoords = reinterpret_cast<SkPoint*>(
605 reinterpret_cast<intptr_t>(positions) + vertSize - sizeof(SkPoint));
606 textureCoords->setRectFan(SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx)),
607 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty)),
608 SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx + tw)),
609 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty + th)),
610 vertSize);
611 if (useColorVerts) {
612 if (0xFF == GrColorUnpackA(fPaint.getColor())) {
613 fDrawTarget->drawState()->setHint(GrDrawState::kVertexColorsAreOpaque_Hint, true);
614 }
615 // color comes after position.
616 GrColor* colors = reinterpret_cast<GrColor*>(positions + 1);
617 for (int i = 0; i < 4; ++i) {
618 *colors = fPaint.getColor();
619 colors = reinterpret_cast<GrColor*>(reinterpret_cast<intptr_t>(colors) + vertSize);
620 }
621 }
622
623 fCurrVertex += 4;
jvanverthfca302c2014-10-20 13:12:54 -0700624
625 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700626}
627
628void GrDistanceFieldTextContext::flush() {
629 if (NULL == fDrawTarget) {
630 return;
631 }
632
633 GrDrawState* drawState = fDrawTarget->drawState();
634 GrDrawState::AutoRestoreEffects are(drawState);
635
636 drawState->setFromPaint(fPaint, fContext->getMatrix(), fContext->getRenderTarget());
637
638 if (fCurrVertex > 0) {
639 // setup our sampler state for our text texture/atlas
640 SkASSERT(SkIsAlign4(fCurrVertex));
641
642 // get our current color
643 SkColor filteredColor;
644 SkColorFilter* colorFilter = fSkPaint.getColorFilter();
645 if (colorFilter) {
646 filteredColor = colorFilter->filterColor(fSkPaint.getColor());
647 } else {
648 filteredColor = fSkPaint.getColor();
649 }
650 this->setupCoverageEffect(filteredColor);
651
652 // Effects could be stored with one of the cache objects (atlas?)
653 drawState->setGeometryProcessor(fCachedGeometryProcessor.get());
654
655 // Set draw state
656 if (fUseLCDText) {
657 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
658 if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
659 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
660 fPaint.numColorStages()) {
661 GrPrintf("LCD Text will not draw correctly.\n");
662 }
663 SkASSERT(!drawState->hasColorVertexAttribute());
664 // We don't use the GrPaint's color in this case because it's been premultiplied by
665 // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by
666 // the mask texture color. The end result is that we get
667 // mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor
668 int a = SkColorGetA(fSkPaint.getColor());
669 // paintAlpha
670 drawState->setColor(SkColorSetARGB(a, a, a, a));
671 // paintColor
672 drawState->setBlendConstant(colorNoPreMul);
673 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
674 } else {
675 // set back to normal in case we took LCD path previously.
676 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
677 // We're using per-vertex color.
678 SkASSERT(drawState->hasColorVertexAttribute());
679 }
jvanverth73f10532014-10-23 11:57:12 -0700680 int nGlyphs = fCurrVertex / kVerticesPerGlyph;
jvanverth0fedb192014-10-08 09:07:27 -0700681 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
682 fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
683 nGlyphs,
jvanverth73f10532014-10-23 11:57:12 -0700684 kVerticesPerGlyph, kIndicesPerGlyph, &fVertexBounds);
jvanverth0fedb192014-10-08 09:07:27 -0700685 fDrawTarget->resetVertexSource();
686 fVertices = NULL;
jvanverth73f10532014-10-23 11:57:12 -0700687 fTotalVertexCount -= fCurrVertex;
jvanverth0fedb192014-10-08 09:07:27 -0700688 fCurrVertex = 0;
689 SkSafeSetNull(fCurrTexture);
690 fVertexBounds.setLargestInverted();
691 }
692}
693
694inline void GrDistanceFieldTextContext::finish() {
695 this->flush();
jvanverth73f10532014-10-23 11:57:12 -0700696 fTotalVertexCount = 0;
jvanverth0fedb192014-10-08 09:07:27 -0700697
698 GrTextContext::finish();
699}
700