blob: 8d993fa2039e116f4cca56ed9fd55e67131af6e4 [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"
jvanverth787cdf92014-12-04 10:46:50 -080013#include "GrFontCache.h"
jvanverth@google.comd830d132013-11-11 20:54:09 +000014#include "GrFontScaler.h"
jvanverth2d2a68c2014-06-10 06:42:56 -070015#include "GrGpu.h"
jvanverth@google.comd830d132013-11-11 20:54:09 +000016#include "GrIndexBuffer.h"
egdanield58a0ba2014-06-11 10:30:05 -070017#include "GrStrokeInfo.h"
bsalomonafbf2d62014-09-30 12:18:44 -070018#include "GrTexturePriv.h"
bsalomonafbf2d62014-09-30 12:18:44 -070019
jvanverth2b9dc1d2014-10-20 06:48:59 -070020#include "SkAutoKern.h"
bsalomonafbf2d62014-09-30 12:18:44 -070021#include "SkColorFilter.h"
commit-bot@chromium.org64b08a12014-04-15 17:53:21 +000022#include "SkDistanceFieldGen.h"
commit-bot@chromium.org9f94b912014-01-30 15:22:54 +000023#include "SkDraw.h"
bsalomonafbf2d62014-09-30 12:18:44 -070024#include "SkGlyphCache.h"
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +000025#include "SkGpuDevice.h"
jvanverth@google.comd830d132013-11-11 20:54:09 +000026#include "SkPath.h"
27#include "SkRTConf.h"
28#include "SkStrokeRec.h"
29#include "effects/GrDistanceFieldTextureEffect.h"
30
jvanverthfeceba52014-07-25 19:03:34 -070031SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
32 "Dump the contents of the font cache before every purge.");
33
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +000034static const int kSmallDFFontSize = 32;
35static const int kSmallDFFontLimit = 32;
jvanverthada68ef2014-11-03 14:00:24 -080036static const int kMediumDFFontSize = 78;
37static const int kMediumDFFontLimit = 78;
38static const int kLargeDFFontSize = 192;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +000039
jvanverth73f10532014-10-23 11:57:12 -070040static const int kVerticesPerGlyph = 4;
41static const int kIndicesPerGlyph = 6;
jvanverth@google.comd830d132013-11-11 20:54:09 +000042
skia.committer@gmail.come5d70152014-01-29 07:01:48 +000043GrDistanceFieldTextContext::GrDistanceFieldTextContext(GrContext* context,
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000044 const SkDeviceProperties& properties,
45 bool enable)
Mike Klein6a25bd02014-08-29 10:03:59 -040046 : GrTextContext(context, properties) {
jvanverth4736e142014-11-07 07:12:46 -080047#if SK_FORCE_DISTANCE_FIELD_TEXT
Mike Klein6a25bd02014-08-29 10:03:59 -040048 fEnableDFRendering = true;
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000049#else
Mike Klein6a25bd02014-08-29 10:03:59 -040050 fEnableDFRendering = enable;
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000051#endif
Mike Klein6a25bd02014-08-29 10:03:59 -040052 fStrike = NULL;
53 fGammaTexture = NULL;
54
Mike Klein6a25bd02014-08-29 10:03:59 -040055 fEffectTextureUniqueID = SK_InvalidUniqueID;
56 fEffectColor = GrColor_ILLEGAL;
jvanverth6d22eca2014-10-28 11:10:48 -070057 fEffectFlags = kInvalid_DistanceFieldEffectFlag;
Mike Klein6a25bd02014-08-29 10:03:59 -040058
59 fVertices = NULL;
jvanverth73f10532014-10-23 11:57:12 -070060 fCurrVertex = 0;
61 fAllocVertexCount = 0;
62 fTotalVertexCount = 0;
63 fCurrTexture = NULL;
Mike Klein6a25bd02014-08-29 10:03:59 -040064
jvanverth1723bfc2014-07-30 09:16:33 -070065 fVertexBounds.setLargestInverted();
jvanverth@google.comd830d132013-11-11 20:54:09 +000066}
67
jvanverth8c27a182014-10-14 08:45:50 -070068GrDistanceFieldTextContext* GrDistanceFieldTextContext::Create(GrContext* context,
69 const SkDeviceProperties& props,
70 bool enable) {
71 GrDistanceFieldTextContext* textContext = SkNEW_ARGS(GrDistanceFieldTextContext,
72 (context, props, enable));
73 textContext->fFallbackTextContext = GrBitmapTextContext::Create(context, props);
74
75 return textContext;
76}
77
jvanverth@google.comd830d132013-11-11 20:54:09 +000078GrDistanceFieldTextContext::~GrDistanceFieldTextContext() {
jvanverth2d2a68c2014-06-10 06:42:56 -070079 SkSafeSetNull(fGammaTexture);
jvanverth@google.comd830d132013-11-11 20:54:09 +000080}
81
joshualitt5531d512014-12-17 15:50:11 -080082bool GrDistanceFieldTextContext::canDraw(const SkPaint& paint, const SkMatrix& viewMatrix) {
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000083 if (!fEnableDFRendering && !paint.isDistanceFieldTextTEMP()) {
commit-bot@chromium.orgeefd8a02014-04-08 20:14:32 +000084 return false;
85 }
86
skia.committer@gmail.come1d94432014-04-09 03:04:11 +000087 // rasterizers and mask filters modify alpha, which doesn't
commit-bot@chromium.orgeefd8a02014-04-08 20:14:32 +000088 // translate well to distance
89 if (paint.getRasterizer() || paint.getMaskFilter() ||
90 !fContext->getTextTarget()->caps()->shaderDerivativeSupport()) {
91 return false;
92 }
93
94 // TODO: add some stroking support
95 if (paint.getStyle() != SkPaint::kFill_Style) {
96 return false;
97 }
98
99 // TODO: choose an appropriate maximum scale for distance fields and
100 // enable perspective
joshualitt5531d512014-12-17 15:50:11 -0800101 if (SkDraw::ShouldDrawTextAsPaths(paint, viewMatrix)) {
commit-bot@chromium.orgeefd8a02014-04-08 20:14:32 +0000102 return false;
103 }
104
jvanverth2faa2282014-10-31 12:59:57 -0700105 return true;
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000106}
107
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000108inline void GrDistanceFieldTextContext::init(const GrPaint& paint, const SkPaint& skPaint) {
109 GrTextContext::init(paint, skPaint);
110
111 fStrike = NULL;
112
joshualitt5531d512014-12-17 15:50:11 -0800113 const SkMatrix& ctm = fViewMatrix;
jvanverth9564ce62014-09-16 05:45:19 -0700114
115 // getMaxScale doesn't support perspective, so neither do we at the moment
jvanverth76ce81e2014-09-22 14:26:53 -0700116 SkASSERT(!ctm.hasPerspective());
117 SkScalar maxScale = ctm.getMaxScale();
jvanverth9564ce62014-09-16 05:45:19 -0700118 SkScalar textSize = fSkPaint.getTextSize();
jvanverth76ce81e2014-09-22 14:26:53 -0700119 SkScalar scaledTextSize = textSize;
120 // if we have non-unity scale, we need to choose our base text size
121 // based on the SkPaint's text size multiplied by the max scale factor
jvanverth9564ce62014-09-16 05:45:19 -0700122 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
123 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
jvanverth76ce81e2014-09-22 14:26:53 -0700124 scaledTextSize *= maxScale;
jvanverth9564ce62014-09-16 05:45:19 -0700125 }
126
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000127 fVertices = NULL;
jvanverth73f10532014-10-23 11:57:12 -0700128 fCurrVertex = 0;
129 fAllocVertexCount = 0;
130 fTotalVertexCount = 0;
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000131
jvanverth76ce81e2014-09-22 14:26:53 -0700132 if (scaledTextSize <= kSmallDFFontLimit) {
jvanverth9564ce62014-09-16 05:45:19 -0700133 fTextRatio = textSize / kSmallDFFontSize;
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000134 fSkPaint.setTextSize(SkIntToScalar(kSmallDFFontSize));
jvanverthada68ef2014-11-03 14:00:24 -0800135#if DEBUG_TEXT_SIZE
136 fSkPaint.setColor(SkColorSetARGB(0xFF, 0x00, 0x00, 0xFF));
137 fPaint.setColor(GrColorPackRGBA(0x00, 0x00, 0xFF, 0xFF));
138#endif
jvanverth76ce81e2014-09-22 14:26:53 -0700139 } else if (scaledTextSize <= kMediumDFFontLimit) {
jvanverth9564ce62014-09-16 05:45:19 -0700140 fTextRatio = textSize / kMediumDFFontSize;
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000141 fSkPaint.setTextSize(SkIntToScalar(kMediumDFFontSize));
jvanverthada68ef2014-11-03 14:00:24 -0800142#if DEBUG_TEXT_SIZE
143 fSkPaint.setColor(SkColorSetARGB(0xFF, 0x00, 0xFF, 0x00));
144 fPaint.setColor(GrColorPackRGBA(0x00, 0xFF, 0x00, 0xFF));
145#endif
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000146 } else {
jvanverth9564ce62014-09-16 05:45:19 -0700147 fTextRatio = textSize / kLargeDFFontSize;
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000148 fSkPaint.setTextSize(SkIntToScalar(kLargeDFFontSize));
jvanverthada68ef2014-11-03 14:00:24 -0800149#if DEBUG_TEXT_SIZE
150 fSkPaint.setColor(SkColorSetARGB(0xFF, 0xFF, 0x00, 0x00));
151 fPaint.setColor(GrColorPackRGBA(0xFF, 0x00, 0x00, 0xFF));
152#endif
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000153 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000154
commit-bot@chromium.org609ced42014-04-03 18:25:48 +0000155 fUseLCDText = fSkPaint.isLCDRenderText();
skia.committer@gmail.com221b9112014-04-04 03:04:32 +0000156
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000157 fSkPaint.setLCDRenderText(false);
158 fSkPaint.setAutohinted(false);
jvanverth2d2a68c2014-06-10 06:42:56 -0700159 fSkPaint.setHinting(SkPaint::kNormal_Hinting);
commit-bot@chromium.org0bed43c2014-03-14 21:22:38 +0000160 fSkPaint.setSubpixelText(true);
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000161}
162
jvanverth2d2a68c2014-06-10 06:42:56 -0700163static void setup_gamma_texture(GrContext* context, const SkGlyphCache* cache,
164 const SkDeviceProperties& deviceProperties,
165 GrTexture** gammaTexture) {
166 if (NULL == *gammaTexture) {
167 int width, height;
168 size_t size;
169
170#ifdef SK_GAMMA_CONTRAST
171 SkScalar contrast = SK_GAMMA_CONTRAST;
172#else
173 SkScalar contrast = 0.5f;
174#endif
reedb2d77e42014-10-14 08:26:33 -0700175 SkScalar paintGamma = deviceProperties.gamma();
176 SkScalar deviceGamma = deviceProperties.gamma();
jvanverth2d2a68c2014-06-10 06:42:56 -0700177
178 size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
179 &width, &height);
180
181 SkAutoTArray<uint8_t> data((int)size);
182 SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
183
184 // TODO: Update this to use the cache rather than directly creating a texture.
bsalomonf2703d82014-10-28 14:33:06 -0700185 GrSurfaceDesc desc;
186 desc.fFlags = kNone_GrSurfaceFlags;
jvanverth2d2a68c2014-06-10 06:42:56 -0700187 desc.fWidth = width;
188 desc.fHeight = height;
189 desc.fConfig = kAlpha_8_GrPixelConfig;
190
191 *gammaTexture = context->getGpu()->createTexture(desc, NULL, 0);
192 if (NULL == *gammaTexture) {
193 return;
194 }
195
bsalomon81beccc2014-10-13 12:32:55 -0700196 (*gammaTexture)->writePixels(0, 0, width, height,
197 (*gammaTexture)->config(), data.get(), 0,
198 GrContext::kDontFlush_PixelOpsFlag);
jvanverth2d2a68c2014-06-10 06:42:56 -0700199 }
200}
201
jvanverthaab626c2014-10-16 08:04:39 -0700202void GrDistanceFieldTextContext::onDrawText(const GrPaint& paint, const SkPaint& skPaint,
joshualitt5531d512014-12-17 15:50:11 -0800203 const SkMatrix& viewMatrix,
204 const char text[], size_t byteLength,
205 SkScalar x, SkScalar y) {
jvanverthaab626c2014-10-16 08:04:39 -0700206 SkASSERT(byteLength == 0 || text != NULL);
207
jvanverth2b9dc1d2014-10-20 06:48:59 -0700208 // nothing to draw
209 if (text == NULL || byteLength == 0) {
jvanverthaab626c2014-10-16 08:04:39 -0700210 return;
211 }
212
joshualitt5531d512014-12-17 15:50:11 -0800213 fViewMatrix = viewMatrix;
jvanverth2b9dc1d2014-10-20 06:48:59 -0700214 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
215 SkAutoGlyphCache autoCache(skPaint, &fDeviceProperties, NULL);
216 SkGlyphCache* cache = autoCache.getCache();
jvanverthaab626c2014-10-16 08:04:39 -0700217
jvanverth2b9dc1d2014-10-20 06:48:59 -0700218 SkTArray<SkScalar> positions;
jvanverthaab626c2014-10-16 08:04:39 -0700219
jvanverth2b9dc1d2014-10-20 06:48:59 -0700220 const char* textPtr = text;
221 SkFixed stopX = 0;
222 SkFixed stopY = 0;
223 SkFixed origin;
224 switch (skPaint.getTextAlign()) {
225 case SkPaint::kRight_Align: origin = SK_Fixed1; break;
226 case SkPaint::kCenter_Align: origin = SK_FixedHalf; break;
227 case SkPaint::kLeft_Align: origin = 0; break;
228 default: SkFAIL("Invalid paint origin"); return;
229 }
jvanverthaab626c2014-10-16 08:04:39 -0700230
jvanverth2b9dc1d2014-10-20 06:48:59 -0700231 SkAutoKern autokern;
jvanverthaab626c2014-10-16 08:04:39 -0700232 const char* stop = text + byteLength;
jvanverth2b9dc1d2014-10-20 06:48:59 -0700233 while (textPtr < stop) {
234 // don't need x, y here, since all subpixel variants will have the
235 // same advance
236 const SkGlyph& glyph = glyphCacheProc(cache, &textPtr, 0, 0);
jvanverthaab626c2014-10-16 08:04:39 -0700237
jvanverth2b9dc1d2014-10-20 06:48:59 -0700238 SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph);
239 positions.push_back(SkFixedToScalar(stopX + SkFixedMul_portable(origin, width)));
jvanverthaab626c2014-10-16 08:04:39 -0700240
jvanverth2b9dc1d2014-10-20 06:48:59 -0700241 SkFixed height = glyph.fAdvanceY;
242 positions.push_back(SkFixedToScalar(stopY + SkFixedMul_portable(origin, height)));
jvanverthaab626c2014-10-16 08:04:39 -0700243
jvanverth2b9dc1d2014-10-20 06:48:59 -0700244 stopX += width;
245 stopY += height;
jvanverthaab626c2014-10-16 08:04:39 -0700246 }
jvanverth2b9dc1d2014-10-20 06:48:59 -0700247 SkASSERT(textPtr == stop);
jvanverthaab626c2014-10-16 08:04:39 -0700248
jvanverth2b9dc1d2014-10-20 06:48:59 -0700249 // now adjust starting point depending on alignment
250 SkScalar alignX = SkFixedToScalar(stopX);
251 SkScalar alignY = SkFixedToScalar(stopY);
252 if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
253 alignX = SkScalarHalf(alignX);
254 alignY = SkScalarHalf(alignY);
255 } else if (skPaint.getTextAlign() == SkPaint::kLeft_Align) {
256 alignX = 0;
257 alignY = 0;
jvanverthaab626c2014-10-16 08:04:39 -0700258 }
jvanverth2b9dc1d2014-10-20 06:48:59 -0700259 x -= alignX;
260 y -= alignY;
261 SkPoint offset = SkPoint::Make(x, y);
jvanverthaab626c2014-10-16 08:04:39 -0700262
joshualitt5531d512014-12-17 15:50:11 -0800263 this->drawPosText(paint, skPaint, viewMatrix, text, byteLength, positions.begin(), 2, offset);
jvanverthaab626c2014-10-16 08:04:39 -0700264}
265
jvanverth8c27a182014-10-14 08:45:50 -0700266void GrDistanceFieldTextContext::onDrawPosText(const GrPaint& paint, const SkPaint& skPaint,
joshualitt5531d512014-12-17 15:50:11 -0800267 const SkMatrix& viewMatrix,
268 const char text[], size_t byteLength,
269 const SkScalar pos[], int scalarsPerPosition,
270 const SkPoint& offset) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000271
272 SkASSERT(byteLength == 0 || text != NULL);
273 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
274
275 // nothing to draw
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000276 if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000277 return;
278 }
279
joshualitt5531d512014-12-17 15:50:11 -0800280 fViewMatrix = viewMatrix;
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000281 this->init(paint, skPaint);
282
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000283 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
284
jvanverth2d2a68c2014-06-10 06:42:56 -0700285 SkAutoGlyphCacheNoGamma autoCache(fSkPaint, &fDeviceProperties, NULL);
286 SkGlyphCache* cache = autoCache.getCache();
287 GrFontScaler* fontScaler = GetGrFontScaler(cache);
288
289 setup_gamma_texture(fContext, cache, fDeviceProperties, &fGammaTexture);
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000290
jvanverth73f10532014-10-23 11:57:12 -0700291 int numGlyphs = fSkPaint.textToGlyphs(text, byteLength, NULL);
292 fTotalVertexCount = kVerticesPerGlyph*numGlyphs;
293
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000294 const char* stop = text + byteLength;
jvanverthfca302c2014-10-20 13:12:54 -0700295 SkTArray<char> fallbackTxt;
296 SkTArray<SkScalar> fallbackPos;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000297
298 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
299 while (text < stop) {
jvanverthfca302c2014-10-20 13:12:54 -0700300 const char* lastText = text;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000301 // the last 2 parameters are ignored
302 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
303
304 if (glyph.fWidth) {
fmalita05c4a432014-09-29 06:29:53 -0700305 SkScalar x = offset.x() + pos[0];
306 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000307
jvanverthfca302c2014-10-20 13:12:54 -0700308 if (!this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
309 glyph.getSubXFixed(),
310 glyph.getSubYFixed()),
djsollen058f01e2014-10-30 11:54:43 -0700311 x, y, fontScaler)) {
jvanverthfca302c2014-10-20 13:12:54 -0700312 // couldn't append, send to fallback
bsalomonef3fcd82014-12-12 08:51:38 -0800313 fallbackTxt.push_back_n(SkToInt(text-lastText), lastText);
jvanverthfca302c2014-10-20 13:12:54 -0700314 fallbackPos.push_back(pos[0]);
315 if (2 == scalarsPerPosition) {
316 fallbackPos.push_back(pos[1]);
317 }
318 }
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000319 }
320 pos += scalarsPerPosition;
321 }
322 } else {
jvanverth2b9dc1d2014-10-20 06:48:59 -0700323 SkScalar alignMul = SkPaint::kCenter_Align == fSkPaint.getTextAlign() ? SK_ScalarHalf
324 : SK_Scalar1;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000325 while (text < stop) {
jvanverthfca302c2014-10-20 13:12:54 -0700326 const char* lastText = text;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000327 // the last 2 parameters are ignored
328 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
329
330 if (glyph.fWidth) {
fmalita05c4a432014-09-29 06:29:53 -0700331 SkScalar x = offset.x() + pos[0];
332 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
skia.committer@gmail.com22e96722013-12-20 07:01:36 +0000333
jvanverth2b9dc1d2014-10-20 06:48:59 -0700334 SkScalar advanceX = SkFixedToScalar(glyph.fAdvanceX)*alignMul*fTextRatio;
335 SkScalar advanceY = SkFixedToScalar(glyph.fAdvanceY)*alignMul*fTextRatio;
336
jvanverthfca302c2014-10-20 13:12:54 -0700337 if (!this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
338 glyph.getSubXFixed(),
339 glyph.getSubYFixed()),
djsollen058f01e2014-10-30 11:54:43 -0700340 x - advanceX, y - advanceY, fontScaler)) {
jvanverthfca302c2014-10-20 13:12:54 -0700341 // couldn't append, send to fallback
bsalomonef3fcd82014-12-12 08:51:38 -0800342 fallbackTxt.push_back_n(SkToInt(text-lastText), lastText);
jvanverthfca302c2014-10-20 13:12:54 -0700343 fallbackPos.push_back(pos[0]);
344 if (2 == scalarsPerPosition) {
345 fallbackPos.push_back(pos[1]);
346 }
347 }
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000348 }
349 pos += scalarsPerPosition;
350 }
351 }
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000352
353 this->finish();
jvanverthfca302c2014-10-20 13:12:54 -0700354
355 if (fallbackTxt.count() > 0) {
joshualitt5531d512014-12-17 15:50:11 -0800356 fFallbackTextContext->drawPosText(paint, skPaint, viewMatrix, fallbackTxt.begin(),
357 fallbackTxt.count(), fallbackPos.begin(),
358 scalarsPerPosition, offset);
jvanverthfca302c2014-10-20 13:12:54 -0700359 }
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000360}
jvanverth0fedb192014-10-08 09:07:27 -0700361
362static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
363 unsigned r = SkColorGetR(c);
364 unsigned g = SkColorGetG(c);
365 unsigned b = SkColorGetB(c);
366 return GrColorPackRGBA(r, g, b, 0xff);
367}
368
joshualitt9853cce2014-11-17 14:22:48 -0800369static size_t get_vertex_stride(bool useColorVerts) {
370 return useColorVerts ? (2 * sizeof(SkPoint) + sizeof(GrColor)) :
371 (2 * sizeof(SkPoint));
372}
373
374static void* alloc_vertices(GrDrawTarget* drawTarget,
375 int numVertices,
376 bool useColorVerts) {
jvanverth73f10532014-10-23 11:57:12 -0700377 if (numVertices <= 0) {
378 return NULL;
379 }
380
jvanverth73f10532014-10-23 11:57:12 -0700381 void* vertices = NULL;
382 bool success = drawTarget->reserveVertexAndIndexSpace(numVertices,
joshualitt9853cce2014-11-17 14:22:48 -0800383 get_vertex_stride(useColorVerts),
jvanverth73f10532014-10-23 11:57:12 -0700384 0,
385 &vertices,
386 NULL);
387 GrAlwaysAssert(success);
388 return vertices;
389}
390
jvanverth0fedb192014-10-08 09:07:27 -0700391void GrDistanceFieldTextContext::setupCoverageEffect(const SkColor& filteredColor) {
392 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode);
393 GrTextureParams gammaParams(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
394
395 uint32_t textureUniqueID = fCurrTexture->getUniqueID();
joshualitt5531d512014-12-17 15:50:11 -0800396 const SkMatrix& ctm = fViewMatrix;
jvanverth0fedb192014-10-08 09:07:27 -0700397
398 // set up any flags
399 uint32_t flags = 0;
400 flags |= ctm.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
401 flags |= fUseLCDText ? kUseLCD_DistanceFieldEffectFlag : 0;
402 flags |= fUseLCDText && ctm.rectStaysRect() ?
403 kRectToRect_DistanceFieldEffectFlag : 0;
reedb2d77e42014-10-14 08:26:33 -0700404 bool useBGR = SkPixelGeometryIsBGR(fDeviceProperties.pixelGeometry());
jvanverth0fedb192014-10-08 09:07:27 -0700405 flags |= fUseLCDText && useBGR ? kBGR_DistanceFieldEffectFlag : 0;
406
407 // see if we need to create a new effect
408 if (textureUniqueID != fEffectTextureUniqueID ||
409 filteredColor != fEffectColor ||
410 flags != fEffectFlags) {
joshualitt2e3b3e32014-12-09 13:31:14 -0800411 GrColor color = fPaint.getColor();
jvanverth0fedb192014-10-08 09:07:27 -0700412 if (fUseLCDText) {
413 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
joshualitt2e3b3e32014-12-09 13:31:14 -0800414 fCachedGeometryProcessor.reset(GrDistanceFieldLCDTextureEffect::Create(color,
415 fCurrTexture,
jvanverth0fedb192014-10-08 09:07:27 -0700416 params,
417 fGammaTexture,
418 gammaParams,
419 colorNoPreMul,
420 flags));
421 } else {
joshualitt2dd1ae02014-12-03 06:24:10 -0800422 flags |= kColorAttr_DistanceFieldEffectFlag;
joshualitt56995b52014-12-11 15:44:02 -0800423 bool opaque = GrColorIsOpaque(color);
jvanverth0fedb192014-10-08 09:07:27 -0700424#ifdef SK_GAMMA_APPLY_TO_A8
reedb2d77e42014-10-14 08:26:33 -0700425 U8CPU lum = SkColorSpaceLuminance::computeLuminance(fDeviceProperties.gamma(),
jvanverth0fedb192014-10-08 09:07:27 -0700426 filteredColor);
joshualitt2e3b3e32014-12-09 13:31:14 -0800427 fCachedGeometryProcessor.reset(GrDistanceFieldTextureEffect::Create(color,
428 fCurrTexture,
jvanverth0fedb192014-10-08 09:07:27 -0700429 params,
430 fGammaTexture,
431 gammaParams,
432 lum/255.f,
joshualitt56995b52014-12-11 15:44:02 -0800433 flags,
434 opaque));
jvanverth0fedb192014-10-08 09:07:27 -0700435#else
joshualitt2e3b3e32014-12-09 13:31:14 -0800436 fCachedGeometryProcessor.reset(GrDistanceFieldNoGammaTextureEffect::Create(color,
437 fCurrTexture,
438 params,
joshualitt56995b52014-12-11 15:44:02 -0800439 flags,
440 opaque));
jvanverth0fedb192014-10-08 09:07:27 -0700441#endif
442 }
443 fEffectTextureUniqueID = textureUniqueID;
444 fEffectColor = filteredColor;
445 fEffectFlags = flags;
446 }
447
448}
449
jvanverth787cdf92014-12-04 10:46:50 -0800450inline bool GrDistanceFieldTextContext::uploadGlyph(GrGlyph* glyph, GrFontScaler* scaler) {
451 if (!fStrike->glyphTooLargeForAtlas(glyph)) {
452 if (fStrike->addGlyphToAtlas(glyph, scaler)) {
453 return true;
454 }
455
456 // try to clear out an unused plot before we flush
457 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
458 fStrike->addGlyphToAtlas(glyph, scaler)) {
459 return true;
460 }
461
462 if (c_DumpFontCache) {
463#ifdef SK_DEVELOPER
464 fContext->getFontCache()->dump();
465#endif
466 }
467
468 // before we purge the cache, we must flush any accumulated draws
469 this->flush();
470 fContext->flush();
471
472 // we should have an unused plot now
473 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
474 fStrike->addGlyphToAtlas(glyph, scaler)) {
475 return true;
476 }
477
478 // we should never get here
479 SkASSERT(false);
480 }
481
482 return false;
483}
484
485
jvanverthfca302c2014-10-20 13:12:54 -0700486// Returns true if this method handled the glyph, false if needs to be passed to fallback
487//
488bool GrDistanceFieldTextContext::appendGlyph(GrGlyph::PackedID packed,
djsollen058f01e2014-10-30 11:54:43 -0700489 SkScalar sx, SkScalar sy,
jvanverth0fedb192014-10-08 09:07:27 -0700490 GrFontScaler* scaler) {
491 if (NULL == fDrawTarget) {
jvanverthfca302c2014-10-20 13:12:54 -0700492 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700493 }
494
495 if (NULL == fStrike) {
496 fStrike = fContext->getFontCache()->getStrike(scaler, true);
497 }
498
499 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
500 if (NULL == glyph || glyph->fBounds.isEmpty()) {
jvanverthfca302c2014-10-20 13:12:54 -0700501 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700502 }
503
jvanverthfca302c2014-10-20 13:12:54 -0700504 // fallback to color glyph support
jvanverth294c3262014-10-10 11:36:12 -0700505 if (kA8_GrMaskFormat != glyph->fMaskFormat) {
jvanverthfca302c2014-10-20 13:12:54 -0700506 return false;
jvanverth294c3262014-10-10 11:36:12 -0700507 }
508
jvanverth2faa2282014-10-31 12:59:57 -0700509 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
510 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
511 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2*SK_DistanceFieldInset);
512 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2*SK_DistanceFieldInset);
jvanverth0fedb192014-10-08 09:07:27 -0700513
jvanverth2faa2282014-10-31 12:59:57 -0700514 SkScalar scale = fTextRatio;
515 dx *= scale;
516 dy *= scale;
517 sx += dx;
518 sy += dy;
519 width *= scale;
520 height *= scale;
521 SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height);
jvanverth0fedb192014-10-08 09:07:27 -0700522
523 // check if we clipped out
jvanverth2faa2282014-10-31 12:59:57 -0700524 SkRect dstRect;
joshualitt5531d512014-12-17 15:50:11 -0800525 const SkMatrix& ctm = fViewMatrix;
jvanverth2faa2282014-10-31 12:59:57 -0700526 (void) ctm.mapRect(&dstRect, glyphRect);
527 if (fClipRect.quickReject(SkScalarTruncToInt(dstRect.left()),
528 SkScalarTruncToInt(dstRect.top()),
529 SkScalarTruncToInt(dstRect.right()),
530 SkScalarTruncToInt(dstRect.bottom()))) {
jvanverth0fedb192014-10-08 09:07:27 -0700531// SkCLZ(3); // so we can set a break-point in the debugger
jvanverth2faa2282014-10-31 12:59:57 -0700532 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700533 }
jvanverth2faa2282014-10-31 12:59:57 -0700534
jvanverth95f9b5f2014-12-05 07:56:53 -0800535 if (NULL == glyph->fPlot) {
536 // needs to be a separate conditional to avoid over-optimization
537 // on Nexus 7 and Nexus 10
joshualittc2625822014-12-18 16:40:54 -0800538
539 // If the glyph is too large we fall back to paths
jvanverth95f9b5f2014-12-05 07:56:53 -0800540 if (!uploadGlyph(glyph, scaler)) {
541 if (NULL == glyph->fPath) {
542 SkPath* path = SkNEW(SkPath);
543 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
544 // flag the glyph as being dead?
545 delete path;
546 return true;
547 }
548 glyph->fPath = path;
jvanverth0fedb192014-10-08 09:07:27 -0700549 }
jvanverth95f9b5f2014-12-05 07:56:53 -0800550
551 // flush any accumulated draws before drawing this glyph as a path.
552 this->flush();
553
jvanverth95f9b5f2014-12-05 07:56:53 -0800554 SkMatrix ctm;
555 ctm.setScale(fTextRatio, fTextRatio);
556 ctm.postTranslate(sx - dx, sy - dy);
joshualitt5531d512014-12-17 15:50:11 -0800557
joshualittc2625822014-12-18 16:40:54 -0800558 SkPath tmpPath(*glyph->fPath);
559 tmpPath.transform(ctm);
560
jvanverth95f9b5f2014-12-05 07:56:53 -0800561 GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle);
joshualittc2625822014-12-18 16:40:54 -0800562 fContext->drawPath(fPaint, fViewMatrix, tmpPath, strokeInfo);
jvanverth95f9b5f2014-12-05 07:56:53 -0800563
564 // remove this glyph from the vertices we need to allocate
565 fTotalVertexCount -= kVerticesPerGlyph;
566 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700567 }
jvanverth0fedb192014-10-08 09:07:27 -0700568 }
569
jvanverth0fedb192014-10-08 09:07:27 -0700570 SkASSERT(glyph->fPlot);
571 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
572 glyph->fPlot->setDrawToken(drawToken);
573
574 GrTexture* texture = glyph->fPlot->texture();
575 SkASSERT(texture);
576
jvanverth73f10532014-10-23 11:57:12 -0700577 if (fCurrTexture != texture || fCurrVertex + kVerticesPerGlyph > fTotalVertexCount) {
jvanverth0fedb192014-10-08 09:07:27 -0700578 this->flush();
579 fCurrTexture = texture;
580 fCurrTexture->ref();
581 }
582
583 bool useColorVerts = !fUseLCDText;
584
585 if (NULL == fVertices) {
jvanverth73f10532014-10-23 11:57:12 -0700586 int maxQuadVertices = kVerticesPerGlyph * fContext->getQuadIndexBuffer()->maxQuads();
587 fAllocVertexCount = SkMin32(fTotalVertexCount, maxQuadVertices);
joshualitt9853cce2014-11-17 14:22:48 -0800588 fVertices = alloc_vertices(fDrawTarget,
589 fAllocVertexCount,
590 useColorVerts);
jvanverth0fedb192014-10-08 09:07:27 -0700591 }
592
jvanverth0fedb192014-10-08 09:07:27 -0700593 SkFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX + SK_DistanceFieldInset);
594 SkFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY + SK_DistanceFieldInset);
595 SkFixed tw = SkIntToFixed(glyph->fBounds.width() - 2*SK_DistanceFieldInset);
596 SkFixed th = SkIntToFixed(glyph->fBounds.height() - 2*SK_DistanceFieldInset);
597
jvanverth2faa2282014-10-31 12:59:57 -0700598 fVertexBounds.joinNonEmptyArg(glyphRect);
jvanverth0fedb192014-10-08 09:07:27 -0700599
joshualitt9853cce2014-11-17 14:22:48 -0800600 size_t vertSize = get_vertex_stride(useColorVerts);
jvanverth0fedb192014-10-08 09:07:27 -0700601
602 SkPoint* positions = reinterpret_cast<SkPoint*>(
603 reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVertex);
jvanverth2faa2282014-10-31 12:59:57 -0700604 positions->setRectFan(glyphRect.fLeft, glyphRect.fTop, glyphRect.fRight, glyphRect.fBottom,
605 vertSize);
jvanverth0fedb192014-10-08 09:07:27 -0700606
607 // The texture coords are last in both the with and without color vertex layouts.
608 SkPoint* textureCoords = reinterpret_cast<SkPoint*>(
609 reinterpret_cast<intptr_t>(positions) + vertSize - sizeof(SkPoint));
610 textureCoords->setRectFan(SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx)),
611 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty)),
612 SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx + tw)),
613 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty + th)),
614 vertSize);
615 if (useColorVerts) {
jvanverth0fedb192014-10-08 09:07:27 -0700616 // color comes after position.
617 GrColor* colors = reinterpret_cast<GrColor*>(positions + 1);
618 for (int i = 0; i < 4; ++i) {
619 *colors = fPaint.getColor();
620 colors = reinterpret_cast<GrColor*>(reinterpret_cast<intptr_t>(colors) + vertSize);
621 }
622 }
623
624 fCurrVertex += 4;
jvanverthfca302c2014-10-20 13:12:54 -0700625
626 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700627}
628
629void GrDistanceFieldTextContext::flush() {
630 if (NULL == fDrawTarget) {
631 return;
632 }
633
jvanverth0fedb192014-10-08 09:07:27 -0700634 if (fCurrVertex > 0) {
joshualitt780b11e2014-11-18 09:40:40 -0800635 GrDrawState drawState;
joshualitt5531d512014-12-17 15:50:11 -0800636 drawState.setFromPaint(fPaint, fViewMatrix, fContext->getRenderTarget());
joshualitt780b11e2014-11-18 09:40:40 -0800637
jvanverth0fedb192014-10-08 09:07:27 -0700638 // setup our sampler state for our text texture/atlas
639 SkASSERT(SkIsAlign4(fCurrVertex));
640
641 // get our current color
642 SkColor filteredColor;
643 SkColorFilter* colorFilter = fSkPaint.getColorFilter();
644 if (colorFilter) {
645 filteredColor = colorFilter->filterColor(fSkPaint.getColor());
646 } else {
647 filteredColor = fSkPaint.getColor();
648 }
649 this->setupCoverageEffect(filteredColor);
650
jvanverth0fedb192014-10-08 09:07:27 -0700651 // Set draw state
652 if (fUseLCDText) {
egdaniel378092f2014-12-03 10:40:13 -0800653 // TODO: move supportsRGBCoverage check to setupCoverageEffect and only add LCD
654 // processor if the xp can support it. For now we will simply assume that if
655 // fUseLCDText is true, then we have a known color output.
656 if (!drawState.getXPFactory()->supportsRGBCoverage(0, kRGBA_GrColorComponentFlags)) {
tfarina38406c82014-10-31 07:11:12 -0700657 SkDebugf("LCD Text will not draw correctly.\n");
jvanverth0fedb192014-10-08 09:07:27 -0700658 }
joshualitt56995b52014-12-11 15:44:02 -0800659 SkASSERT(!fCachedGeometryProcessor->hasVertexColor());
jvanverth0fedb192014-10-08 09:07:27 -0700660 } else {
jvanverth0fedb192014-10-08 09:07:27 -0700661 // We're using per-vertex color.
joshualitt56995b52014-12-11 15:44:02 -0800662 SkASSERT(fCachedGeometryProcessor->hasVertexColor());
jvanverth0fedb192014-10-08 09:07:27 -0700663 }
jvanverth73f10532014-10-23 11:57:12 -0700664 int nGlyphs = fCurrVertex / kVerticesPerGlyph;
jvanverth0fedb192014-10-08 09:07:27 -0700665 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
joshualitt9853cce2014-11-17 14:22:48 -0800666 fDrawTarget->drawIndexedInstances(&drawState,
joshualitt56995b52014-12-11 15:44:02 -0800667 fCachedGeometryProcessor.get(),
joshualitt9853cce2014-11-17 14:22:48 -0800668 kTriangles_GrPrimitiveType,
jvanverth0fedb192014-10-08 09:07:27 -0700669 nGlyphs,
joshualitt9853cce2014-11-17 14:22:48 -0800670 kVerticesPerGlyph,
671 kIndicesPerGlyph,
672 &fVertexBounds);
jvanverth0fedb192014-10-08 09:07:27 -0700673 fDrawTarget->resetVertexSource();
674 fVertices = NULL;
jvanverth73f10532014-10-23 11:57:12 -0700675 fTotalVertexCount -= fCurrVertex;
jvanverth0fedb192014-10-08 09:07:27 -0700676 fCurrVertex = 0;
677 SkSafeSetNull(fCurrTexture);
678 fVertexBounds.setLargestInverted();
679 }
680}
681
682inline void GrDistanceFieldTextContext::finish() {
683 this->flush();
jvanverth73f10532014-10-23 11:57:12 -0700684 fTotalVertexCount = 0;
jvanverth0fedb192014-10-08 09:07:27 -0700685
686 GrTextContext::finish();
687}
688