blob: a9fe4c9bf6fac7f1ff2213c123533dd70a70b5af [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"
jvanverthbe03bb62015-03-10 11:53:39 -070013#include "GrFontAtlasSizes.h"
jvanverth787cdf92014-12-04 10:46:50 -080014#include "GrFontCache.h"
jvanverth@google.comd830d132013-11-11 20:54:09 +000015#include "GrFontScaler.h"
jvanverth2d2a68c2014-06-10 06:42:56 -070016#include "GrGpu.h"
jvanverth@google.comd830d132013-11-11 20:54:09 +000017#include "GrIndexBuffer.h"
egdanield58a0ba2014-06-11 10:30:05 -070018#include "GrStrokeInfo.h"
bsalomonafbf2d62014-09-30 12:18:44 -070019#include "GrTexturePriv.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;
jvanverthbe03bb62015-03-10 11:53:39 -070037static const int kMediumDFFontSize = 70;
38static const int kMediumDFFontLimit = 70;
39static const int kLargeDFFontSize = 156;
40SK_COMPILE_ASSERT(GR_SDF_MAX_SIZE >= kLargeDFFontSize, sdf_too_big);
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +000041
jvanverth73f10532014-10-23 11:57:12 -070042static const int kVerticesPerGlyph = 4;
43static const int kIndicesPerGlyph = 6;
jvanverth@google.comd830d132013-11-11 20:54:09 +000044
skia.committer@gmail.come5d70152014-01-29 07:01:48 +000045GrDistanceFieldTextContext::GrDistanceFieldTextContext(GrContext* context,
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000046 const SkDeviceProperties& properties,
47 bool enable)
Mike Klein6a25bd02014-08-29 10:03:59 -040048 : GrTextContext(context, properties) {
jvanverth4736e142014-11-07 07:12:46 -080049#if SK_FORCE_DISTANCE_FIELD_TEXT
Mike Klein6a25bd02014-08-29 10:03:59 -040050 fEnableDFRendering = true;
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000051#else
Mike Klein6a25bd02014-08-29 10:03:59 -040052 fEnableDFRendering = enable;
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000053#endif
Mike Klein6a25bd02014-08-29 10:03:59 -040054 fStrike = NULL;
55 fGammaTexture = NULL;
56
Mike Klein6a25bd02014-08-29 10:03:59 -040057 fEffectTextureUniqueID = SK_InvalidUniqueID;
58 fEffectColor = GrColor_ILLEGAL;
jvanverth6d22eca2014-10-28 11:10:48 -070059 fEffectFlags = kInvalid_DistanceFieldEffectFlag;
Mike Klein6a25bd02014-08-29 10:03:59 -040060
61 fVertices = NULL;
jvanverth73f10532014-10-23 11:57:12 -070062 fCurrVertex = 0;
63 fAllocVertexCount = 0;
64 fTotalVertexCount = 0;
65 fCurrTexture = NULL;
Mike Klein6a25bd02014-08-29 10:03:59 -040066
jvanverth1723bfc2014-07-30 09:16:33 -070067 fVertexBounds.setLargestInverted();
jvanverth@google.comd830d132013-11-11 20:54:09 +000068}
69
jvanverth8c27a182014-10-14 08:45:50 -070070GrDistanceFieldTextContext* GrDistanceFieldTextContext::Create(GrContext* context,
71 const SkDeviceProperties& props,
72 bool enable) {
73 GrDistanceFieldTextContext* textContext = SkNEW_ARGS(GrDistanceFieldTextContext,
74 (context, props, enable));
75 textContext->fFallbackTextContext = GrBitmapTextContext::Create(context, props);
76
77 return textContext;
78}
79
jvanverth@google.comd830d132013-11-11 20:54:09 +000080GrDistanceFieldTextContext::~GrDistanceFieldTextContext() {
jvanverth2d2a68c2014-06-10 06:42:56 -070081 SkSafeSetNull(fGammaTexture);
jvanverth@google.comd830d132013-11-11 20:54:09 +000082}
83
joshualitt5531d512014-12-17 15:50:11 -080084bool GrDistanceFieldTextContext::canDraw(const SkPaint& paint, const SkMatrix& viewMatrix) {
jvanverth5a3d92f2015-01-28 13:08:40 -080085 // TODO: support perspective (need getMaxScale replacement)
86 if (viewMatrix.hasPerspective()) {
87 return false;
88 }
89
90 SkScalar maxScale = viewMatrix.getMaxScale();
91 SkScalar scaledTextSize = maxScale*paint.getTextSize();
92 // Scaling up beyond 2x yields undesireable artifacts
93 if (scaledTextSize > 2*kLargeDFFontSize) {
94 return false;
95 }
96
97 if (!fEnableDFRendering && !paint.isDistanceFieldTextTEMP() &&
98 scaledTextSize < kLargeDFFontSize) {
commit-bot@chromium.orgeefd8a02014-04-08 20:14:32 +000099 return false;
100 }
101
skia.committer@gmail.come1d94432014-04-09 03:04:11 +0000102 // rasterizers and mask filters modify alpha, which doesn't
commit-bot@chromium.orgeefd8a02014-04-08 20:14:32 +0000103 // translate well to distance
104 if (paint.getRasterizer() || paint.getMaskFilter() ||
105 !fContext->getTextTarget()->caps()->shaderDerivativeSupport()) {
106 return false;
107 }
108
109 // TODO: add some stroking support
110 if (paint.getStyle() != SkPaint::kFill_Style) {
111 return false;
112 }
113
jvanverth2faa2282014-10-31 12:59:57 -0700114 return true;
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000115}
116
joshualitt570d2f82015-02-25 13:19:48 -0800117inline void GrDistanceFieldTextContext::init(GrRenderTarget* rt, const GrClip& clip,
118 const GrPaint& paint, const SkPaint& skPaint) {
119 GrTextContext::init(rt, clip, paint, skPaint);
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000120
121 fStrike = NULL;
122
joshualitt5531d512014-12-17 15:50:11 -0800123 const SkMatrix& ctm = fViewMatrix;
jvanverth9564ce62014-09-16 05:45:19 -0700124
125 // getMaxScale doesn't support perspective, so neither do we at the moment
jvanverth76ce81e2014-09-22 14:26:53 -0700126 SkASSERT(!ctm.hasPerspective());
127 SkScalar maxScale = ctm.getMaxScale();
jvanverth9564ce62014-09-16 05:45:19 -0700128 SkScalar textSize = fSkPaint.getTextSize();
jvanverth76ce81e2014-09-22 14:26:53 -0700129 SkScalar scaledTextSize = textSize;
130 // if we have non-unity scale, we need to choose our base text size
131 // based on the SkPaint's text size multiplied by the max scale factor
jvanverth9564ce62014-09-16 05:45:19 -0700132 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
133 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
jvanverth76ce81e2014-09-22 14:26:53 -0700134 scaledTextSize *= maxScale;
jvanverth9564ce62014-09-16 05:45:19 -0700135 }
136
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000137 fVertices = NULL;
jvanverth73f10532014-10-23 11:57:12 -0700138 fCurrVertex = 0;
139 fAllocVertexCount = 0;
140 fTotalVertexCount = 0;
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000141
jvanverth76ce81e2014-09-22 14:26:53 -0700142 if (scaledTextSize <= kSmallDFFontLimit) {
jvanverth9564ce62014-09-16 05:45:19 -0700143 fTextRatio = textSize / kSmallDFFontSize;
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000144 fSkPaint.setTextSize(SkIntToScalar(kSmallDFFontSize));
jvanverthada68ef2014-11-03 14:00:24 -0800145#if DEBUG_TEXT_SIZE
146 fSkPaint.setColor(SkColorSetARGB(0xFF, 0x00, 0x00, 0xFF));
147 fPaint.setColor(GrColorPackRGBA(0x00, 0x00, 0xFF, 0xFF));
148#endif
jvanverth76ce81e2014-09-22 14:26:53 -0700149 } else if (scaledTextSize <= kMediumDFFontLimit) {
jvanverth9564ce62014-09-16 05:45:19 -0700150 fTextRatio = textSize / kMediumDFFontSize;
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000151 fSkPaint.setTextSize(SkIntToScalar(kMediumDFFontSize));
jvanverthada68ef2014-11-03 14:00:24 -0800152#if DEBUG_TEXT_SIZE
153 fSkPaint.setColor(SkColorSetARGB(0xFF, 0x00, 0xFF, 0x00));
154 fPaint.setColor(GrColorPackRGBA(0x00, 0xFF, 0x00, 0xFF));
155#endif
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000156 } else {
jvanverth9564ce62014-09-16 05:45:19 -0700157 fTextRatio = textSize / kLargeDFFontSize;
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000158 fSkPaint.setTextSize(SkIntToScalar(kLargeDFFontSize));
jvanverthada68ef2014-11-03 14:00:24 -0800159#if DEBUG_TEXT_SIZE
160 fSkPaint.setColor(SkColorSetARGB(0xFF, 0xFF, 0x00, 0x00));
161 fPaint.setColor(GrColorPackRGBA(0xFF, 0x00, 0x00, 0xFF));
162#endif
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000163 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000164
commit-bot@chromium.org609ced42014-04-03 18:25:48 +0000165 fUseLCDText = fSkPaint.isLCDRenderText();
skia.committer@gmail.com221b9112014-04-04 03:04:32 +0000166
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000167 fSkPaint.setLCDRenderText(false);
168 fSkPaint.setAutohinted(false);
jvanverth2d2a68c2014-06-10 06:42:56 -0700169 fSkPaint.setHinting(SkPaint::kNormal_Hinting);
commit-bot@chromium.org0bed43c2014-03-14 21:22:38 +0000170 fSkPaint.setSubpixelText(true);
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000171}
172
jvanverth2d2a68c2014-06-10 06:42:56 -0700173static void setup_gamma_texture(GrContext* context, const SkGlyphCache* cache,
174 const SkDeviceProperties& deviceProperties,
175 GrTexture** gammaTexture) {
176 if (NULL == *gammaTexture) {
177 int width, height;
178 size_t size;
179
180#ifdef SK_GAMMA_CONTRAST
181 SkScalar contrast = SK_GAMMA_CONTRAST;
182#else
183 SkScalar contrast = 0.5f;
184#endif
reedb2d77e42014-10-14 08:26:33 -0700185 SkScalar paintGamma = deviceProperties.gamma();
186 SkScalar deviceGamma = deviceProperties.gamma();
jvanverth2d2a68c2014-06-10 06:42:56 -0700187
188 size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
189 &width, &height);
190
191 SkAutoTArray<uint8_t> data((int)size);
192 SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
193
194 // TODO: Update this to use the cache rather than directly creating a texture.
bsalomonf2703d82014-10-28 14:33:06 -0700195 GrSurfaceDesc desc;
196 desc.fFlags = kNone_GrSurfaceFlags;
jvanverth2d2a68c2014-06-10 06:42:56 -0700197 desc.fWidth = width;
198 desc.fHeight = height;
199 desc.fConfig = kAlpha_8_GrPixelConfig;
200
bsalomon5236cf42015-01-14 10:42:08 -0800201 *gammaTexture = context->getGpu()->createTexture(desc, true, NULL, 0);
jvanverth2d2a68c2014-06-10 06:42:56 -0700202 if (NULL == *gammaTexture) {
203 return;
204 }
205
bsalomon81beccc2014-10-13 12:32:55 -0700206 (*gammaTexture)->writePixels(0, 0, width, height,
207 (*gammaTexture)->config(), data.get(), 0,
208 GrContext::kDontFlush_PixelOpsFlag);
jvanverth2d2a68c2014-06-10 06:42:56 -0700209 }
210}
211
joshualitt570d2f82015-02-25 13:19:48 -0800212void GrDistanceFieldTextContext::onDrawText(GrRenderTarget* rt, const GrClip& clip,
213 const GrPaint& paint,
joshualitt25d9c152015-02-18 12:29:52 -0800214 const SkPaint& skPaint, const SkMatrix& viewMatrix,
joshualitt5531d512014-12-17 15:50:11 -0800215 const char text[], size_t byteLength,
216 SkScalar x, SkScalar y) {
jvanverthaab626c2014-10-16 08:04:39 -0700217 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
joshualitt5531d512014-12-17 15:50:11 -0800224 fViewMatrix = viewMatrix;
jvanverth2b9dc1d2014-10-20 06:48:59 -0700225 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
226 SkAutoGlyphCache autoCache(skPaint, &fDeviceProperties, NULL);
227 SkGlyphCache* cache = autoCache.getCache();
jvanverthaab626c2014-10-16 08:04:39 -0700228
jvanverth2b9dc1d2014-10-20 06:48:59 -0700229 SkTArray<SkScalar> positions;
jvanverthaab626c2014-10-16 08:04:39 -0700230
jvanverth2b9dc1d2014-10-20 06:48:59 -0700231 const char* textPtr = text;
232 SkFixed stopX = 0;
233 SkFixed stopY = 0;
234 SkFixed origin;
235 switch (skPaint.getTextAlign()) {
236 case SkPaint::kRight_Align: origin = SK_Fixed1; break;
237 case SkPaint::kCenter_Align: origin = SK_FixedHalf; break;
238 case SkPaint::kLeft_Align: origin = 0; break;
239 default: SkFAIL("Invalid paint origin"); return;
240 }
jvanverthaab626c2014-10-16 08:04:39 -0700241
jvanverth2b9dc1d2014-10-20 06:48:59 -0700242 SkAutoKern autokern;
jvanverthaab626c2014-10-16 08:04:39 -0700243 const char* stop = text + byteLength;
jvanverth2b9dc1d2014-10-20 06:48:59 -0700244 while (textPtr < stop) {
245 // don't need x, y here, since all subpixel variants will have the
246 // same advance
247 const SkGlyph& glyph = glyphCacheProc(cache, &textPtr, 0, 0);
jvanverthaab626c2014-10-16 08:04:39 -0700248
jvanverth2b9dc1d2014-10-20 06:48:59 -0700249 SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph);
250 positions.push_back(SkFixedToScalar(stopX + SkFixedMul_portable(origin, width)));
jvanverthaab626c2014-10-16 08:04:39 -0700251
jvanverth2b9dc1d2014-10-20 06:48:59 -0700252 SkFixed height = glyph.fAdvanceY;
253 positions.push_back(SkFixedToScalar(stopY + SkFixedMul_portable(origin, height)));
jvanverthaab626c2014-10-16 08:04:39 -0700254
jvanverth2b9dc1d2014-10-20 06:48:59 -0700255 stopX += width;
256 stopY += height;
jvanverthaab626c2014-10-16 08:04:39 -0700257 }
jvanverth2b9dc1d2014-10-20 06:48:59 -0700258 SkASSERT(textPtr == stop);
jvanverthaab626c2014-10-16 08:04:39 -0700259
jvanverth2b9dc1d2014-10-20 06:48:59 -0700260 // now adjust starting point depending on alignment
261 SkScalar alignX = SkFixedToScalar(stopX);
262 SkScalar alignY = SkFixedToScalar(stopY);
263 if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
264 alignX = SkScalarHalf(alignX);
265 alignY = SkScalarHalf(alignY);
266 } else if (skPaint.getTextAlign() == SkPaint::kLeft_Align) {
267 alignX = 0;
268 alignY = 0;
jvanverthaab626c2014-10-16 08:04:39 -0700269 }
jvanverth2b9dc1d2014-10-20 06:48:59 -0700270 x -= alignX;
271 y -= alignY;
272 SkPoint offset = SkPoint::Make(x, y);
jvanverthaab626c2014-10-16 08:04:39 -0700273
joshualitt570d2f82015-02-25 13:19:48 -0800274 this->drawPosText(rt, clip, paint, skPaint, viewMatrix, text, byteLength, positions.begin(), 2,
joshualitt25d9c152015-02-18 12:29:52 -0800275 offset);
jvanverthaab626c2014-10-16 08:04:39 -0700276}
277
joshualitt570d2f82015-02-25 13:19:48 -0800278void GrDistanceFieldTextContext::onDrawPosText(GrRenderTarget* rt, const GrClip& clip,
279 const GrPaint& paint,
joshualitt25d9c152015-02-18 12:29:52 -0800280 const SkPaint& skPaint, const SkMatrix& viewMatrix,
joshualitt5531d512014-12-17 15:50:11 -0800281 const char text[], size_t byteLength,
282 const SkScalar pos[], int scalarsPerPosition,
283 const SkPoint& offset) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000284
285 SkASSERT(byteLength == 0 || text != NULL);
286 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
287
288 // nothing to draw
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000289 if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000290 return;
291 }
292
joshualitt5531d512014-12-17 15:50:11 -0800293 fViewMatrix = viewMatrix;
joshualitt570d2f82015-02-25 13:19:48 -0800294 this->init(rt, clip, paint, skPaint);
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000295
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000296 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
297
jvanverth2d2a68c2014-06-10 06:42:56 -0700298 SkAutoGlyphCacheNoGamma autoCache(fSkPaint, &fDeviceProperties, NULL);
299 SkGlyphCache* cache = autoCache.getCache();
300 GrFontScaler* fontScaler = GetGrFontScaler(cache);
301
302 setup_gamma_texture(fContext, cache, fDeviceProperties, &fGammaTexture);
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000303
jvanverth73f10532014-10-23 11:57:12 -0700304 int numGlyphs = fSkPaint.textToGlyphs(text, byteLength, NULL);
305 fTotalVertexCount = kVerticesPerGlyph*numGlyphs;
306
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000307 const char* stop = text + byteLength;
jvanverthfca302c2014-10-20 13:12:54 -0700308 SkTArray<char> fallbackTxt;
309 SkTArray<SkScalar> fallbackPos;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000310
311 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
312 while (text < stop) {
jvanverthfca302c2014-10-20 13:12:54 -0700313 const char* lastText = text;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000314 // the last 2 parameters are ignored
315 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
316
317 if (glyph.fWidth) {
fmalita05c4a432014-09-29 06:29:53 -0700318 SkScalar x = offset.x() + pos[0];
319 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000320
jvanverthfca302c2014-10-20 13:12:54 -0700321 if (!this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
322 glyph.getSubXFixed(),
323 glyph.getSubYFixed()),
djsollen058f01e2014-10-30 11:54:43 -0700324 x, y, fontScaler)) {
jvanverthfca302c2014-10-20 13:12:54 -0700325 // couldn't append, send to fallback
bsalomonef3fcd82014-12-12 08:51:38 -0800326 fallbackTxt.push_back_n(SkToInt(text-lastText), lastText);
jvanverthfca302c2014-10-20 13:12:54 -0700327 fallbackPos.push_back(pos[0]);
328 if (2 == scalarsPerPosition) {
329 fallbackPos.push_back(pos[1]);
330 }
331 }
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000332 }
333 pos += scalarsPerPosition;
334 }
335 } else {
jvanverth2b9dc1d2014-10-20 06:48:59 -0700336 SkScalar alignMul = SkPaint::kCenter_Align == fSkPaint.getTextAlign() ? SK_ScalarHalf
337 : SK_Scalar1;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000338 while (text < stop) {
jvanverthfca302c2014-10-20 13:12:54 -0700339 const char* lastText = text;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000340 // the last 2 parameters are ignored
341 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
342
343 if (glyph.fWidth) {
fmalita05c4a432014-09-29 06:29:53 -0700344 SkScalar x = offset.x() + pos[0];
345 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
skia.committer@gmail.com22e96722013-12-20 07:01:36 +0000346
jvanverth2b9dc1d2014-10-20 06:48:59 -0700347 SkScalar advanceX = SkFixedToScalar(glyph.fAdvanceX)*alignMul*fTextRatio;
348 SkScalar advanceY = SkFixedToScalar(glyph.fAdvanceY)*alignMul*fTextRatio;
349
jvanverthfca302c2014-10-20 13:12:54 -0700350 if (!this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
351 glyph.getSubXFixed(),
352 glyph.getSubYFixed()),
djsollen058f01e2014-10-30 11:54:43 -0700353 x - advanceX, y - advanceY, fontScaler)) {
jvanverthfca302c2014-10-20 13:12:54 -0700354 // couldn't append, send to fallback
bsalomonef3fcd82014-12-12 08:51:38 -0800355 fallbackTxt.push_back_n(SkToInt(text-lastText), lastText);
jvanverthfca302c2014-10-20 13:12:54 -0700356 fallbackPos.push_back(pos[0]);
357 if (2 == scalarsPerPosition) {
358 fallbackPos.push_back(pos[1]);
359 }
360 }
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000361 }
362 pos += scalarsPerPosition;
363 }
364 }
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000365
366 this->finish();
jvanverthfca302c2014-10-20 13:12:54 -0700367
368 if (fallbackTxt.count() > 0) {
joshualitt570d2f82015-02-25 13:19:48 -0800369 fFallbackTextContext->drawPosText(rt, clip, paint, skPaint, viewMatrix, fallbackTxt.begin(),
joshualitt5531d512014-12-17 15:50:11 -0800370 fallbackTxt.count(), fallbackPos.begin(),
371 scalarsPerPosition, offset);
jvanverthfca302c2014-10-20 13:12:54 -0700372 }
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000373}
jvanverth0fedb192014-10-08 09:07:27 -0700374
375static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
376 unsigned r = SkColorGetR(c);
377 unsigned g = SkColorGetG(c);
378 unsigned b = SkColorGetB(c);
379 return GrColorPackRGBA(r, g, b, 0xff);
380}
381
joshualitt9853cce2014-11-17 14:22:48 -0800382static size_t get_vertex_stride(bool useColorVerts) {
jvanverth5a105ff2015-02-18 11:36:35 -0800383 return useColorVerts ? (sizeof(SkPoint) + sizeof(GrColor) + sizeof(SkIPoint16)) :
384 (sizeof(SkPoint) + sizeof(SkIPoint16));
joshualitt9853cce2014-11-17 14:22:48 -0800385}
386
387static void* alloc_vertices(GrDrawTarget* drawTarget,
388 int numVertices,
389 bool useColorVerts) {
jvanverth73f10532014-10-23 11:57:12 -0700390 if (numVertices <= 0) {
391 return NULL;
392 }
393
jvanverth73f10532014-10-23 11:57:12 -0700394 void* vertices = NULL;
395 bool success = drawTarget->reserveVertexAndIndexSpace(numVertices,
joshualitt9853cce2014-11-17 14:22:48 -0800396 get_vertex_stride(useColorVerts),
jvanverth73f10532014-10-23 11:57:12 -0700397 0,
398 &vertices,
399 NULL);
400 GrAlwaysAssert(success);
401 return vertices;
402}
403
jvanverth0fedb192014-10-08 09:07:27 -0700404void GrDistanceFieldTextContext::setupCoverageEffect(const SkColor& filteredColor) {
jvanverthbe03bb62015-03-10 11:53:39 -0700405 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode);
jvanverth0fedb192014-10-08 09:07:27 -0700406 GrTextureParams gammaParams(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
407
408 uint32_t textureUniqueID = fCurrTexture->getUniqueID();
joshualitt5531d512014-12-17 15:50:11 -0800409 const SkMatrix& ctm = fViewMatrix;
jvanverth0fedb192014-10-08 09:07:27 -0700410
411 // set up any flags
412 uint32_t flags = 0;
413 flags |= ctm.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
414 flags |= fUseLCDText ? kUseLCD_DistanceFieldEffectFlag : 0;
415 flags |= fUseLCDText && ctm.rectStaysRect() ?
416 kRectToRect_DistanceFieldEffectFlag : 0;
reedb2d77e42014-10-14 08:26:33 -0700417 bool useBGR = SkPixelGeometryIsBGR(fDeviceProperties.pixelGeometry());
jvanverth0fedb192014-10-08 09:07:27 -0700418 flags |= fUseLCDText && useBGR ? kBGR_DistanceFieldEffectFlag : 0;
419
420 // see if we need to create a new effect
421 if (textureUniqueID != fEffectTextureUniqueID ||
422 filteredColor != fEffectColor ||
joshualitt8059eb92014-12-29 15:10:07 -0800423 flags != fEffectFlags ||
424 !fCachedGeometryProcessor->viewMatrix().cheapEqualTo(fViewMatrix)) {
joshualitt2e3b3e32014-12-09 13:31:14 -0800425 GrColor color = fPaint.getColor();
jvanverth0fedb192014-10-08 09:07:27 -0700426 if (fUseLCDText) {
427 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
joshualitt2e3b3e32014-12-09 13:31:14 -0800428 fCachedGeometryProcessor.reset(GrDistanceFieldLCDTextureEffect::Create(color,
joshualitt8059eb92014-12-29 15:10:07 -0800429 fViewMatrix,
joshualitt2e3b3e32014-12-09 13:31:14 -0800430 fCurrTexture,
jvanverth0fedb192014-10-08 09:07:27 -0700431 params,
432 fGammaTexture,
433 gammaParams,
434 colorNoPreMul,
435 flags));
436 } else {
joshualitt2dd1ae02014-12-03 06:24:10 -0800437 flags |= kColorAttr_DistanceFieldEffectFlag;
joshualitt56995b52014-12-11 15:44:02 -0800438 bool opaque = GrColorIsOpaque(color);
jvanverth0fedb192014-10-08 09:07:27 -0700439#ifdef SK_GAMMA_APPLY_TO_A8
reedb2d77e42014-10-14 08:26:33 -0700440 U8CPU lum = SkColorSpaceLuminance::computeLuminance(fDeviceProperties.gamma(),
jvanverth0fedb192014-10-08 09:07:27 -0700441 filteredColor);
joshualitt2e3b3e32014-12-09 13:31:14 -0800442 fCachedGeometryProcessor.reset(GrDistanceFieldTextureEffect::Create(color,
joshualitt8059eb92014-12-29 15:10:07 -0800443 fViewMatrix,
joshualitt2e3b3e32014-12-09 13:31:14 -0800444 fCurrTexture,
jvanverth0fedb192014-10-08 09:07:27 -0700445 params,
446 fGammaTexture,
447 gammaParams,
448 lum/255.f,
joshualitt56995b52014-12-11 15:44:02 -0800449 flags,
450 opaque));
jvanverth0fedb192014-10-08 09:07:27 -0700451#else
jvanverth5a105ff2015-02-18 11:36:35 -0800452 fCachedGeometryProcessor.reset(GrDistanceFieldTextureEffect::Create(color,
453 fViewMatrix,
454 fCurrTexture,
455 params,
456 flags,
457 opaque));
jvanverth0fedb192014-10-08 09:07:27 -0700458#endif
459 }
460 fEffectTextureUniqueID = textureUniqueID;
461 fEffectColor = filteredColor;
462 fEffectFlags = flags;
463 }
464
465}
466
jvanverth787cdf92014-12-04 10:46:50 -0800467inline bool GrDistanceFieldTextContext::uploadGlyph(GrGlyph* glyph, GrFontScaler* scaler) {
468 if (!fStrike->glyphTooLargeForAtlas(glyph)) {
469 if (fStrike->addGlyphToAtlas(glyph, scaler)) {
470 return true;
471 }
472
473 // try to clear out an unused plot before we flush
474 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
475 fStrike->addGlyphToAtlas(glyph, scaler)) {
476 return true;
477 }
478
479 if (c_DumpFontCache) {
480#ifdef SK_DEVELOPER
481 fContext->getFontCache()->dump();
482#endif
483 }
484
485 // before we purge the cache, we must flush any accumulated draws
486 this->flush();
487 fContext->flush();
488
489 // we should have an unused plot now
490 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
491 fStrike->addGlyphToAtlas(glyph, scaler)) {
492 return true;
493 }
494
495 // we should never get here
496 SkASSERT(false);
497 }
498
499 return false;
500}
501
502
jvanverthfca302c2014-10-20 13:12:54 -0700503// Returns true if this method handled the glyph, false if needs to be passed to fallback
504//
505bool GrDistanceFieldTextContext::appendGlyph(GrGlyph::PackedID packed,
djsollen058f01e2014-10-30 11:54:43 -0700506 SkScalar sx, SkScalar sy,
jvanverth0fedb192014-10-08 09:07:27 -0700507 GrFontScaler* scaler) {
508 if (NULL == fDrawTarget) {
jvanverthfca302c2014-10-20 13:12:54 -0700509 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700510 }
511
512 if (NULL == fStrike) {
513 fStrike = fContext->getFontCache()->getStrike(scaler, true);
514 }
515
516 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
517 if (NULL == glyph || glyph->fBounds.isEmpty()) {
jvanverthfca302c2014-10-20 13:12:54 -0700518 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700519 }
520
jvanverthfca302c2014-10-20 13:12:54 -0700521 // fallback to color glyph support
jvanverth294c3262014-10-10 11:36:12 -0700522 if (kA8_GrMaskFormat != glyph->fMaskFormat) {
jvanverthfca302c2014-10-20 13:12:54 -0700523 return false;
jvanverth294c3262014-10-10 11:36:12 -0700524 }
525
jvanverth2faa2282014-10-31 12:59:57 -0700526 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
527 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
528 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2*SK_DistanceFieldInset);
529 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2*SK_DistanceFieldInset);
jvanverth0fedb192014-10-08 09:07:27 -0700530
jvanverth2faa2282014-10-31 12:59:57 -0700531 SkScalar scale = fTextRatio;
532 dx *= scale;
533 dy *= scale;
534 sx += dx;
535 sy += dy;
536 width *= scale;
537 height *= scale;
538 SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height);
jvanverth0fedb192014-10-08 09:07:27 -0700539
540 // check if we clipped out
jvanverth2faa2282014-10-31 12:59:57 -0700541 SkRect dstRect;
joshualitt5531d512014-12-17 15:50:11 -0800542 const SkMatrix& ctm = fViewMatrix;
jvanverth2faa2282014-10-31 12:59:57 -0700543 (void) ctm.mapRect(&dstRect, glyphRect);
544 if (fClipRect.quickReject(SkScalarTruncToInt(dstRect.left()),
545 SkScalarTruncToInt(dstRect.top()),
546 SkScalarTruncToInt(dstRect.right()),
547 SkScalarTruncToInt(dstRect.bottom()))) {
jvanverth2faa2282014-10-31 12:59:57 -0700548 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700549 }
jvanverth2faa2282014-10-31 12:59:57 -0700550
jvanverth95f9b5f2014-12-05 07:56:53 -0800551 if (NULL == glyph->fPlot) {
552 // needs to be a separate conditional to avoid over-optimization
553 // on Nexus 7 and Nexus 10
joshualittc2625822014-12-18 16:40:54 -0800554
555 // If the glyph is too large we fall back to paths
jvanverth95f9b5f2014-12-05 07:56:53 -0800556 if (!uploadGlyph(glyph, scaler)) {
557 if (NULL == glyph->fPath) {
558 SkPath* path = SkNEW(SkPath);
559 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
560 // flag the glyph as being dead?
561 delete path;
562 return true;
563 }
564 glyph->fPath = path;
jvanverth0fedb192014-10-08 09:07:27 -0700565 }
jvanverth95f9b5f2014-12-05 07:56:53 -0800566
567 // flush any accumulated draws before drawing this glyph as a path.
568 this->flush();
569
jvanverth95f9b5f2014-12-05 07:56:53 -0800570 SkMatrix ctm;
571 ctm.setScale(fTextRatio, fTextRatio);
572 ctm.postTranslate(sx - dx, sy - dy);
joshualitt5531d512014-12-17 15:50:11 -0800573
joshualittc2625822014-12-18 16:40:54 -0800574 SkPath tmpPath(*glyph->fPath);
575 tmpPath.transform(ctm);
576
jvanverth95f9b5f2014-12-05 07:56:53 -0800577 GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle);
joshualitt570d2f82015-02-25 13:19:48 -0800578 fContext->drawPath(fRenderTarget, fClip, fPaint, fViewMatrix, tmpPath, strokeInfo);
jvanverth95f9b5f2014-12-05 07:56:53 -0800579
580 // remove this glyph from the vertices we need to allocate
581 fTotalVertexCount -= kVerticesPerGlyph;
582 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700583 }
jvanverth0fedb192014-10-08 09:07:27 -0700584 }
585
jvanverth0fedb192014-10-08 09:07:27 -0700586 SkASSERT(glyph->fPlot);
587 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
588 glyph->fPlot->setDrawToken(drawToken);
589
590 GrTexture* texture = glyph->fPlot->texture();
591 SkASSERT(texture);
592
jvanverth73f10532014-10-23 11:57:12 -0700593 if (fCurrTexture != texture || fCurrVertex + kVerticesPerGlyph > fTotalVertexCount) {
jvanverth0fedb192014-10-08 09:07:27 -0700594 this->flush();
595 fCurrTexture = texture;
596 fCurrTexture->ref();
597 }
598
599 bool useColorVerts = !fUseLCDText;
600
601 if (NULL == fVertices) {
jvanverth73f10532014-10-23 11:57:12 -0700602 int maxQuadVertices = kVerticesPerGlyph * fContext->getQuadIndexBuffer()->maxQuads();
603 fAllocVertexCount = SkMin32(fTotalVertexCount, maxQuadVertices);
joshualitt9853cce2014-11-17 14:22:48 -0800604 fVertices = alloc_vertices(fDrawTarget,
605 fAllocVertexCount,
606 useColorVerts);
jvanverth0fedb192014-10-08 09:07:27 -0700607 }
608
jvanverth2faa2282014-10-31 12:59:57 -0700609 fVertexBounds.joinNonEmptyArg(glyphRect);
jvanverth0fedb192014-10-08 09:07:27 -0700610
jvanverth5a105ff2015-02-18 11:36:35 -0800611 int u0 = glyph->fAtlasLocation.fX + SK_DistanceFieldInset;
612 int v0 = glyph->fAtlasLocation.fY + SK_DistanceFieldInset;
613 int u1 = u0 + glyph->fBounds.width() - 2*SK_DistanceFieldInset;
614 int v1 = v0 + glyph->fBounds.height() - 2*SK_DistanceFieldInset;
615
joshualitt9853cce2014-11-17 14:22:48 -0800616 size_t vertSize = get_vertex_stride(useColorVerts);
jvanverth5a105ff2015-02-18 11:36:35 -0800617 intptr_t vertex = reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVertex;
jvanverth0fedb192014-10-08 09:07:27 -0700618
jvanverth5a105ff2015-02-18 11:36:35 -0800619 // V0
620 SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
621 position->set(glyphRect.fLeft, glyphRect.fTop);
jvanverth059034d2015-02-17 08:39:56 -0800622 if (useColorVerts) {
jvanverth5a105ff2015-02-18 11:36:35 -0800623 SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
624 *color = fPaint.getColor();
jvanverth059034d2015-02-17 08:39:56 -0800625 }
jvanverth5a105ff2015-02-18 11:36:35 -0800626 SkIPoint16* textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize -
627 sizeof(SkIPoint16));
628 textureCoords->set(u0, v0);
629 vertex += vertSize;
630
631 // V1
632 position = reinterpret_cast<SkPoint*>(vertex);
633 position->set(glyphRect.fLeft, glyphRect.fBottom);
634 if (useColorVerts) {
635 SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
636 *color = fPaint.getColor();
637 }
638 textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize - sizeof(SkIPoint16));
639 textureCoords->set(u0, v1);
640 vertex += vertSize;
641
642 // V2
643 position = reinterpret_cast<SkPoint*>(vertex);
644 position->set(glyphRect.fRight, glyphRect.fBottom);
645 if (useColorVerts) {
646 SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
647 *color = fPaint.getColor();
648 }
649 textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize - sizeof(SkIPoint16));
650 textureCoords->set(u1, v1);
651 vertex += vertSize;
652
653 // V3
654 position = reinterpret_cast<SkPoint*>(vertex);
655 position->set(glyphRect.fRight, glyphRect.fTop);
656 if (useColorVerts) {
657 SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
658 *color = fPaint.getColor();
659 }
660 textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize - sizeof(SkIPoint16));
661 textureCoords->set(u1, v0);
jvanverth0fedb192014-10-08 09:07:27 -0700662
663 fCurrVertex += 4;
jvanverthfca302c2014-10-20 13:12:54 -0700664
665 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700666}
667
668void GrDistanceFieldTextContext::flush() {
669 if (NULL == fDrawTarget) {
670 return;
671 }
672
jvanverth0fedb192014-10-08 09:07:27 -0700673 if (fCurrVertex > 0) {
egdaniel8dd688b2015-01-22 10:16:09 -0800674 GrPipelineBuilder pipelineBuilder;
joshualitt44701df2015-02-23 14:44:57 -0800675 pipelineBuilder.setFromPaint(fPaint, fRenderTarget, fClip);
joshualitt780b11e2014-11-18 09:40:40 -0800676
jvanverth0fedb192014-10-08 09:07:27 -0700677 // setup our sampler state for our text texture/atlas
678 SkASSERT(SkIsAlign4(fCurrVertex));
679
680 // get our current color
681 SkColor filteredColor;
682 SkColorFilter* colorFilter = fSkPaint.getColorFilter();
683 if (colorFilter) {
684 filteredColor = colorFilter->filterColor(fSkPaint.getColor());
685 } else {
686 filteredColor = fSkPaint.getColor();
687 }
688 this->setupCoverageEffect(filteredColor);
689
jvanverth0fedb192014-10-08 09:07:27 -0700690 // Set draw state
691 if (fUseLCDText) {
egdaniel378092f2014-12-03 10:40:13 -0800692 // TODO: move supportsRGBCoverage check to setupCoverageEffect and only add LCD
693 // processor if the xp can support it. For now we will simply assume that if
694 // fUseLCDText is true, then we have a known color output.
egdaniel8dd688b2015-01-22 10:16:09 -0800695 const GrXPFactory* xpFactory = pipelineBuilder.getXPFactory();
696 if (!xpFactory->supportsRGBCoverage(0, kRGBA_GrColorComponentFlags)) {
tfarina38406c82014-10-31 07:11:12 -0700697 SkDebugf("LCD Text will not draw correctly.\n");
jvanverth0fedb192014-10-08 09:07:27 -0700698 }
joshualitt56995b52014-12-11 15:44:02 -0800699 SkASSERT(!fCachedGeometryProcessor->hasVertexColor());
jvanverth0fedb192014-10-08 09:07:27 -0700700 } else {
jvanverth0fedb192014-10-08 09:07:27 -0700701 // We're using per-vertex color.
joshualitt56995b52014-12-11 15:44:02 -0800702 SkASSERT(fCachedGeometryProcessor->hasVertexColor());
jvanverth0fedb192014-10-08 09:07:27 -0700703 }
jvanverth73f10532014-10-23 11:57:12 -0700704 int nGlyphs = fCurrVertex / kVerticesPerGlyph;
jvanverth0fedb192014-10-08 09:07:27 -0700705 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
egdaniel8dd688b2015-01-22 10:16:09 -0800706 fDrawTarget->drawIndexedInstances(&pipelineBuilder,
joshualitt56995b52014-12-11 15:44:02 -0800707 fCachedGeometryProcessor.get(),
joshualitt9853cce2014-11-17 14:22:48 -0800708 kTriangles_GrPrimitiveType,
jvanverth0fedb192014-10-08 09:07:27 -0700709 nGlyphs,
joshualitt9853cce2014-11-17 14:22:48 -0800710 kVerticesPerGlyph,
711 kIndicesPerGlyph,
712 &fVertexBounds);
jvanverth0fedb192014-10-08 09:07:27 -0700713 fDrawTarget->resetVertexSource();
714 fVertices = NULL;
jvanverth73f10532014-10-23 11:57:12 -0700715 fTotalVertexCount -= fCurrVertex;
jvanverth0fedb192014-10-08 09:07:27 -0700716 fCurrVertex = 0;
717 SkSafeSetNull(fCurrTexture);
718 fVertexBounds.setLargestInverted();
719 }
720}
721
722inline void GrDistanceFieldTextContext::finish() {
723 this->flush();
jvanverth73f10532014-10-23 11:57:12 -0700724 fTotalVertexCount = 0;
jvanverth0fedb192014-10-08 09:07:27 -0700725
726 GrTextContext::finish();
727}
728