blob: db98238a0ed70801bbd4e5aa161da9943f8f3ca9 [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;
jvanverthada68ef2014-11-03 14:00:24 -080037static const int kMediumDFFontSize = 78;
38static const int kMediumDFFontLimit = 78;
39static const int kLargeDFFontSize = 192;
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) {
jvanverth4736e142014-11-07 07:12:46 -080067#if SK_FORCE_DISTANCE_FIELD_TEXT
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() {
jvanverth2d2a68c2014-06-10 06:42:56 -070099 SkSafeSetNull(fGammaTexture);
jvanverth@google.comd830d132013-11-11 20:54:09 +0000100}
101
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000102bool GrDistanceFieldTextContext::canDraw(const SkPaint& paint) {
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +0000103 if (!fEnableDFRendering && !paint.isDistanceFieldTextTEMP()) {
commit-bot@chromium.orgeefd8a02014-04-08 20:14:32 +0000104 return false;
105 }
106
skia.committer@gmail.come1d94432014-04-09 03:04:11 +0000107 // rasterizers and mask filters modify alpha, which doesn't
commit-bot@chromium.orgeefd8a02014-04-08 20:14:32 +0000108 // translate well to distance
109 if (paint.getRasterizer() || paint.getMaskFilter() ||
110 !fContext->getTextTarget()->caps()->shaderDerivativeSupport()) {
111 return false;
112 }
113
114 // TODO: add some stroking support
115 if (paint.getStyle() != SkPaint::kFill_Style) {
116 return false;
117 }
118
119 // TODO: choose an appropriate maximum scale for distance fields and
120 // enable perspective
121 if (SkDraw::ShouldDrawTextAsPaths(paint, fContext->getMatrix())) {
122 return false;
123 }
124
jvanverth2faa2282014-10-31 12:59:57 -0700125 return true;
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000126}
127
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000128inline void GrDistanceFieldTextContext::init(const GrPaint& paint, const SkPaint& skPaint) {
129 GrTextContext::init(paint, skPaint);
130
131 fStrike = NULL;
132
jvanverth76ce81e2014-09-22 14:26:53 -0700133 const SkMatrix& ctm = fContext->getMatrix();
jvanverth9564ce62014-09-16 05:45:19 -0700134
135 // getMaxScale doesn't support perspective, so neither do we at the moment
jvanverth76ce81e2014-09-22 14:26:53 -0700136 SkASSERT(!ctm.hasPerspective());
137 SkScalar maxScale = ctm.getMaxScale();
jvanverth9564ce62014-09-16 05:45:19 -0700138 SkScalar textSize = fSkPaint.getTextSize();
jvanverth76ce81e2014-09-22 14:26:53 -0700139 SkScalar scaledTextSize = textSize;
140 // if we have non-unity scale, we need to choose our base text size
141 // based on the SkPaint's text size multiplied by the max scale factor
jvanverth9564ce62014-09-16 05:45:19 -0700142 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
143 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
jvanverth76ce81e2014-09-22 14:26:53 -0700144 scaledTextSize *= maxScale;
jvanverth9564ce62014-09-16 05:45:19 -0700145 }
146
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000147 fVertices = NULL;
jvanverth73f10532014-10-23 11:57:12 -0700148 fCurrVertex = 0;
149 fAllocVertexCount = 0;
150 fTotalVertexCount = 0;
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000151
jvanverth76ce81e2014-09-22 14:26:53 -0700152 if (scaledTextSize <= kSmallDFFontLimit) {
jvanverth9564ce62014-09-16 05:45:19 -0700153 fTextRatio = textSize / kSmallDFFontSize;
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000154 fSkPaint.setTextSize(SkIntToScalar(kSmallDFFontSize));
jvanverthada68ef2014-11-03 14:00:24 -0800155#if DEBUG_TEXT_SIZE
156 fSkPaint.setColor(SkColorSetARGB(0xFF, 0x00, 0x00, 0xFF));
157 fPaint.setColor(GrColorPackRGBA(0x00, 0x00, 0xFF, 0xFF));
158#endif
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));
jvanverthada68ef2014-11-03 14:00:24 -0800162#if DEBUG_TEXT_SIZE
163 fSkPaint.setColor(SkColorSetARGB(0xFF, 0x00, 0xFF, 0x00));
164 fPaint.setColor(GrColorPackRGBA(0x00, 0xFF, 0x00, 0xFF));
165#endif
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000166 } else {
jvanverth9564ce62014-09-16 05:45:19 -0700167 fTextRatio = textSize / kLargeDFFontSize;
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000168 fSkPaint.setTextSize(SkIntToScalar(kLargeDFFontSize));
jvanverthada68ef2014-11-03 14:00:24 -0800169#if DEBUG_TEXT_SIZE
170 fSkPaint.setColor(SkColorSetARGB(0xFF, 0xFF, 0x00, 0x00));
171 fPaint.setColor(GrColorPackRGBA(0xFF, 0x00, 0x00, 0xFF));
172#endif
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000173 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000174
commit-bot@chromium.org609ced42014-04-03 18:25:48 +0000175 fUseLCDText = fSkPaint.isLCDRenderText();
skia.committer@gmail.com221b9112014-04-04 03:04:32 +0000176
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000177 fSkPaint.setLCDRenderText(false);
178 fSkPaint.setAutohinted(false);
jvanverth2d2a68c2014-06-10 06:42:56 -0700179 fSkPaint.setHinting(SkPaint::kNormal_Hinting);
commit-bot@chromium.org0bed43c2014-03-14 21:22:38 +0000180 fSkPaint.setSubpixelText(true);
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000181}
182
jvanverth2d2a68c2014-06-10 06:42:56 -0700183static void setup_gamma_texture(GrContext* context, const SkGlyphCache* cache,
184 const SkDeviceProperties& deviceProperties,
185 GrTexture** gammaTexture) {
186 if (NULL == *gammaTexture) {
187 int width, height;
188 size_t size;
189
190#ifdef SK_GAMMA_CONTRAST
191 SkScalar contrast = SK_GAMMA_CONTRAST;
192#else
193 SkScalar contrast = 0.5f;
194#endif
reedb2d77e42014-10-14 08:26:33 -0700195 SkScalar paintGamma = deviceProperties.gamma();
196 SkScalar deviceGamma = deviceProperties.gamma();
jvanverth2d2a68c2014-06-10 06:42:56 -0700197
198 size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
199 &width, &height);
200
201 SkAutoTArray<uint8_t> data((int)size);
202 SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
203
204 // TODO: Update this to use the cache rather than directly creating a texture.
bsalomonf2703d82014-10-28 14:33:06 -0700205 GrSurfaceDesc desc;
206 desc.fFlags = kNone_GrSurfaceFlags;
jvanverth2d2a68c2014-06-10 06:42:56 -0700207 desc.fWidth = width;
208 desc.fHeight = height;
209 desc.fConfig = kAlpha_8_GrPixelConfig;
210
211 *gammaTexture = context->getGpu()->createTexture(desc, NULL, 0);
212 if (NULL == *gammaTexture) {
213 return;
214 }
215
bsalomon81beccc2014-10-13 12:32:55 -0700216 (*gammaTexture)->writePixels(0, 0, width, height,
217 (*gammaTexture)->config(), data.get(), 0,
218 GrContext::kDontFlush_PixelOpsFlag);
jvanverth2d2a68c2014-06-10 06:42:56 -0700219 }
220}
221
jvanverthaab626c2014-10-16 08:04:39 -0700222void GrDistanceFieldTextContext::onDrawText(const GrPaint& paint, const SkPaint& skPaint,
223 const char text[], size_t byteLength,
224 SkScalar x, SkScalar y) {
225 SkASSERT(byteLength == 0 || text != NULL);
226
jvanverth2b9dc1d2014-10-20 06:48:59 -0700227 // nothing to draw
228 if (text == NULL || byteLength == 0) {
jvanverthaab626c2014-10-16 08:04:39 -0700229 return;
230 }
231
jvanverth2b9dc1d2014-10-20 06:48:59 -0700232 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
233 SkAutoGlyphCache autoCache(skPaint, &fDeviceProperties, NULL);
234 SkGlyphCache* cache = autoCache.getCache();
jvanverthaab626c2014-10-16 08:04:39 -0700235
jvanverth2b9dc1d2014-10-20 06:48:59 -0700236 SkTArray<SkScalar> positions;
jvanverthaab626c2014-10-16 08:04:39 -0700237
jvanverth2b9dc1d2014-10-20 06:48:59 -0700238 const char* textPtr = text;
239 SkFixed stopX = 0;
240 SkFixed stopY = 0;
241 SkFixed origin;
242 switch (skPaint.getTextAlign()) {
243 case SkPaint::kRight_Align: origin = SK_Fixed1; break;
244 case SkPaint::kCenter_Align: origin = SK_FixedHalf; break;
245 case SkPaint::kLeft_Align: origin = 0; break;
246 default: SkFAIL("Invalid paint origin"); return;
247 }
jvanverthaab626c2014-10-16 08:04:39 -0700248
jvanverth2b9dc1d2014-10-20 06:48:59 -0700249 SkAutoKern autokern;
jvanverthaab626c2014-10-16 08:04:39 -0700250 const char* stop = text + byteLength;
jvanverth2b9dc1d2014-10-20 06:48:59 -0700251 while (textPtr < stop) {
252 // don't need x, y here, since all subpixel variants will have the
253 // same advance
254 const SkGlyph& glyph = glyphCacheProc(cache, &textPtr, 0, 0);
jvanverthaab626c2014-10-16 08:04:39 -0700255
jvanverth2b9dc1d2014-10-20 06:48:59 -0700256 SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph);
257 positions.push_back(SkFixedToScalar(stopX + SkFixedMul_portable(origin, width)));
jvanverthaab626c2014-10-16 08:04:39 -0700258
jvanverth2b9dc1d2014-10-20 06:48:59 -0700259 SkFixed height = glyph.fAdvanceY;
260 positions.push_back(SkFixedToScalar(stopY + SkFixedMul_portable(origin, height)));
jvanverthaab626c2014-10-16 08:04:39 -0700261
jvanverth2b9dc1d2014-10-20 06:48:59 -0700262 stopX += width;
263 stopY += height;
jvanverthaab626c2014-10-16 08:04:39 -0700264 }
jvanverth2b9dc1d2014-10-20 06:48:59 -0700265 SkASSERT(textPtr == stop);
jvanverthaab626c2014-10-16 08:04:39 -0700266
jvanverth2b9dc1d2014-10-20 06:48:59 -0700267 // now adjust starting point depending on alignment
268 SkScalar alignX = SkFixedToScalar(stopX);
269 SkScalar alignY = SkFixedToScalar(stopY);
270 if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
271 alignX = SkScalarHalf(alignX);
272 alignY = SkScalarHalf(alignY);
273 } else if (skPaint.getTextAlign() == SkPaint::kLeft_Align) {
274 alignX = 0;
275 alignY = 0;
jvanverthaab626c2014-10-16 08:04:39 -0700276 }
jvanverth2b9dc1d2014-10-20 06:48:59 -0700277 x -= alignX;
278 y -= alignY;
279 SkPoint offset = SkPoint::Make(x, y);
jvanverthaab626c2014-10-16 08:04:39 -0700280
jvanverth2b9dc1d2014-10-20 06:48:59 -0700281 this->drawPosText(paint, skPaint, text, byteLength, positions.begin(), 2, offset);
jvanverthaab626c2014-10-16 08:04:39 -0700282}
283
jvanverth8c27a182014-10-14 08:45:50 -0700284void GrDistanceFieldTextContext::onDrawPosText(const GrPaint& paint, const SkPaint& skPaint,
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000285 const char text[], size_t byteLength,
fmalita05c4a432014-09-29 06:29:53 -0700286 const SkScalar pos[], int scalarsPerPosition,
287 const SkPoint& offset) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000288
289 SkASSERT(byteLength == 0 || text != NULL);
290 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
291
292 // nothing to draw
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000293 if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000294 return;
295 }
296
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000297 this->init(paint, skPaint);
298
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000299 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
300
jvanverth2d2a68c2014-06-10 06:42:56 -0700301 SkAutoGlyphCacheNoGamma autoCache(fSkPaint, &fDeviceProperties, NULL);
302 SkGlyphCache* cache = autoCache.getCache();
303 GrFontScaler* fontScaler = GetGrFontScaler(cache);
304
305 setup_gamma_texture(fContext, cache, fDeviceProperties, &fGammaTexture);
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000306
jvanverth73f10532014-10-23 11:57:12 -0700307 int numGlyphs = fSkPaint.textToGlyphs(text, byteLength, NULL);
308 fTotalVertexCount = kVerticesPerGlyph*numGlyphs;
309
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000310 const char* stop = text + byteLength;
jvanverthfca302c2014-10-20 13:12:54 -0700311 SkTArray<char> fallbackTxt;
312 SkTArray<SkScalar> fallbackPos;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000313
314 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
315 while (text < stop) {
jvanverthfca302c2014-10-20 13:12:54 -0700316 const char* lastText = text;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000317 // the last 2 parameters are ignored
318 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
319
320 if (glyph.fWidth) {
fmalita05c4a432014-09-29 06:29:53 -0700321 SkScalar x = offset.x() + pos[0];
322 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000323
jvanverthfca302c2014-10-20 13:12:54 -0700324 if (!this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
325 glyph.getSubXFixed(),
326 glyph.getSubYFixed()),
djsollen058f01e2014-10-30 11:54:43 -0700327 x, y, fontScaler)) {
jvanverthfca302c2014-10-20 13:12:54 -0700328 // couldn't append, send to fallback
329 fallbackTxt.push_back_n(text-lastText, lastText);
330 fallbackPos.push_back(pos[0]);
331 if (2 == scalarsPerPosition) {
332 fallbackPos.push_back(pos[1]);
333 }
334 }
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000335 }
336 pos += scalarsPerPosition;
337 }
338 } else {
jvanverth2b9dc1d2014-10-20 06:48:59 -0700339 SkScalar alignMul = SkPaint::kCenter_Align == fSkPaint.getTextAlign() ? SK_ScalarHalf
340 : SK_Scalar1;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000341 while (text < stop) {
jvanverthfca302c2014-10-20 13:12:54 -0700342 const char* lastText = text;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000343 // the last 2 parameters are ignored
344 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
345
346 if (glyph.fWidth) {
fmalita05c4a432014-09-29 06:29:53 -0700347 SkScalar x = offset.x() + pos[0];
348 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
skia.committer@gmail.com22e96722013-12-20 07:01:36 +0000349
jvanverth2b9dc1d2014-10-20 06:48:59 -0700350 SkScalar advanceX = SkFixedToScalar(glyph.fAdvanceX)*alignMul*fTextRatio;
351 SkScalar advanceY = SkFixedToScalar(glyph.fAdvanceY)*alignMul*fTextRatio;
352
jvanverthfca302c2014-10-20 13:12:54 -0700353 if (!this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
354 glyph.getSubXFixed(),
355 glyph.getSubYFixed()),
djsollen058f01e2014-10-30 11:54:43 -0700356 x - advanceX, y - advanceY, fontScaler)) {
jvanverthfca302c2014-10-20 13:12:54 -0700357 // couldn't append, send to fallback
358 fallbackTxt.push_back_n(text-lastText, lastText);
359 fallbackPos.push_back(pos[0]);
360 if (2 == scalarsPerPosition) {
361 fallbackPos.push_back(pos[1]);
362 }
363 }
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000364 }
365 pos += scalarsPerPosition;
366 }
367 }
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000368
369 this->finish();
jvanverthfca302c2014-10-20 13:12:54 -0700370
371 if (fallbackTxt.count() > 0) {
372 fFallbackTextContext->drawPosText(paint, skPaint, fallbackTxt.begin(), fallbackTxt.count(),
373 fallbackPos.begin(), scalarsPerPosition, offset);
374 }
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000375}
jvanverth0fedb192014-10-08 09:07:27 -0700376
377static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
378 unsigned r = SkColorGetR(c);
379 unsigned g = SkColorGetG(c);
380 unsigned b = SkColorGetB(c);
381 return GrColorPackRGBA(r, g, b, 0xff);
382}
383
joshualitt9853cce2014-11-17 14:22:48 -0800384static size_t get_vertex_stride(bool useColorVerts) {
385 return useColorVerts ? (2 * sizeof(SkPoint) + sizeof(GrColor)) :
386 (2 * sizeof(SkPoint));
387}
388
389static void* alloc_vertices(GrDrawTarget* drawTarget,
390 int numVertices,
391 bool useColorVerts) {
jvanverth73f10532014-10-23 11:57:12 -0700392 if (numVertices <= 0) {
393 return NULL;
394 }
395
jvanverth73f10532014-10-23 11:57:12 -0700396 void* vertices = NULL;
397 bool success = drawTarget->reserveVertexAndIndexSpace(numVertices,
joshualitt9853cce2014-11-17 14:22:48 -0800398 get_vertex_stride(useColorVerts),
jvanverth73f10532014-10-23 11:57:12 -0700399 0,
400 &vertices,
401 NULL);
402 GrAlwaysAssert(success);
403 return vertices;
404}
405
jvanverth0fedb192014-10-08 09:07:27 -0700406void GrDistanceFieldTextContext::setupCoverageEffect(const SkColor& filteredColor) {
407 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode);
408 GrTextureParams gammaParams(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
409
410 uint32_t textureUniqueID = fCurrTexture->getUniqueID();
411 const SkMatrix& ctm = fContext->getMatrix();
412
413 // set up any flags
414 uint32_t flags = 0;
415 flags |= ctm.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
416 flags |= fUseLCDText ? kUseLCD_DistanceFieldEffectFlag : 0;
417 flags |= fUseLCDText && ctm.rectStaysRect() ?
418 kRectToRect_DistanceFieldEffectFlag : 0;
reedb2d77e42014-10-14 08:26:33 -0700419 bool useBGR = SkPixelGeometryIsBGR(fDeviceProperties.pixelGeometry());
jvanverth0fedb192014-10-08 09:07:27 -0700420 flags |= fUseLCDText && useBGR ? kBGR_DistanceFieldEffectFlag : 0;
421
422 // see if we need to create a new effect
423 if (textureUniqueID != fEffectTextureUniqueID ||
424 filteredColor != fEffectColor ||
425 flags != fEffectFlags) {
426 if (fUseLCDText) {
427 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
428 fCachedGeometryProcessor.reset(GrDistanceFieldLCDTextureEffect::Create(fCurrTexture,
429 params,
430 fGammaTexture,
431 gammaParams,
432 colorNoPreMul,
433 flags));
434 } else {
435#ifdef SK_GAMMA_APPLY_TO_A8
reedb2d77e42014-10-14 08:26:33 -0700436 U8CPU lum = SkColorSpaceLuminance::computeLuminance(fDeviceProperties.gamma(),
jvanverth0fedb192014-10-08 09:07:27 -0700437 filteredColor);
438 fCachedGeometryProcessor.reset(GrDistanceFieldTextureEffect::Create(fCurrTexture,
439 params,
440 fGammaTexture,
441 gammaParams,
442 lum/255.f,
443 flags));
444#else
jvanverth2faa2282014-10-31 12:59:57 -0700445 fCachedGeometryProcessor.reset(GrDistanceFieldNoGammaTextureEffect::Create(fCurrTexture,
jvanverth0fedb192014-10-08 09:07:27 -0700446 params, flags));
447#endif
448 }
449 fEffectTextureUniqueID = textureUniqueID;
450 fEffectColor = filteredColor;
451 fEffectFlags = flags;
452 }
453
454}
455
jvanverthfca302c2014-10-20 13:12:54 -0700456// Returns true if this method handled the glyph, false if needs to be passed to fallback
457//
458bool GrDistanceFieldTextContext::appendGlyph(GrGlyph::PackedID packed,
djsollen058f01e2014-10-30 11:54:43 -0700459 SkScalar sx, SkScalar sy,
jvanverth0fedb192014-10-08 09:07:27 -0700460 GrFontScaler* scaler) {
461 if (NULL == fDrawTarget) {
jvanverthfca302c2014-10-20 13:12:54 -0700462 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700463 }
464
465 if (NULL == fStrike) {
466 fStrike = fContext->getFontCache()->getStrike(scaler, true);
467 }
468
469 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
470 if (NULL == glyph || glyph->fBounds.isEmpty()) {
jvanverthfca302c2014-10-20 13:12:54 -0700471 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700472 }
473
jvanverthfca302c2014-10-20 13:12:54 -0700474 // fallback to color glyph support
jvanverth294c3262014-10-10 11:36:12 -0700475 if (kA8_GrMaskFormat != glyph->fMaskFormat) {
jvanverthfca302c2014-10-20 13:12:54 -0700476 return false;
jvanverth294c3262014-10-10 11:36:12 -0700477 }
478
jvanverth2faa2282014-10-31 12:59:57 -0700479 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
480 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
481 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2*SK_DistanceFieldInset);
482 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2*SK_DistanceFieldInset);
jvanverth0fedb192014-10-08 09:07:27 -0700483
jvanverth2faa2282014-10-31 12:59:57 -0700484 SkScalar scale = fTextRatio;
485 dx *= scale;
486 dy *= scale;
487 sx += dx;
488 sy += dy;
489 width *= scale;
490 height *= scale;
491 SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height);
jvanverth0fedb192014-10-08 09:07:27 -0700492
493 // check if we clipped out
jvanverth2faa2282014-10-31 12:59:57 -0700494 SkRect dstRect;
495 const SkMatrix& ctm = fContext->getMatrix();
496 (void) ctm.mapRect(&dstRect, glyphRect);
497 if (fClipRect.quickReject(SkScalarTruncToInt(dstRect.left()),
498 SkScalarTruncToInt(dstRect.top()),
499 SkScalarTruncToInt(dstRect.right()),
500 SkScalarTruncToInt(dstRect.bottom()))) {
jvanverth0fedb192014-10-08 09:07:27 -0700501// SkCLZ(3); // so we can set a break-point in the debugger
jvanverth2faa2282014-10-31 12:59:57 -0700502 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700503 }
jvanverth2faa2282014-10-31 12:59:57 -0700504
jvanverth0fedb192014-10-08 09:07:27 -0700505 if (NULL == glyph->fPlot) {
506 if (!fStrike->glyphTooLargeForAtlas(glyph)) {
507 if (fStrike->addGlyphToAtlas(glyph, scaler)) {
508 goto HAS_ATLAS;
509 }
510
511 // try to clear out an unused plot before we flush
jvanverth294c3262014-10-10 11:36:12 -0700512 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
jvanverth0fedb192014-10-08 09:07:27 -0700513 fStrike->addGlyphToAtlas(glyph, scaler)) {
514 goto HAS_ATLAS;
515 }
516
517 if (c_DumpFontCache) {
518#ifdef SK_DEVELOPER
519 fContext->getFontCache()->dump();
520#endif
521 }
522
523 // before we purge the cache, we must flush any accumulated draws
524 this->flush();
525 fContext->flush();
526
527 // we should have an unused plot now
jvanverth294c3262014-10-10 11:36:12 -0700528 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
jvanverth0fedb192014-10-08 09:07:27 -0700529 fStrike->addGlyphToAtlas(glyph, scaler)) {
530 goto HAS_ATLAS;
531 }
532 }
533
534 if (NULL == glyph->fPath) {
535 SkPath* path = SkNEW(SkPath);
536 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
537 // flag the glyph as being dead?
538 delete path;
jvanverthfca302c2014-10-20 13:12:54 -0700539 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700540 }
541 glyph->fPath = path;
542 }
543
bsalomonec87dc62014-10-14 10:52:00 -0700544 // flush any accumulated draws before drawing this glyph as a path.
545 this->flush();
546
jvanverth0fedb192014-10-08 09:07:27 -0700547 GrContext::AutoMatrix am;
548 SkMatrix ctm;
549 ctm.setScale(fTextRatio, fTextRatio);
jvanverth0e66aaa2014-11-04 13:32:53 -0800550 ctm.postTranslate(sx - dx, sy - dy);
jvanverth0fedb192014-10-08 09:07:27 -0700551 GrPaint tmpPaint(fPaint);
552 am.setPreConcat(fContext, ctm, &tmpPaint);
553 GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle);
554 fContext->drawPath(tmpPaint, *glyph->fPath, strokeInfo);
jvanverth73f10532014-10-23 11:57:12 -0700555
556 // remove this glyph from the vertices we need to allocate
557 fTotalVertexCount -= kVerticesPerGlyph;
jvanverthfca302c2014-10-20 13:12:54 -0700558 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700559 }
560
561HAS_ATLAS:
562 SkASSERT(glyph->fPlot);
563 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
564 glyph->fPlot->setDrawToken(drawToken);
565
566 GrTexture* texture = glyph->fPlot->texture();
567 SkASSERT(texture);
568
jvanverth73f10532014-10-23 11:57:12 -0700569 if (fCurrTexture != texture || fCurrVertex + kVerticesPerGlyph > fTotalVertexCount) {
jvanverth0fedb192014-10-08 09:07:27 -0700570 this->flush();
571 fCurrTexture = texture;
572 fCurrTexture->ref();
573 }
574
575 bool useColorVerts = !fUseLCDText;
576
577 if (NULL == fVertices) {
jvanverth73f10532014-10-23 11:57:12 -0700578 int maxQuadVertices = kVerticesPerGlyph * fContext->getQuadIndexBuffer()->maxQuads();
579 fAllocVertexCount = SkMin32(fTotalVertexCount, maxQuadVertices);
joshualitt9853cce2014-11-17 14:22:48 -0800580 fVertices = alloc_vertices(fDrawTarget,
581 fAllocVertexCount,
582 useColorVerts);
jvanverth0fedb192014-10-08 09:07:27 -0700583 }
584
jvanverth0fedb192014-10-08 09:07:27 -0700585 SkFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX + SK_DistanceFieldInset);
586 SkFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY + SK_DistanceFieldInset);
587 SkFixed tw = SkIntToFixed(glyph->fBounds.width() - 2*SK_DistanceFieldInset);
588 SkFixed th = SkIntToFixed(glyph->fBounds.height() - 2*SK_DistanceFieldInset);
589
jvanverth2faa2282014-10-31 12:59:57 -0700590 fVertexBounds.joinNonEmptyArg(glyphRect);
jvanverth0fedb192014-10-08 09:07:27 -0700591
joshualitt9853cce2014-11-17 14:22:48 -0800592 size_t vertSize = get_vertex_stride(useColorVerts);
jvanverth0fedb192014-10-08 09:07:27 -0700593
594 SkPoint* positions = reinterpret_cast<SkPoint*>(
595 reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVertex);
jvanverth2faa2282014-10-31 12:59:57 -0700596 positions->setRectFan(glyphRect.fLeft, glyphRect.fTop, glyphRect.fRight, glyphRect.fBottom,
597 vertSize);
jvanverth0fedb192014-10-08 09:07:27 -0700598
599 // The texture coords are last in both the with and without color vertex layouts.
600 SkPoint* textureCoords = reinterpret_cast<SkPoint*>(
601 reinterpret_cast<intptr_t>(positions) + vertSize - sizeof(SkPoint));
602 textureCoords->setRectFan(SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx)),
603 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty)),
604 SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx + tw)),
605 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty + th)),
606 vertSize);
607 if (useColorVerts) {
jvanverth0fedb192014-10-08 09:07:27 -0700608 // color comes after position.
609 GrColor* colors = reinterpret_cast<GrColor*>(positions + 1);
610 for (int i = 0; i < 4; ++i) {
611 *colors = fPaint.getColor();
612 colors = reinterpret_cast<GrColor*>(reinterpret_cast<intptr_t>(colors) + vertSize);
613 }
614 }
615
616 fCurrVertex += 4;
jvanverthfca302c2014-10-20 13:12:54 -0700617
618 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700619}
620
joshualitt9853cce2014-11-17 14:22:48 -0800621// We use color vertices if we aren't drawing LCD text
622static void set_vertex_attributes(GrDrawState* drawState, bool useColorVerts) {
623 // set up attributes
624 if (useColorVerts) {
625 drawState->setVertexAttribs<gTextVertexWithColorAttribs>(
626 SK_ARRAY_COUNT(gTextVertexWithColorAttribs), kTextVAColorSize);
627 } else {
628 drawState->setVertexAttribs<gTextVertexAttribs>(
629 SK_ARRAY_COUNT(gTextVertexAttribs), kTextVASize);
630 }
631}
632
jvanverth0fedb192014-10-08 09:07:27 -0700633void GrDistanceFieldTextContext::flush() {
634 if (NULL == fDrawTarget) {
635 return;
636 }
637
joshualitt9853cce2014-11-17 14:22:48 -0800638 GrDrawState drawState;
639 drawState.setFromPaint(fPaint, fContext->getMatrix(), fContext->getRenderTarget());
640 bool useColorVerts = !fUseLCDText;
641 set_vertex_attributes(&drawState, useColorVerts);
jvanverth0fedb192014-10-08 09:07:27 -0700642
643 if (fCurrVertex > 0) {
644 // setup our sampler state for our text texture/atlas
645 SkASSERT(SkIsAlign4(fCurrVertex));
646
647 // get our current color
648 SkColor filteredColor;
649 SkColorFilter* colorFilter = fSkPaint.getColorFilter();
650 if (colorFilter) {
651 filteredColor = colorFilter->filterColor(fSkPaint.getColor());
652 } else {
653 filteredColor = fSkPaint.getColor();
654 }
655 this->setupCoverageEffect(filteredColor);
656
657 // Effects could be stored with one of the cache objects (atlas?)
joshualitt9853cce2014-11-17 14:22:48 -0800658 drawState.setGeometryProcessor(fCachedGeometryProcessor.get());
jvanverth0fedb192014-10-08 09:07:27 -0700659
660 // Set draw state
661 if (fUseLCDText) {
662 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
663 if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
664 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
665 fPaint.numColorStages()) {
tfarina38406c82014-10-31 07:11:12 -0700666 SkDebugf("LCD Text will not draw correctly.\n");
jvanverth0fedb192014-10-08 09:07:27 -0700667 }
joshualitt9853cce2014-11-17 14:22:48 -0800668 SkASSERT(!drawState.hasColorVertexAttribute());
jvanverth0fedb192014-10-08 09:07:27 -0700669 // We don't use the GrPaint's color in this case because it's been premultiplied by
670 // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by
671 // the mask texture color. The end result is that we get
672 // mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor
673 int a = SkColorGetA(fSkPaint.getColor());
674 // paintAlpha
joshualitt9853cce2014-11-17 14:22:48 -0800675 drawState.setColor(SkColorSetARGB(a, a, a, a));
jvanverth0fedb192014-10-08 09:07:27 -0700676 // paintColor
joshualitt9853cce2014-11-17 14:22:48 -0800677 drawState.setBlendConstant(colorNoPreMul);
678 drawState.setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
jvanverth0fedb192014-10-08 09:07:27 -0700679 } else {
joshualitt9853cce2014-11-17 14:22:48 -0800680 if (0xFF == GrColorUnpackA(fPaint.getColor())) {
681 drawState.setHint(GrDrawState::kVertexColorsAreOpaque_Hint, true);
682 }
jvanverth0fedb192014-10-08 09:07:27 -0700683 // set back to normal in case we took LCD path previously.
joshualitt9853cce2014-11-17 14:22:48 -0800684 drawState.setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
jvanverth0fedb192014-10-08 09:07:27 -0700685 // We're using per-vertex color.
joshualitt9853cce2014-11-17 14:22:48 -0800686 SkASSERT(drawState.hasColorVertexAttribute());
jvanverth0fedb192014-10-08 09:07:27 -0700687 }
jvanverth73f10532014-10-23 11:57:12 -0700688 int nGlyphs = fCurrVertex / kVerticesPerGlyph;
jvanverth0fedb192014-10-08 09:07:27 -0700689 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
joshualitt9853cce2014-11-17 14:22:48 -0800690 fDrawTarget->drawIndexedInstances(&drawState,
691 kTriangles_GrPrimitiveType,
jvanverth0fedb192014-10-08 09:07:27 -0700692 nGlyphs,
joshualitt9853cce2014-11-17 14:22:48 -0800693 kVerticesPerGlyph,
694 kIndicesPerGlyph,
695 &fVertexBounds);
jvanverth0fedb192014-10-08 09:07:27 -0700696 fDrawTarget->resetVertexSource();
697 fVertices = NULL;
jvanverth73f10532014-10-23 11:57:12 -0700698 fTotalVertexCount -= fCurrVertex;
jvanverth0fedb192014-10-08 09:07:27 -0700699 fCurrVertex = 0;
700 SkSafeSetNull(fCurrTexture);
701 fVertexBounds.setLargestInverted();
702 }
703}
704
705inline void GrDistanceFieldTextContext::finish() {
706 this->flush();
jvanverth73f10532014-10-23 11:57:12 -0700707 fTotalVertexCount = 0;
jvanverth0fedb192014-10-08 09:07:27 -0700708
709 GrTextContext::finish();
710}
711