blob: 9fa0c1d7b8780e0c73eb9c4040afdc11fc2cc0c7 [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
21#include "SkColorFilter.h"
commit-bot@chromium.org64b08a12014-04-15 17:53:21 +000022#include "SkDistanceFieldGen.h"
commit-bot@chromium.org9f94b912014-01-30 15:22:54 +000023#include "SkDraw.h"
bsalomonafbf2d62014-09-30 12:18:44 -070024#include "SkGlyphCache.h"
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +000025#include "SkGpuDevice.h"
jvanverth@google.comd830d132013-11-11 20:54:09 +000026#include "SkPath.h"
27#include "SkRTConf.h"
28#include "SkStrokeRec.h"
29#include "effects/GrDistanceFieldTextureEffect.h"
30
jvanverthfeceba52014-07-25 19:03:34 -070031SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
32 "Dump the contents of the font cache before every purge.");
33
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +000034static const int kSmallDFFontSize = 32;
35static const int kSmallDFFontLimit = 32;
36static const int kMediumDFFontSize = 64;
37static const int kMediumDFFontLimit = 64;
38static const int kLargeDFFontSize = 128;
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +000039
jvanverthfeceba52014-07-25 19:03:34 -070040namespace {
41// position + texture coord
42extern const GrVertexAttrib gTextVertexAttribs[] = {
43 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
joshualittb0a8a372014-09-23 09:50:21 -070044 {kVec2f_GrVertexAttribType, sizeof(SkPoint) , kGeometryProcessor_GrVertexAttribBinding}
jvanverthfeceba52014-07-25 19:03:34 -070045};
46
egdaniel7b3d5ee2014-08-28 05:41:14 -070047static const size_t kTextVASize = 2 * sizeof(SkPoint);
48
jvanverthfeceba52014-07-25 19:03:34 -070049// position + color + texture coord
50extern const GrVertexAttrib gTextVertexWithColorAttribs[] = {
51 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
52 {kVec4ub_GrVertexAttribType, sizeof(SkPoint), kColor_GrVertexAttribBinding},
joshualittb0a8a372014-09-23 09:50:21 -070053 {kVec2f_GrVertexAttribType, sizeof(SkPoint) + sizeof(GrColor), kGeometryProcessor_GrVertexAttribBinding}
jvanverthfeceba52014-07-25 19:03:34 -070054};
55
egdaniel7b3d5ee2014-08-28 05:41:14 -070056static const size_t kTextVAColorSize = 2 * sizeof(SkPoint) + sizeof(GrColor);
57
jvanverthfeceba52014-07-25 19:03:34 -070058};
jvanverth@google.comd830d132013-11-11 20:54:09 +000059
skia.committer@gmail.come5d70152014-01-29 07:01:48 +000060GrDistanceFieldTextContext::GrDistanceFieldTextContext(GrContext* context,
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000061 const SkDeviceProperties& properties,
62 bool enable)
Mike Klein6a25bd02014-08-29 10:03:59 -040063 : GrTextContext(context, properties) {
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000064#if SK_FORCE_DISTANCEFIELD_FONTS
Mike Klein6a25bd02014-08-29 10:03:59 -040065 fEnableDFRendering = true;
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000066#else
Mike Klein6a25bd02014-08-29 10:03:59 -040067 fEnableDFRendering = enable;
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +000068#endif
Mike Klein6a25bd02014-08-29 10:03:59 -040069 fStrike = NULL;
70 fGammaTexture = NULL;
71
72 fCurrTexture = NULL;
73 fCurrVertex = 0;
74 fEffectTextureUniqueID = SK_InvalidUniqueID;
75 fEffectColor = GrColor_ILLEGAL;
76 fEffectFlags = 0;
77
78 fVertices = NULL;
79 fMaxVertices = 0;
80
jvanverth1723bfc2014-07-30 09:16:33 -070081 fVertexBounds.setLargestInverted();
jvanverth@google.comd830d132013-11-11 20:54:09 +000082}
83
jvanverth8c27a182014-10-14 08:45:50 -070084GrDistanceFieldTextContext* GrDistanceFieldTextContext::Create(GrContext* context,
85 const SkDeviceProperties& props,
86 bool enable) {
87 GrDistanceFieldTextContext* textContext = SkNEW_ARGS(GrDistanceFieldTextContext,
88 (context, props, enable));
89 textContext->fFallbackTextContext = GrBitmapTextContext::Create(context, props);
90
91 return textContext;
92}
93
jvanverth@google.comd830d132013-11-11 20:54:09 +000094GrDistanceFieldTextContext::~GrDistanceFieldTextContext() {
jvanverth0fedb192014-10-08 09:07:27 -070095 this->flush();
jvanverth2d2a68c2014-06-10 06:42:56 -070096 SkSafeSetNull(fGammaTexture);
jvanverth@google.comd830d132013-11-11 20:54:09 +000097}
98
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000099bool GrDistanceFieldTextContext::canDraw(const SkPaint& paint) {
commit-bot@chromium.org6fcd1ef2014-05-02 12:39:41 +0000100 if (!fEnableDFRendering && !paint.isDistanceFieldTextTEMP()) {
commit-bot@chromium.orgeefd8a02014-04-08 20:14:32 +0000101 return false;
102 }
103
skia.committer@gmail.come1d94432014-04-09 03:04:11 +0000104 // rasterizers and mask filters modify alpha, which doesn't
commit-bot@chromium.orgeefd8a02014-04-08 20:14:32 +0000105 // translate well to distance
106 if (paint.getRasterizer() || paint.getMaskFilter() ||
107 !fContext->getTextTarget()->caps()->shaderDerivativeSupport()) {
108 return false;
109 }
110
111 // TODO: add some stroking support
112 if (paint.getStyle() != SkPaint::kFill_Style) {
113 return false;
114 }
115
116 // TODO: choose an appropriate maximum scale for distance fields and
117 // enable perspective
118 if (SkDraw::ShouldDrawTextAsPaths(paint, fContext->getMatrix())) {
119 return false;
120 }
121
122 // distance fields cannot represent color fonts
123 SkScalerContext::Rec rec;
124 SkScalerContext::MakeRec(paint, &fDeviceProperties, NULL, &rec);
125 return rec.getFormat() != SkMask::kARGB32_Format;
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 fCurrVertex = 0;
148
149 fVertices = NULL;
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000150
jvanverth76ce81e2014-09-22 14:26:53 -0700151 if (scaledTextSize <= kSmallDFFontLimit) {
jvanverth9564ce62014-09-16 05:45:19 -0700152 fTextRatio = textSize / kSmallDFFontSize;
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000153 fSkPaint.setTextSize(SkIntToScalar(kSmallDFFontSize));
jvanverth76ce81e2014-09-22 14:26:53 -0700154 } else if (scaledTextSize <= kMediumDFFontLimit) {
jvanverth9564ce62014-09-16 05:45:19 -0700155 fTextRatio = textSize / kMediumDFFontSize;
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000156 fSkPaint.setTextSize(SkIntToScalar(kMediumDFFontSize));
157 } else {
jvanverth9564ce62014-09-16 05:45:19 -0700158 fTextRatio = textSize / kLargeDFFontSize;
commit-bot@chromium.orgdc5cd852014-04-02 19:24:32 +0000159 fSkPaint.setTextSize(SkIntToScalar(kLargeDFFontSize));
160 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000161
commit-bot@chromium.org609ced42014-04-03 18:25:48 +0000162 fUseLCDText = fSkPaint.isLCDRenderText();
skia.committer@gmail.com221b9112014-04-04 03:04:32 +0000163
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000164 fSkPaint.setLCDRenderText(false);
165 fSkPaint.setAutohinted(false);
jvanverth2d2a68c2014-06-10 06:42:56 -0700166 fSkPaint.setHinting(SkPaint::kNormal_Hinting);
commit-bot@chromium.org0bed43c2014-03-14 21:22:38 +0000167 fSkPaint.setSubpixelText(true);
jvanverth2d2a68c2014-06-10 06:42:56 -0700168
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000169}
170
jvanverth2d2a68c2014-06-10 06:42:56 -0700171static void setup_gamma_texture(GrContext* context, const SkGlyphCache* cache,
172 const SkDeviceProperties& deviceProperties,
173 GrTexture** gammaTexture) {
174 if (NULL == *gammaTexture) {
175 int width, height;
176 size_t size;
177
178#ifdef SK_GAMMA_CONTRAST
179 SkScalar contrast = SK_GAMMA_CONTRAST;
180#else
181 SkScalar contrast = 0.5f;
182#endif
reedb2d77e42014-10-14 08:26:33 -0700183 SkScalar paintGamma = deviceProperties.gamma();
184 SkScalar deviceGamma = deviceProperties.gamma();
jvanverth2d2a68c2014-06-10 06:42:56 -0700185
186 size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
187 &width, &height);
188
189 SkAutoTArray<uint8_t> data((int)size);
190 SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
191
192 // TODO: Update this to use the cache rather than directly creating a texture.
193 GrTextureDesc desc;
194 desc.fFlags = kDynamicUpdate_GrTextureFlagBit;
195 desc.fWidth = width;
196 desc.fHeight = height;
197 desc.fConfig = kAlpha_8_GrPixelConfig;
198
199 *gammaTexture = context->getGpu()->createTexture(desc, NULL, 0);
200 if (NULL == *gammaTexture) {
201 return;
202 }
203
bsalomon81beccc2014-10-13 12:32:55 -0700204 (*gammaTexture)->writePixels(0, 0, width, height,
205 (*gammaTexture)->config(), data.get(), 0,
206 GrContext::kDontFlush_PixelOpsFlag);
jvanverth2d2a68c2014-06-10 06:42:56 -0700207 }
208}
209
jvanverth8c27a182014-10-14 08:45:50 -0700210void GrDistanceFieldTextContext::onDrawText(const GrPaint& paint, const SkPaint& skPaint,
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000211 const char text[], size_t byteLength,
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000212 SkScalar x, SkScalar y) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000213 SkASSERT(byteLength == 0 || text != NULL);
214
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000215 // nothing to draw or can't draw
216 if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/
217 || fSkPaint.getRasterizer()) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000218 return;
219 }
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000220
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000221 this->init(paint, skPaint);
222
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000223 SkScalar sizeRatio = fTextRatio;
224
225 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
226
jvanverth2d2a68c2014-06-10 06:42:56 -0700227 SkAutoGlyphCacheNoGamma autoCache(fSkPaint, &fDeviceProperties, NULL);
228 SkGlyphCache* cache = autoCache.getCache();
229 GrFontScaler* fontScaler = GetGrFontScaler(cache);
230
231 setup_gamma_texture(fContext, cache, fDeviceProperties, &fGammaTexture);
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000232
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000233 // need to measure first
234 // TODO - generate positions and pre-load cache as well?
235 const char* stop = text + byteLength;
236 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
237 SkFixed stopX = 0;
238 SkFixed stopY = 0;
239
240 const char* textPtr = text;
241 while (textPtr < stop) {
242 // don't need x, y here, since all subpixel variants will have the
243 // same advance
244 const SkGlyph& glyph = glyphCacheProc(cache, &textPtr, 0, 0);
245
246 stopX += glyph.fAdvanceX;
247 stopY += glyph.fAdvanceY;
248 }
249 SkASSERT(textPtr == stop);
250
251 SkScalar alignX = SkFixedToScalar(stopX)*sizeRatio;
252 SkScalar alignY = SkFixedToScalar(stopY)*sizeRatio;
253
254 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
255 alignX = SkScalarHalf(alignX);
256 alignY = SkScalarHalf(alignY);
257 }
258
259 x -= alignX;
260 y -= alignY;
261 }
262
commit-bot@chromium.org5408f8f2014-05-21 19:44:24 +0000263 SkFixed fx = SkScalarToFixed(x);
264 SkFixed fy = SkScalarToFixed(y);
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000265 SkFixed fixedScale = SkScalarToFixed(sizeRatio);
266 while (text < stop) {
commit-bot@chromium.orga9dae712014-03-24 18:34:04 +0000267 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000268
269 if (glyph.fWidth) {
jvanverth0fedb192014-10-08 09:07:27 -0700270 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
271 glyph.getSubXFixed(),
272 glyph.getSubYFixed()),
273 fx,
274 fy,
275 fontScaler);
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000276 }
277
278 fx += SkFixedMul_portable(glyph.fAdvanceX, fixedScale);
279 fy += SkFixedMul_portable(glyph.fAdvanceY, fixedScale);
280 }
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000281
282 this->finish();
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000283}
284
jvanverth8c27a182014-10-14 08:45:50 -0700285void GrDistanceFieldTextContext::onDrawPosText(const GrPaint& paint, const SkPaint& skPaint,
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000286 const char text[], size_t byteLength,
fmalita05c4a432014-09-29 06:29:53 -0700287 const SkScalar pos[], int scalarsPerPosition,
288 const SkPoint& offset) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000289
290 SkASSERT(byteLength == 0 || text != NULL);
291 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
292
293 // nothing to draw
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000294 if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/) {
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000295 return;
296 }
297
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000298 this->init(paint, skPaint);
299
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000300 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
301
jvanverth2d2a68c2014-06-10 06:42:56 -0700302 SkAutoGlyphCacheNoGamma autoCache(fSkPaint, &fDeviceProperties, NULL);
303 SkGlyphCache* cache = autoCache.getCache();
304 GrFontScaler* fontScaler = GetGrFontScaler(cache);
305
306 setup_gamma_texture(fContext, cache, fDeviceProperties, &fGammaTexture);
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000307
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000308 const char* stop = text + byteLength;
309
310 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
311 while (text < stop) {
312 // the last 2 parameters are ignored
313 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
314
315 if (glyph.fWidth) {
fmalita05c4a432014-09-29 06:29:53 -0700316 SkScalar x = offset.x() + pos[0];
317 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000318
jvanverth0fedb192014-10-08 09:07:27 -0700319 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
320 glyph.getSubXFixed(),
321 glyph.getSubYFixed()),
322 SkScalarToFixed(x),
323 SkScalarToFixed(y),
324 fontScaler);
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000325 }
326 pos += scalarsPerPosition;
327 }
328 } else {
329 int alignShift = SkPaint::kCenter_Align == fSkPaint.getTextAlign() ? 1 : 0;
330 while (text < stop) {
331 // the last 2 parameters are ignored
332 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
333
334 if (glyph.fWidth) {
fmalita05c4a432014-09-29 06:29:53 -0700335 SkScalar x = offset.x() + pos[0];
336 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
skia.committer@gmail.com22e96722013-12-20 07:01:36 +0000337
jvanverth0fedb192014-10-08 09:07:27 -0700338 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
339 glyph.getSubXFixed(),
340 glyph.getSubYFixed()),
341 SkScalarToFixed(x) - (glyph.fAdvanceX >> alignShift),
342 SkScalarToFixed(y) - (glyph.fAdvanceY >> alignShift),
343 fontScaler);
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000344 }
345 pos += scalarsPerPosition;
346 }
347 }
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000348
349 this->finish();
commit-bot@chromium.org8128d8c2013-12-19 16:12:25 +0000350}
jvanverth0fedb192014-10-08 09:07:27 -0700351
352static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
353 unsigned r = SkColorGetR(c);
354 unsigned g = SkColorGetG(c);
355 unsigned b = SkColorGetB(c);
356 return GrColorPackRGBA(r, g, b, 0xff);
357}
358
359void GrDistanceFieldTextContext::setupCoverageEffect(const SkColor& filteredColor) {
360 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode);
361 GrTextureParams gammaParams(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
362
363 uint32_t textureUniqueID = fCurrTexture->getUniqueID();
364 const SkMatrix& ctm = fContext->getMatrix();
365
366 // set up any flags
367 uint32_t flags = 0;
368 flags |= ctm.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
369 flags |= fUseLCDText ? kUseLCD_DistanceFieldEffectFlag : 0;
370 flags |= fUseLCDText && ctm.rectStaysRect() ?
371 kRectToRect_DistanceFieldEffectFlag : 0;
reedb2d77e42014-10-14 08:26:33 -0700372 bool useBGR = SkPixelGeometryIsBGR(fDeviceProperties.pixelGeometry());
jvanverth0fedb192014-10-08 09:07:27 -0700373 flags |= fUseLCDText && useBGR ? kBGR_DistanceFieldEffectFlag : 0;
374
375 // see if we need to create a new effect
376 if (textureUniqueID != fEffectTextureUniqueID ||
377 filteredColor != fEffectColor ||
378 flags != fEffectFlags) {
379 if (fUseLCDText) {
380 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
381 fCachedGeometryProcessor.reset(GrDistanceFieldLCDTextureEffect::Create(fCurrTexture,
382 params,
383 fGammaTexture,
384 gammaParams,
385 colorNoPreMul,
386 flags));
387 } else {
388#ifdef SK_GAMMA_APPLY_TO_A8
reedb2d77e42014-10-14 08:26:33 -0700389 U8CPU lum = SkColorSpaceLuminance::computeLuminance(fDeviceProperties.gamma(),
jvanverth0fedb192014-10-08 09:07:27 -0700390 filteredColor);
391 fCachedGeometryProcessor.reset(GrDistanceFieldTextureEffect::Create(fCurrTexture,
392 params,
393 fGammaTexture,
394 gammaParams,
395 lum/255.f,
396 flags));
397#else
398 fCachedGeometryProcessor.reset(GrDistanceFieldTextureEffect::Create(fCurrTexture,
399 params, flags));
400#endif
401 }
402 fEffectTextureUniqueID = textureUniqueID;
403 fEffectColor = filteredColor;
404 fEffectFlags = flags;
405 }
406
407}
408
409void GrDistanceFieldTextContext::appendGlyph(GrGlyph::PackedID packed,
410 SkFixed vx, SkFixed vy,
411 GrFontScaler* scaler) {
412 if (NULL == fDrawTarget) {
413 return;
414 }
415
416 if (NULL == fStrike) {
417 fStrike = fContext->getFontCache()->getStrike(scaler, true);
418 }
419
420 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
421 if (NULL == glyph || glyph->fBounds.isEmpty()) {
422 return;
423 }
424
jvanverth294c3262014-10-10 11:36:12 -0700425 // TODO: support color glyphs
426 if (kA8_GrMaskFormat != glyph->fMaskFormat) {
427 return;
428 }
429
jvanverth0fedb192014-10-08 09:07:27 -0700430 SkScalar sx = SkFixedToScalar(vx);
431 SkScalar sy = SkFixedToScalar(vy);
432/*
433 // not valid, need to find a different solution for this
434 vx += SkIntToFixed(glyph->fBounds.fLeft);
435 vy += SkIntToFixed(glyph->fBounds.fTop);
436
437 // keep them as ints until we've done the clip-test
438 GrFixed width = glyph->fBounds.width();
439 GrFixed height = glyph->fBounds.height();
440
441 // check if we clipped out
442 if (true || NULL == glyph->fPlot) {
443 int x = vx >> 16;
444 int y = vy >> 16;
445 if (fClipRect.quickReject(x, y, x + width, y + height)) {
446// SkCLZ(3); // so we can set a break-point in the debugger
447 return;
448 }
449 }
450*/
451 if (NULL == glyph->fPlot) {
452 if (!fStrike->glyphTooLargeForAtlas(glyph)) {
453 if (fStrike->addGlyphToAtlas(glyph, scaler)) {
454 goto HAS_ATLAS;
455 }
456
457 // try to clear out an unused plot before we flush
jvanverth294c3262014-10-10 11:36:12 -0700458 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
jvanverth0fedb192014-10-08 09:07:27 -0700459 fStrike->addGlyphToAtlas(glyph, scaler)) {
460 goto HAS_ATLAS;
461 }
462
463 if (c_DumpFontCache) {
464#ifdef SK_DEVELOPER
465 fContext->getFontCache()->dump();
466#endif
467 }
468
469 // before we purge the cache, we must flush any accumulated draws
470 this->flush();
471 fContext->flush();
472
473 // we should have an unused plot now
jvanverth294c3262014-10-10 11:36:12 -0700474 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
jvanverth0fedb192014-10-08 09:07:27 -0700475 fStrike->addGlyphToAtlas(glyph, scaler)) {
476 goto HAS_ATLAS;
477 }
478 }
479
480 if (NULL == glyph->fPath) {
481 SkPath* path = SkNEW(SkPath);
482 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
483 // flag the glyph as being dead?
484 delete path;
485 return;
486 }
487 glyph->fPath = path;
488 }
489
bsalomonec87dc62014-10-14 10:52:00 -0700490 // flush any accumulated draws before drawing this glyph as a path.
491 this->flush();
492
jvanverth0fedb192014-10-08 09:07:27 -0700493 GrContext::AutoMatrix am;
494 SkMatrix ctm;
495 ctm.setScale(fTextRatio, fTextRatio);
496 ctm.postTranslate(sx, sy);
497 GrPaint tmpPaint(fPaint);
498 am.setPreConcat(fContext, ctm, &tmpPaint);
499 GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle);
500 fContext->drawPath(tmpPaint, *glyph->fPath, strokeInfo);
501 return;
502 }
503
504HAS_ATLAS:
505 SkASSERT(glyph->fPlot);
506 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
507 glyph->fPlot->setDrawToken(drawToken);
508
509 GrTexture* texture = glyph->fPlot->texture();
510 SkASSERT(texture);
511
512 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
513 this->flush();
514 fCurrTexture = texture;
515 fCurrTexture->ref();
516 }
517
518 bool useColorVerts = !fUseLCDText;
519
520 if (NULL == fVertices) {
521 // If we need to reserve vertices allow the draw target to suggest
522 // a number of verts to reserve and whether to perform a flush.
523 fMaxVertices = kMinRequestedVerts;
524 if (useColorVerts) {
525 fDrawTarget->drawState()->setVertexAttribs<gTextVertexWithColorAttribs>(
526 SK_ARRAY_COUNT(gTextVertexWithColorAttribs), kTextVAColorSize);
527 } else {
528 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
529 SK_ARRAY_COUNT(gTextVertexAttribs), kTextVASize);
530 }
531 bool flush = fDrawTarget->geometryHints(&fMaxVertices, NULL);
532 if (flush) {
533 this->flush();
534 fContext->flush();
535 if (useColorVerts) {
536 fDrawTarget->drawState()->setVertexAttribs<gTextVertexWithColorAttribs>(
537 SK_ARRAY_COUNT(gTextVertexWithColorAttribs), kTextVAColorSize);
538 } else {
539 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
540 SK_ARRAY_COUNT(gTextVertexAttribs), kTextVASize);
541 }
542 }
543 fMaxVertices = kDefaultRequestedVerts;
544 // ignore return, no point in flushing again.
545 fDrawTarget->geometryHints(&fMaxVertices, NULL);
546
547 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
548 if (fMaxVertices < kMinRequestedVerts) {
549 fMaxVertices = kDefaultRequestedVerts;
550 } else if (fMaxVertices > maxQuadVertices) {
551 // don't exceed the limit of the index buffer
552 fMaxVertices = maxQuadVertices;
553 }
554 bool success = fDrawTarget->reserveVertexAndIndexSpace(fMaxVertices,
555 0,
556 &fVertices,
557 NULL);
558 GrAlwaysAssert(success);
559 }
560
561 SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset);
562 SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset);
563 SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2*SK_DistanceFieldInset);
564 SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2*SK_DistanceFieldInset);
565
566 SkScalar scale = fTextRatio;
567 dx *= scale;
568 dy *= scale;
569 sx += dx;
570 sy += dy;
571 width *= scale;
572 height *= scale;
573
574 SkFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX + SK_DistanceFieldInset);
575 SkFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY + SK_DistanceFieldInset);
576 SkFixed tw = SkIntToFixed(glyph->fBounds.width() - 2*SK_DistanceFieldInset);
577 SkFixed th = SkIntToFixed(glyph->fBounds.height() - 2*SK_DistanceFieldInset);
578
579 SkRect r;
580 r.fLeft = sx;
581 r.fTop = sy;
582 r.fRight = sx + width;
583 r.fBottom = sy + height;
584
585 fVertexBounds.joinNonEmptyArg(r);
586
587 size_t vertSize = fUseLCDText ? (2 * sizeof(SkPoint))
588 : (2 * sizeof(SkPoint) + sizeof(GrColor));
589
590 SkASSERT(vertSize == fDrawTarget->getDrawState().getVertexStride());
591
592 SkPoint* positions = reinterpret_cast<SkPoint*>(
593 reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVertex);
594 positions->setRectFan(r.fLeft, r.fTop, r.fRight, r.fBottom, vertSize);
595
596 // The texture coords are last in both the with and without color vertex layouts.
597 SkPoint* textureCoords = reinterpret_cast<SkPoint*>(
598 reinterpret_cast<intptr_t>(positions) + vertSize - sizeof(SkPoint));
599 textureCoords->setRectFan(SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx)),
600 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty)),
601 SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx + tw)),
602 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty + th)),
603 vertSize);
604 if (useColorVerts) {
605 if (0xFF == GrColorUnpackA(fPaint.getColor())) {
606 fDrawTarget->drawState()->setHint(GrDrawState::kVertexColorsAreOpaque_Hint, true);
607 }
608 // 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;
617}
618
619void GrDistanceFieldTextContext::flush() {
620 if (NULL == fDrawTarget) {
621 return;
622 }
623
624 GrDrawState* drawState = fDrawTarget->drawState();
625 GrDrawState::AutoRestoreEffects are(drawState);
626
627 drawState->setFromPaint(fPaint, fContext->getMatrix(), fContext->getRenderTarget());
628
629 if (fCurrVertex > 0) {
630 // setup our sampler state for our text texture/atlas
631 SkASSERT(SkIsAlign4(fCurrVertex));
632
633 // get our current color
634 SkColor filteredColor;
635 SkColorFilter* colorFilter = fSkPaint.getColorFilter();
636 if (colorFilter) {
637 filteredColor = colorFilter->filterColor(fSkPaint.getColor());
638 } else {
639 filteredColor = fSkPaint.getColor();
640 }
641 this->setupCoverageEffect(filteredColor);
642
643 // Effects could be stored with one of the cache objects (atlas?)
644 drawState->setGeometryProcessor(fCachedGeometryProcessor.get());
645
646 // Set draw state
647 if (fUseLCDText) {
648 GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
649 if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
650 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
651 fPaint.numColorStages()) {
652 GrPrintf("LCD Text will not draw correctly.\n");
653 }
654 SkASSERT(!drawState->hasColorVertexAttribute());
655 // We don't use the GrPaint's color in this case because it's been premultiplied by
656 // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by
657 // the mask texture color. The end result is that we get
658 // mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor
659 int a = SkColorGetA(fSkPaint.getColor());
660 // paintAlpha
661 drawState->setColor(SkColorSetARGB(a, a, a, a));
662 // paintColor
663 drawState->setBlendConstant(colorNoPreMul);
664 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
665 } else {
666 // set back to normal in case we took LCD path previously.
667 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
668 // We're using per-vertex color.
669 SkASSERT(drawState->hasColorVertexAttribute());
670 }
671 int nGlyphs = fCurrVertex / 4;
672 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
673 fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
674 nGlyphs,
675 4, 6, &fVertexBounds);
676 fDrawTarget->resetVertexSource();
677 fVertices = NULL;
678 fMaxVertices = 0;
679 fCurrVertex = 0;
680 SkSafeSetNull(fCurrTexture);
681 fVertexBounds.setLargestInverted();
682 }
683}
684
685inline void GrDistanceFieldTextContext::finish() {
686 this->flush();
687
688 GrTextContext::finish();
689}
690