blob: 99aed573b609e78462349eb86f24764eba38cb95 [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
egdaniel7b3d5ee2014-08-28 05:41:14 -070041static const size_t kTextVASize = 2 * sizeof(SkPoint);
egdaniel7b3d5ee2014-08-28 05:41:14 -070042static const size_t kTextVAColorSize = 2 * sizeof(SkPoint) + sizeof(GrColor);
43
jvanverth73f10532014-10-23 11:57:12 -070044static const int kVerticesPerGlyph = 4;
45static const int kIndicesPerGlyph = 6;
jvanverth@google.comd830d132013-11-11 20:54:09 +000046
skia.committer@gmail.come5d70152014-01-29 07:01:48 +000047GrDistanceFieldTextContext::GrDistanceFieldTextContext(GrContext* context,
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000048 const SkDeviceProperties& properties,
49 bool enable)
Mike Klein6a25bd02014-08-29 10:03:59 -040050 : GrTextContext(context, properties) {
jvanverth4736e142014-11-07 07:12:46 -080051#if SK_FORCE_DISTANCE_FIELD_TEXT
Mike Klein6a25bd02014-08-29 10:03:59 -040052 fEnableDFRendering = true;
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000053#else
Mike Klein6a25bd02014-08-29 10:03:59 -040054 fEnableDFRendering = enable;
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000055#endif
Mike Klein6a25bd02014-08-29 10:03:59 -040056 fStrike = NULL;
57 fGammaTexture = NULL;
58
Mike Klein6a25bd02014-08-29 10:03:59 -040059 fEffectTextureUniqueID = SK_InvalidUniqueID;
60 fEffectColor = GrColor_ILLEGAL;
jvanverth6d22eca2014-10-28 11:10:48 -070061 fEffectFlags = kInvalid_DistanceFieldEffectFlag;
Mike Klein6a25bd02014-08-29 10:03:59 -040062
63 fVertices = NULL;
jvanverth73f10532014-10-23 11:57:12 -070064 fCurrVertex = 0;
65 fAllocVertexCount = 0;
66 fTotalVertexCount = 0;
67 fCurrTexture = NULL;
Mike Klein6a25bd02014-08-29 10:03:59 -040068
jvanverth1723bfc2014-07-30 09:16:33 -070069 fVertexBounds.setLargestInverted();
jvanverth@google.comd830d132013-11-11 20:54:09 +000070}
71
jvanverth8c27a182014-10-14 08:45:50 -070072GrDistanceFieldTextContext* GrDistanceFieldTextContext::Create(GrContext* context,
73 const SkDeviceProperties& props,
74 bool enable) {
75 GrDistanceFieldTextContext* textContext = SkNEW_ARGS(GrDistanceFieldTextContext,
76 (context, props, enable));
77 textContext->fFallbackTextContext = GrBitmapTextContext::Create(context, props);
78
79 return textContext;
80}
81
jvanverth@google.comd830d132013-11-11 20:54:09 +000082GrDistanceFieldTextContext::~GrDistanceFieldTextContext() {
jvanverth2d2a68c2014-06-10 06:42:56 -070083 SkSafeSetNull(fGammaTexture);
jvanverth@google.comd830d132013-11-11 20:54:09 +000084}
85
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000086bool GrDistanceFieldTextContext::canDraw(const SkPaint& paint) {
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000087 if (!fEnableDFRendering && !paint.isDistanceFieldTextTEMP()) {
commit-bot@chromium.orgeefd8a02014-04-08 20:14:32 +000088 return false;
89 }
90
skia.committer@gmail.come1d94432014-04-09 03:04:11 +000091 // rasterizers and mask filters modify alpha, which doesn't
commit-bot@chromium.orgeefd8a02014-04-08 20:14:32 +000092 // translate well to distance
93 if (paint.getRasterizer() || paint.getMaskFilter() ||
94 !fContext->getTextTarget()->caps()->shaderDerivativeSupport()) {
95 return false;
96 }
97
98 // TODO: add some stroking support
99 if (paint.getStyle() != SkPaint::kFill_Style) {
100 return false;
101 }
102
103 // TODO: choose an appropriate maximum scale for distance fields and
104 // enable perspective
105 if (SkDraw::ShouldDrawTextAsPaths(paint, fContext->getMatrix())) {
106 return false;
107 }
108
jvanverth2faa2282014-10-31 12:59:57 -0700109 return true;
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000110}
111
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000112inline void GrDistanceFieldTextContext::init(const GrPaint& paint, const SkPaint& skPaint) {
113 GrTextContext::init(paint, skPaint);
114
115 fStrike = NULL;
116
jvanverth76ce81e2014-09-22 14:26:53 -0700117 const SkMatrix& ctm = fContext->getMatrix();
jvanverth9564ce62014-09-16 05:45:19 -0700118
119 // getMaxScale doesn't support perspective, so neither do we at the moment
jvanverth76ce81e2014-09-22 14:26:53 -0700120 SkASSERT(!ctm.hasPerspective());
121 SkScalar maxScale = ctm.getMaxScale();
jvanverth9564ce62014-09-16 05:45:19 -0700122 SkScalar textSize = fSkPaint.getTextSize();
jvanverth76ce81e2014-09-22 14:26:53 -0700123 SkScalar scaledTextSize = textSize;
124 // if we have non-unity scale, we need to choose our base text size
125 // based on the SkPaint's text size multiplied by the max scale factor
jvanverth9564ce62014-09-16 05:45:19 -0700126 // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)?
127 if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) {
jvanverth76ce81e2014-09-22 14:26:53 -0700128 scaledTextSize *= maxScale;
jvanverth9564ce62014-09-16 05:45:19 -0700129 }
130
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000131 fVertices = NULL;
jvanverth73f10532014-10-23 11:57:12 -0700132 fCurrVertex = 0;
133 fAllocVertexCount = 0;
134 fTotalVertexCount = 0;
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000135
jvanverth76ce81e2014-09-22 14:26:53 -0700136 if (scaledTextSize <= kSmallDFFontLimit) {
jvanverth9564ce62014-09-16 05:45:19 -0700137 fTextRatio = textSize / kSmallDFFontSize;
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000138 fSkPaint.setTextSize(SkIntToScalar(kSmallDFFontSize));
jvanverthada68ef2014-11-03 14:00:24 -0800139#if DEBUG_TEXT_SIZE
140 fSkPaint.setColor(SkColorSetARGB(0xFF, 0x00, 0x00, 0xFF));
141 fPaint.setColor(GrColorPackRGBA(0x00, 0x00, 0xFF, 0xFF));
142#endif
jvanverth76ce81e2014-09-22 14:26:53 -0700143 } else if (scaledTextSize <= kMediumDFFontLimit) {
jvanverth9564ce62014-09-16 05:45:19 -0700144 fTextRatio = textSize / kMediumDFFontSize;
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000145 fSkPaint.setTextSize(SkIntToScalar(kMediumDFFontSize));
jvanverthada68ef2014-11-03 14:00:24 -0800146#if DEBUG_TEXT_SIZE
147 fSkPaint.setColor(SkColorSetARGB(0xFF, 0x00, 0xFF, 0x00));
148 fPaint.setColor(GrColorPackRGBA(0x00, 0xFF, 0x00, 0xFF));
149#endif
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000150 } else {
jvanverth9564ce62014-09-16 05:45:19 -0700151 fTextRatio = textSize / kLargeDFFontSize;
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000152 fSkPaint.setTextSize(SkIntToScalar(kLargeDFFontSize));
jvanverthada68ef2014-11-03 14:00:24 -0800153#if DEBUG_TEXT_SIZE
154 fSkPaint.setColor(SkColorSetARGB(0xFF, 0xFF, 0x00, 0x00));
155 fPaint.setColor(GrColorPackRGBA(0xFF, 0x00, 0x00, 0xFF));
156#endif
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000157 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000158
commit-bot@chromium.org609ced42014-04-03 18:25:48 +0000159 fUseLCDText = fSkPaint.isLCDRenderText();
skia.committer@gmail.com221b9112014-04-04 03:04:32 +0000160
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000161 fSkPaint.setLCDRenderText(false);
162 fSkPaint.setAutohinted(false);
jvanverth2d2a68c2014-06-10 06:42:56 -0700163 fSkPaint.setHinting(SkPaint::kNormal_Hinting);
commit-bot@chromium.org0bed43c2014-03-14 21:22:38 +0000164 fSkPaint.setSubpixelText(true);
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000165}
166
jvanverth2d2a68c2014-06-10 06:42:56 -0700167static void setup_gamma_texture(GrContext* context, const SkGlyphCache* cache,
168 const SkDeviceProperties& deviceProperties,
169 GrTexture** gammaTexture) {
170 if (NULL == *gammaTexture) {
171 int width, height;
172 size_t size;
173
174#ifdef SK_GAMMA_CONTRAST
175 SkScalar contrast = SK_GAMMA_CONTRAST;
176#else
177 SkScalar contrast = 0.5f;
178#endif
reedb2d77e42014-10-14 08:26:33 -0700179 SkScalar paintGamma = deviceProperties.gamma();
180 SkScalar deviceGamma = deviceProperties.gamma();
jvanverth2d2a68c2014-06-10 06:42:56 -0700181
182 size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
183 &width, &height);
184
185 SkAutoTArray<uint8_t> data((int)size);
186 SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
187
188 // TODO: Update this to use the cache rather than directly creating a texture.
bsalomonf2703d82014-10-28 14:33:06 -0700189 GrSurfaceDesc desc;
190 desc.fFlags = kNone_GrSurfaceFlags;
jvanverth2d2a68c2014-06-10 06:42:56 -0700191 desc.fWidth = width;
192 desc.fHeight = height;
193 desc.fConfig = kAlpha_8_GrPixelConfig;
194
195 *gammaTexture = context->getGpu()->createTexture(desc, NULL, 0);
196 if (NULL == *gammaTexture) {
197 return;
198 }
199
bsalomon81beccc2014-10-13 12:32:55 -0700200 (*gammaTexture)->writePixels(0, 0, width, height,
201 (*gammaTexture)->config(), data.get(), 0,
202 GrContext::kDontFlush_PixelOpsFlag);
jvanverth2d2a68c2014-06-10 06:42:56 -0700203 }
204}
205
jvanverthaab626c2014-10-16 08:04:39 -0700206void GrDistanceFieldTextContext::onDrawText(const GrPaint& paint, const SkPaint& skPaint,
207 const char text[], size_t byteLength,
208 SkScalar x, SkScalar y) {
209 SkASSERT(byteLength == 0 || text != NULL);
210
jvanverth2b9dc1d2014-10-20 06:48:59 -0700211 // nothing to draw
212 if (text == NULL || byteLength == 0) {
jvanverthaab626c2014-10-16 08:04:39 -0700213 return;
214 }
215
jvanverth2b9dc1d2014-10-20 06:48:59 -0700216 SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
217 SkAutoGlyphCache autoCache(skPaint, &fDeviceProperties, NULL);
218 SkGlyphCache* cache = autoCache.getCache();
jvanverthaab626c2014-10-16 08:04:39 -0700219
jvanverth2b9dc1d2014-10-20 06:48:59 -0700220 SkTArray<SkScalar> positions;
jvanverthaab626c2014-10-16 08:04:39 -0700221
jvanverth2b9dc1d2014-10-20 06:48:59 -0700222 const char* textPtr = text;
223 SkFixed stopX = 0;
224 SkFixed stopY = 0;
225 SkFixed origin;
226 switch (skPaint.getTextAlign()) {
227 case SkPaint::kRight_Align: origin = SK_Fixed1; break;
228 case SkPaint::kCenter_Align: origin = SK_FixedHalf; break;
229 case SkPaint::kLeft_Align: origin = 0; break;
230 default: SkFAIL("Invalid paint origin"); return;
231 }
jvanverthaab626c2014-10-16 08:04:39 -0700232
jvanverth2b9dc1d2014-10-20 06:48:59 -0700233 SkAutoKern autokern;
jvanverthaab626c2014-10-16 08:04:39 -0700234 const char* stop = text + byteLength;
jvanverth2b9dc1d2014-10-20 06:48:59 -0700235 while (textPtr < stop) {
236 // don't need x, y here, since all subpixel variants will have the
237 // same advance
238 const SkGlyph& glyph = glyphCacheProc(cache, &textPtr, 0, 0);
jvanverthaab626c2014-10-16 08:04:39 -0700239
jvanverth2b9dc1d2014-10-20 06:48:59 -0700240 SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph);
241 positions.push_back(SkFixedToScalar(stopX + SkFixedMul_portable(origin, width)));
jvanverthaab626c2014-10-16 08:04:39 -0700242
jvanverth2b9dc1d2014-10-20 06:48:59 -0700243 SkFixed height = glyph.fAdvanceY;
244 positions.push_back(SkFixedToScalar(stopY + SkFixedMul_portable(origin, height)));
jvanverthaab626c2014-10-16 08:04:39 -0700245
jvanverth2b9dc1d2014-10-20 06:48:59 -0700246 stopX += width;
247 stopY += height;
jvanverthaab626c2014-10-16 08:04:39 -0700248 }
jvanverth2b9dc1d2014-10-20 06:48:59 -0700249 SkASSERT(textPtr == stop);
jvanverthaab626c2014-10-16 08:04:39 -0700250
jvanverth2b9dc1d2014-10-20 06:48:59 -0700251 // now adjust starting point depending on alignment
252 SkScalar alignX = SkFixedToScalar(stopX);
253 SkScalar alignY = SkFixedToScalar(stopY);
254 if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
255 alignX = SkScalarHalf(alignX);
256 alignY = SkScalarHalf(alignY);
257 } else if (skPaint.getTextAlign() == SkPaint::kLeft_Align) {
258 alignX = 0;
259 alignY = 0;
jvanverthaab626c2014-10-16 08:04:39 -0700260 }
jvanverth2b9dc1d2014-10-20 06:48:59 -0700261 x -= alignX;
262 y -= alignY;
263 SkPoint offset = SkPoint::Make(x, y);
jvanverthaab626c2014-10-16 08:04:39 -0700264
jvanverth2b9dc1d2014-10-20 06:48:59 -0700265 this->drawPosText(paint, skPaint, text, byteLength, positions.begin(), 2, offset);
jvanverthaab626c2014-10-16 08:04:39 -0700266}
267
jvanverth8c27a182014-10-14 08:45:50 -0700268void GrDistanceFieldTextContext::onDrawPosText(const GrPaint& paint, const SkPaint& skPaint,
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000269 const char text[], size_t byteLength,
fmalita05c4a432014-09-29 06:29:53 -0700270 const SkScalar pos[], int scalarsPerPosition,
271 const SkPoint& offset) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000272
273 SkASSERT(byteLength == 0 || text != NULL);
274 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
275
276 // nothing to draw
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000277 if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000278 return;
279 }
280
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
313 fallbackTxt.push_back_n(text-lastText, lastText);
314 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
342 fallbackTxt.push_back_n(text-lastText, lastText);
343 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) {
356 fFallbackTextContext->drawPosText(paint, skPaint, fallbackTxt.begin(), fallbackTxt.count(),
357 fallbackPos.begin(), scalarsPerPosition, offset);
358 }
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000359}
jvanverth0fedb192014-10-08 09:07:27 -0700360
361static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
362 unsigned r = SkColorGetR(c);
363 unsigned g = SkColorGetG(c);
364 unsigned b = SkColorGetB(c);
365 return GrColorPackRGBA(r, g, b, 0xff);
366}
367
joshualitt9853cce2014-11-17 14:22:48 -0800368static size_t get_vertex_stride(bool useColorVerts) {
369 return useColorVerts ? (2 * sizeof(SkPoint) + sizeof(GrColor)) :
370 (2 * sizeof(SkPoint));
371}
372
373static void* alloc_vertices(GrDrawTarget* drawTarget,
374 int numVertices,
375 bool useColorVerts) {
jvanverth73f10532014-10-23 11:57:12 -0700376 if (numVertices <= 0) {
377 return NULL;
378 }
379
jvanverth73f10532014-10-23 11:57:12 -0700380 void* vertices = NULL;
381 bool success = drawTarget->reserveVertexAndIndexSpace(numVertices,
joshualitt9853cce2014-11-17 14:22:48 -0800382 get_vertex_stride(useColorVerts),
jvanverth73f10532014-10-23 11:57:12 -0700383 0,
384 &vertices,
385 NULL);
386 GrAlwaysAssert(success);
387 return vertices;
388}
389
jvanverth0fedb192014-10-08 09:07:27 -0700390void GrDistanceFieldTextContext::setupCoverageEffect(const SkColor& filteredColor) {
391 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode);
392 GrTextureParams gammaParams(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
393
394 uint32_t textureUniqueID = fCurrTexture->getUniqueID();
395 const SkMatrix& ctm = fContext->getMatrix();
396
397 // set up any flags
398 uint32_t flags = 0;
399 flags |= ctm.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
400 flags |= fUseLCDText ? kUseLCD_DistanceFieldEffectFlag : 0;
401 flags |= fUseLCDText && ctm.rectStaysRect() ?
402 kRectToRect_DistanceFieldEffectFlag : 0;
reedb2d77e42014-10-14 08:26:33 -0700403 bool useBGR = SkPixelGeometryIsBGR(fDeviceProperties.pixelGeometry());
jvanverth0fedb192014-10-08 09:07:27 -0700404 flags |= fUseLCDText && useBGR ? kBGR_DistanceFieldEffectFlag : 0;
405
406 // see if we need to create a new effect
407 if (textureUniqueID != fEffectTextureUniqueID ||
408 filteredColor != fEffectColor ||
409 flags != fEffectFlags) {
410 if (fUseLCDText) {
411 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
412 fCachedGeometryProcessor.reset(GrDistanceFieldLCDTextureEffect::Create(fCurrTexture,
413 params,
414 fGammaTexture,
415 gammaParams,
416 colorNoPreMul,
417 flags));
418 } else {
joshualitt2dd1ae02014-12-03 06:24:10 -0800419 flags |= kColorAttr_DistanceFieldEffectFlag;
jvanverth0fedb192014-10-08 09:07:27 -0700420#ifdef SK_GAMMA_APPLY_TO_A8
reedb2d77e42014-10-14 08:26:33 -0700421 U8CPU lum = SkColorSpaceLuminance::computeLuminance(fDeviceProperties.gamma(),
jvanverth0fedb192014-10-08 09:07:27 -0700422 filteredColor);
423 fCachedGeometryProcessor.reset(GrDistanceFieldTextureEffect::Create(fCurrTexture,
424 params,
425 fGammaTexture,
426 gammaParams,
427 lum/255.f,
428 flags));
429#else
jvanverth2faa2282014-10-31 12:59:57 -0700430 fCachedGeometryProcessor.reset(GrDistanceFieldNoGammaTextureEffect::Create(fCurrTexture,
jvanverth0fedb192014-10-08 09:07:27 -0700431 params, flags));
432#endif
433 }
434 fEffectTextureUniqueID = textureUniqueID;
435 fEffectColor = filteredColor;
436 fEffectFlags = flags;
437 }
438
439}
440
jvanverthfca302c2014-10-20 13:12:54 -0700441// Returns true if this method handled the glyph, false if needs to be passed to fallback
442//
443bool GrDistanceFieldTextContext::appendGlyph(GrGlyph::PackedID packed,
djsollen058f01e2014-10-30 11:54:43 -0700444 SkScalar sx, SkScalar sy,
jvanverth0fedb192014-10-08 09:07:27 -0700445 GrFontScaler* scaler) {
446 if (NULL == fDrawTarget) {
jvanverthfca302c2014-10-20 13:12:54 -0700447 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700448 }
449
450 if (NULL == fStrike) {
451 fStrike = fContext->getFontCache()->getStrike(scaler, true);
452 }
453
454 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
455 if (NULL == glyph || glyph->fBounds.isEmpty()) {
jvanverthfca302c2014-10-20 13:12:54 -0700456 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700457 }
458
jvanverthfca302c2014-10-20 13:12:54 -0700459 // fallback to color glyph support
jvanverth294c3262014-10-10 11:36:12 -0700460 if (kA8_GrMaskFormat != glyph->fMaskFormat) {
jvanverthfca302c2014-10-20 13:12:54 -0700461 return false;
jvanverth294c3262014-10-10 11:36:12 -0700462 }
463
jvanverth2faa2282014-10-31 12:59:57 -0700464 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
465 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
466 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2*SK_DistanceFieldInset);
467 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2*SK_DistanceFieldInset);
jvanverth0fedb192014-10-08 09:07:27 -0700468
jvanverth2faa2282014-10-31 12:59:57 -0700469 SkScalar scale = fTextRatio;
470 dx *= scale;
471 dy *= scale;
472 sx += dx;
473 sy += dy;
474 width *= scale;
475 height *= scale;
476 SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height);
jvanverth0fedb192014-10-08 09:07:27 -0700477
478 // check if we clipped out
jvanverth2faa2282014-10-31 12:59:57 -0700479 SkRect dstRect;
480 const SkMatrix& ctm = fContext->getMatrix();
481 (void) ctm.mapRect(&dstRect, glyphRect);
482 if (fClipRect.quickReject(SkScalarTruncToInt(dstRect.left()),
483 SkScalarTruncToInt(dstRect.top()),
484 SkScalarTruncToInt(dstRect.right()),
485 SkScalarTruncToInt(dstRect.bottom()))) {
jvanverth0fedb192014-10-08 09:07:27 -0700486// SkCLZ(3); // so we can set a break-point in the debugger
jvanverth2faa2282014-10-31 12:59:57 -0700487 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700488 }
jvanverth2faa2282014-10-31 12:59:57 -0700489
jvanverth0fedb192014-10-08 09:07:27 -0700490 if (NULL == glyph->fPlot) {
491 if (!fStrike->glyphTooLargeForAtlas(glyph)) {
492 if (fStrike->addGlyphToAtlas(glyph, scaler)) {
493 goto HAS_ATLAS;
494 }
495
496 // try to clear out an unused plot before we flush
jvanverth294c3262014-10-10 11:36:12 -0700497 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
jvanverth0fedb192014-10-08 09:07:27 -0700498 fStrike->addGlyphToAtlas(glyph, scaler)) {
499 goto HAS_ATLAS;
500 }
501
502 if (c_DumpFontCache) {
503#ifdef SK_DEVELOPER
504 fContext->getFontCache()->dump();
505#endif
506 }
507
508 // before we purge the cache, we must flush any accumulated draws
509 this->flush();
510 fContext->flush();
511
512 // we should have an unused plot now
jvanverth294c3262014-10-10 11:36:12 -0700513 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
jvanverth0fedb192014-10-08 09:07:27 -0700514 fStrike->addGlyphToAtlas(glyph, scaler)) {
515 goto HAS_ATLAS;
516 }
517 }
518
519 if (NULL == glyph->fPath) {
520 SkPath* path = SkNEW(SkPath);
521 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
522 // flag the glyph as being dead?
523 delete path;
jvanverthfca302c2014-10-20 13:12:54 -0700524 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700525 }
526 glyph->fPath = path;
527 }
528
bsalomonec87dc62014-10-14 10:52:00 -0700529 // flush any accumulated draws before drawing this glyph as a path.
530 this->flush();
531
jvanverth0fedb192014-10-08 09:07:27 -0700532 GrContext::AutoMatrix am;
533 SkMatrix ctm;
534 ctm.setScale(fTextRatio, fTextRatio);
jvanverth0e66aaa2014-11-04 13:32:53 -0800535 ctm.postTranslate(sx - dx, sy - dy);
jvanverth0fedb192014-10-08 09:07:27 -0700536 GrPaint tmpPaint(fPaint);
537 am.setPreConcat(fContext, ctm, &tmpPaint);
538 GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle);
539 fContext->drawPath(tmpPaint, *glyph->fPath, strokeInfo);
jvanverth73f10532014-10-23 11:57:12 -0700540
541 // remove this glyph from the vertices we need to allocate
542 fTotalVertexCount -= kVerticesPerGlyph;
jvanverthfca302c2014-10-20 13:12:54 -0700543 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700544 }
545
546HAS_ATLAS:
547 SkASSERT(glyph->fPlot);
548 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
549 glyph->fPlot->setDrawToken(drawToken);
550
551 GrTexture* texture = glyph->fPlot->texture();
552 SkASSERT(texture);
553
jvanverth73f10532014-10-23 11:57:12 -0700554 if (fCurrTexture != texture || fCurrVertex + kVerticesPerGlyph > fTotalVertexCount) {
jvanverth0fedb192014-10-08 09:07:27 -0700555 this->flush();
556 fCurrTexture = texture;
557 fCurrTexture->ref();
558 }
559
560 bool useColorVerts = !fUseLCDText;
561
562 if (NULL == fVertices) {
jvanverth73f10532014-10-23 11:57:12 -0700563 int maxQuadVertices = kVerticesPerGlyph * fContext->getQuadIndexBuffer()->maxQuads();
564 fAllocVertexCount = SkMin32(fTotalVertexCount, maxQuadVertices);
joshualitt9853cce2014-11-17 14:22:48 -0800565 fVertices = alloc_vertices(fDrawTarget,
566 fAllocVertexCount,
567 useColorVerts);
jvanverth0fedb192014-10-08 09:07:27 -0700568 }
569
jvanverth0fedb192014-10-08 09:07:27 -0700570 SkFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX + SK_DistanceFieldInset);
571 SkFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY + SK_DistanceFieldInset);
572 SkFixed tw = SkIntToFixed(glyph->fBounds.width() - 2*SK_DistanceFieldInset);
573 SkFixed th = SkIntToFixed(glyph->fBounds.height() - 2*SK_DistanceFieldInset);
574
jvanverth2faa2282014-10-31 12:59:57 -0700575 fVertexBounds.joinNonEmptyArg(glyphRect);
jvanverth0fedb192014-10-08 09:07:27 -0700576
joshualitt9853cce2014-11-17 14:22:48 -0800577 size_t vertSize = get_vertex_stride(useColorVerts);
jvanverth0fedb192014-10-08 09:07:27 -0700578
579 SkPoint* positions = reinterpret_cast<SkPoint*>(
580 reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVertex);
jvanverth2faa2282014-10-31 12:59:57 -0700581 positions->setRectFan(glyphRect.fLeft, glyphRect.fTop, glyphRect.fRight, glyphRect.fBottom,
582 vertSize);
jvanverth0fedb192014-10-08 09:07:27 -0700583
584 // The texture coords are last in both the with and without color vertex layouts.
585 SkPoint* textureCoords = reinterpret_cast<SkPoint*>(
586 reinterpret_cast<intptr_t>(positions) + vertSize - sizeof(SkPoint));
587 textureCoords->setRectFan(SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx)),
588 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty)),
589 SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx + tw)),
590 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty + th)),
591 vertSize);
592 if (useColorVerts) {
jvanverth0fedb192014-10-08 09:07:27 -0700593 // color comes after position.
594 GrColor* colors = reinterpret_cast<GrColor*>(positions + 1);
595 for (int i = 0; i < 4; ++i) {
596 *colors = fPaint.getColor();
597 colors = reinterpret_cast<GrColor*>(reinterpret_cast<intptr_t>(colors) + vertSize);
598 }
599 }
600
601 fCurrVertex += 4;
jvanverthfca302c2014-10-20 13:12:54 -0700602
603 return true;
jvanverth0fedb192014-10-08 09:07:27 -0700604}
605
606void GrDistanceFieldTextContext::flush() {
607 if (NULL == fDrawTarget) {
608 return;
609 }
610
jvanverth0fedb192014-10-08 09:07:27 -0700611 if (fCurrVertex > 0) {
joshualitt780b11e2014-11-18 09:40:40 -0800612 GrDrawState drawState;
613 drawState.setFromPaint(fPaint, fContext->getMatrix(), fContext->getRenderTarget());
joshualitt780b11e2014-11-18 09:40:40 -0800614
jvanverth0fedb192014-10-08 09:07:27 -0700615 // setup our sampler state for our text texture/atlas
616 SkASSERT(SkIsAlign4(fCurrVertex));
617
618 // get our current color
619 SkColor filteredColor;
620 SkColorFilter* colorFilter = fSkPaint.getColorFilter();
621 if (colorFilter) {
622 filteredColor = colorFilter->filterColor(fSkPaint.getColor());
623 } else {
624 filteredColor = fSkPaint.getColor();
625 }
626 this->setupCoverageEffect(filteredColor);
627
628 // Effects could be stored with one of the cache objects (atlas?)
joshualitt9853cce2014-11-17 14:22:48 -0800629 drawState.setGeometryProcessor(fCachedGeometryProcessor.get());
jvanverth0fedb192014-10-08 09:07:27 -0700630
631 // Set draw state
632 if (fUseLCDText) {
633 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
634 if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
635 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
636 fPaint.numColorStages()) {
tfarina38406c82014-10-31 07:11:12 -0700637 SkDebugf("LCD Text will not draw correctly.\n");
jvanverth0fedb192014-10-08 09:07:27 -0700638 }
joshualitt9853cce2014-11-17 14:22:48 -0800639 SkASSERT(!drawState.hasColorVertexAttribute());
jvanverth0fedb192014-10-08 09:07:27 -0700640 // We don't use the GrPaint's color in this case because it's been premultiplied by
641 // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by
642 // the mask texture color. The end result is that we get
643 // mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor
644 int a = SkColorGetA(fSkPaint.getColor());
645 // paintAlpha
joshualitt9853cce2014-11-17 14:22:48 -0800646 drawState.setColor(SkColorSetARGB(a, a, a, a));
jvanverth0fedb192014-10-08 09:07:27 -0700647 // paintColor
joshualitt9853cce2014-11-17 14:22:48 -0800648 drawState.setBlendConstant(colorNoPreMul);
649 drawState.setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
jvanverth0fedb192014-10-08 09:07:27 -0700650 } else {
joshualitt9853cce2014-11-17 14:22:48 -0800651 if (0xFF == GrColorUnpackA(fPaint.getColor())) {
652 drawState.setHint(GrDrawState::kVertexColorsAreOpaque_Hint, true);
653 }
jvanverth0fedb192014-10-08 09:07:27 -0700654 // set back to normal in case we took LCD path previously.
joshualitt9853cce2014-11-17 14:22:48 -0800655 drawState.setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
jvanverth0fedb192014-10-08 09:07:27 -0700656 // We're using per-vertex color.
joshualitt9853cce2014-11-17 14:22:48 -0800657 SkASSERT(drawState.hasColorVertexAttribute());
jvanverth0fedb192014-10-08 09:07:27 -0700658 }
jvanverth73f10532014-10-23 11:57:12 -0700659 int nGlyphs = fCurrVertex / kVerticesPerGlyph;
jvanverth0fedb192014-10-08 09:07:27 -0700660 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
joshualitt9853cce2014-11-17 14:22:48 -0800661 fDrawTarget->drawIndexedInstances(&drawState,
662 kTriangles_GrPrimitiveType,
jvanverth0fedb192014-10-08 09:07:27 -0700663 nGlyphs,
joshualitt9853cce2014-11-17 14:22:48 -0800664 kVerticesPerGlyph,
665 kIndicesPerGlyph,
666 &fVertexBounds);
jvanverth0fedb192014-10-08 09:07:27 -0700667 fDrawTarget->resetVertexSource();
668 fVertices = NULL;
jvanverth73f10532014-10-23 11:57:12 -0700669 fTotalVertexCount -= fCurrVertex;
jvanverth0fedb192014-10-08 09:07:27 -0700670 fCurrVertex = 0;
671 SkSafeSetNull(fCurrTexture);
672 fVertexBounds.setLargestInverted();
673 }
674}
675
676inline void GrDistanceFieldTextContext::finish() {
677 this->flush();
jvanverth73f10532014-10-23 11:57:12 -0700678 fTotalVertexCount = 0;
jvanverth0fedb192014-10-08 09:07:27 -0700679
680 GrTextContext::finish();
681}
682