blob: f2a6d95cb8c7f04c0221862f4743a7a1ac93be9a [file] [log] [blame]
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +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 "GrBitmapTextContext.h"
9#include "GrAtlas.h"
10#include "GrDrawTarget.h"
11#include "GrFontScaler.h"
12#include "GrIndexBuffer.h"
13#include "GrTextStrike.h"
14#include "GrTextStrike_impl.h"
commit-bot@chromium.org42a89572013-10-28 15:13:50 +000015#include "SkColorPriv.h"
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000016#include "SkPath.h"
17#include "SkRTConf.h"
18#include "SkStrokeRec.h"
19#include "effects/GrCustomCoordsTextureEffect.h"
20
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +000021#include "SkAutoKern.h"
commit-bot@chromium.org9f94b912014-01-30 15:22:54 +000022#include "SkDraw.h"
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +000023#include "SkGlyphCache.h"
24#include "SkGpuDevice.h"
25#include "SkGr.h"
26
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000027static const int kGlyphCoordsAttributeIndex = 1;
28
29SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
30 "Dump the contents of the font cache before every purge.");
31
skia.committer@gmail.come5d70152014-01-29 07:01:48 +000032GrBitmapTextContext::GrBitmapTextContext(GrContext* context,
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +000033 const SkDeviceProperties& properties)
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000034 : GrTextContext(context, properties) {
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000035 fStrike = NULL;
36
37 fCurrTexture = NULL;
38 fCurrVertex = 0;
39
40 fVertices = NULL;
41 fMaxVertices = 0;
commit-bot@chromium.org3ae0e6c2014-02-11 18:24:25 +000042
43 fVertexBounds.setLargestInverted();
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000044}
45
46GrBitmapTextContext::~GrBitmapTextContext() {
47 this->flushGlyphs();
48}
49
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000050bool GrBitmapTextContext::canDraw(const SkPaint& paint) {
51 return !SkDraw::ShouldDrawTextAsPaths(paint, fContext->getMatrix());
52}
53
commit-bot@chromium.org42a89572013-10-28 15:13:50 +000054static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
55 unsigned r = SkColorGetR(c);
56 unsigned g = SkColorGetG(c);
57 unsigned b = SkColorGetB(c);
58 return GrColorPackRGBA(r, g, b, 0xff);
59}
60
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000061void GrBitmapTextContext::flushGlyphs() {
62 if (NULL == fDrawTarget) {
63 return;
64 }
65
66 GrDrawState* drawState = fDrawTarget->drawState();
67 GrDrawState::AutoRestoreEffects are(drawState);
68 drawState->setFromPaint(fPaint, SkMatrix::I(), fContext->getRenderTarget());
69
70 if (fCurrVertex > 0) {
71 // setup our sampler state for our text texture/atlas
72 SkASSERT(GrIsALIGN4(fCurrVertex));
73 SkASSERT(fCurrTexture);
74 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kNone_FilterMode);
75
76 // This effect could be stored with one of the cache objects (atlas?)
77 drawState->addCoverageEffect(
78 GrCustomCoordsTextureEffect::Create(fCurrTexture, params),
79 kGlyphCoordsAttributeIndex)->unref();
80
commit-bot@chromium.orgf8cb1842013-12-03 19:45:22 +000081 if (NULL != fStrike && kARGB_GrMaskFormat == fStrike->getMaskFormat()) {
82 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
83 drawState->setColor(0xffffffff);
84 } else if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000085 if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
86 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
87 fPaint.numColorStages()) {
88 GrPrintf("LCD Text will not draw correctly.\n");
89 }
commit-bot@chromium.org42a89572013-10-28 15:13:50 +000090 // We don't use the GrPaint's color in this case because it's been premultiplied by
91 // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by
skia.committer@gmail.com70402c32013-10-29 07:01:50 +000092 // the mask texture color. The end result is that we get
commit-bot@chromium.org42a89572013-10-28 15:13:50 +000093 // mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor
commit-bot@chromium.orgcc40f062014-01-24 14:38:27 +000094 int a = SkColorGetA(fSkPaint.getColor());
commit-bot@chromium.org42a89572013-10-28 15:13:50 +000095 // paintAlpha
96 drawState->setColor(SkColorSetARGB(a, a, a, a));
97 // paintColor
commit-bot@chromium.orgcc40f062014-01-24 14:38:27 +000098 drawState->setBlendConstant(skcolor_to_grcolor_nopremultiply(fSkPaint.getColor()));
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000099 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000100 } else {
101 // set back to normal in case we took LCD path previously.
102 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
103 drawState->setColor(fPaint.getColor());
104 }
105
106 int nGlyphs = fCurrVertex / 4;
107 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
108 fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
109 nGlyphs,
commit-bot@chromium.org3ae0e6c2014-02-11 18:24:25 +0000110 4, 6, &fVertexBounds);
commit-bot@chromium.org42a89572013-10-28 15:13:50 +0000111
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000112 fDrawTarget->resetVertexSource();
113 fVertices = NULL;
114 fMaxVertices = 0;
115 fCurrVertex = 0;
commit-bot@chromium.org3ae0e6c2014-02-11 18:24:25 +0000116 fVertexBounds.setLargestInverted();
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000117 SkSafeSetNull(fCurrTexture);
118 }
119}
120
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000121inline void GrBitmapTextContext::init(const GrPaint& paint, const SkPaint& skPaint) {
122 GrTextContext::init(paint, skPaint);
123
124 fStrike = NULL;
125
126 fCurrTexture = NULL;
127 fCurrVertex = 0;
128
129 fVertices = NULL;
130 fMaxVertices = 0;
131}
132
133inline void GrBitmapTextContext::finish() {
134 flushGlyphs();
135
136 GrTextContext::finish();
137}
138
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000139void GrBitmapTextContext::drawText(const GrPaint& paint, const SkPaint& skPaint,
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000140 const char text[], size_t byteLength,
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000141 SkScalar x, SkScalar y) {
142 SkASSERT(byteLength == 0 || text != NULL);
143
144 // nothing to draw
145 if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) {
146 return;
147 }
148
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000149 this->init(paint, skPaint);
150
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000151 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
152
153 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, &fContext->getMatrix());
154 SkGlyphCache* cache = autoCache.getCache();
155 GrFontScaler* fontScaler = GetGrFontScaler(cache);
156
157 // transform our starting point
158 {
159 SkPoint loc;
160 fContext->getMatrix().mapXY(x, y, &loc);
161 x = loc.fX;
162 y = loc.fY;
163 }
164
165 // need to measure first
166 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
167 SkVector stop;
168
169 MeasureText(cache, glyphCacheProc, text, byteLength, &stop);
170
171 SkScalar stopX = stop.fX;
172 SkScalar stopY = stop.fY;
173
174 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
175 stopX = SkScalarHalf(stopX);
176 stopY = SkScalarHalf(stopY);
177 }
178 x -= stopX;
179 y -= stopY;
180 }
181
182 const char* stop = text + byteLength;
183
184 SkAutoKern autokern;
185
186 SkFixed fxMask = ~0;
187 SkFixed fyMask = ~0;
188 SkFixed halfSampleX, halfSampleY;
189 if (cache->isSubpixel()) {
190 halfSampleX = halfSampleY = (SK_FixedHalf >> SkGlyph::kSubBits);
191 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(fContext->getMatrix());
192 if (kX_SkAxisAlignment == baseline) {
193 fyMask = 0;
194 halfSampleY = SK_FixedHalf;
195 } else if (kY_SkAxisAlignment == baseline) {
196 fxMask = 0;
197 halfSampleX = SK_FixedHalf;
198 }
199 } else {
200 halfSampleX = halfSampleY = SK_FixedHalf;
201 }
202
203 SkFixed fx = SkScalarToFixed(x) + halfSampleX;
204 SkFixed fy = SkScalarToFixed(y) + halfSampleY;
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000205
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000206 GrContext::AutoMatrix autoMatrix;
207 autoMatrix.setIdentity(fContext, &fPaint);
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000208
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000209 while (text < stop) {
210 const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
211
212 fx += autokern.adjust(glyph);
213
214 if (glyph.fWidth) {
215 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
216 glyph.getSubXFixed(),
217 glyph.getSubYFixed()),
218 SkFixedFloorToFixed(fx),
219 SkFixedFloorToFixed(fy),
220 fontScaler);
221 }
222
223 fx += glyph.fAdvanceX;
224 fy += glyph.fAdvanceY;
225 }
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000226
227 this->finish();
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000228}
229
230///////////////////////////////////////////////////////////////////////////////
231// Copied from SkDraw
232
233// last parameter is interpreted as SkFixed [x, y]
234// return the fixed position, which may be rounded or not by the caller
235// e.g. subpixel doesn't round
236typedef void (*AlignProc)(const SkPoint&, const SkGlyph&, SkIPoint*);
237
238static void leftAlignProc(const SkPoint& loc, const SkGlyph& glyph, SkIPoint* dst) {
239 dst->set(SkScalarToFixed(loc.fX), SkScalarToFixed(loc.fY));
240}
241
242static void centerAlignProc(const SkPoint& loc, const SkGlyph& glyph, SkIPoint* dst) {
243 dst->set(SkScalarToFixed(loc.fX) - (glyph.fAdvanceX >> 1),
244 SkScalarToFixed(loc.fY) - (glyph.fAdvanceY >> 1));
245}
246
247static void rightAlignProc(const SkPoint& loc, const SkGlyph& glyph, SkIPoint* dst) {
248 dst->set(SkScalarToFixed(loc.fX) - glyph.fAdvanceX,
249 SkScalarToFixed(loc.fY) - glyph.fAdvanceY);
250}
251
252static AlignProc pick_align_proc(SkPaint::Align align) {
253 static const AlignProc gProcs[] = {
254 leftAlignProc, centerAlignProc, rightAlignProc
255 };
256
257 SkASSERT((unsigned)align < SK_ARRAY_COUNT(gProcs));
258
259 return gProcs[align];
260}
261
262class BitmapTextMapState {
263public:
264 mutable SkPoint fLoc;
265
266 BitmapTextMapState(const SkMatrix& matrix, SkScalar y)
267 : fMatrix(matrix), fProc(matrix.getMapXYProc()), fY(y) {}
268
269 typedef void (*Proc)(const BitmapTextMapState&, const SkScalar pos[]);
270
271 Proc pickProc(int scalarsPerPosition);
272
273private:
274 const SkMatrix& fMatrix;
275 SkMatrix::MapXYProc fProc;
276 SkScalar fY; // ignored by MapXYProc
277 // these are only used by Only... procs
278 SkScalar fScaleX, fTransX, fTransformedY;
279
280 static void MapXProc(const BitmapTextMapState& state, const SkScalar pos[]) {
281 state.fProc(state.fMatrix, *pos, state.fY, &state.fLoc);
282 }
283
284 static void MapXYProc(const BitmapTextMapState& state, const SkScalar pos[]) {
285 state.fProc(state.fMatrix, pos[0], pos[1], &state.fLoc);
286 }
287
288 static void MapOnlyScaleXProc(const BitmapTextMapState& state,
289 const SkScalar pos[]) {
290 state.fLoc.set(SkScalarMul(state.fScaleX, *pos) + state.fTransX,
291 state.fTransformedY);
292 }
293
294 static void MapOnlyTransXProc(const BitmapTextMapState& state,
295 const SkScalar pos[]) {
296 state.fLoc.set(*pos + state.fTransX, state.fTransformedY);
297 }
298};
299
300BitmapTextMapState::Proc BitmapTextMapState::pickProc(int scalarsPerPosition) {
301 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
302
303 if (1 == scalarsPerPosition) {
304 unsigned mtype = fMatrix.getType();
305 if (mtype & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)) {
306 return MapXProc;
307 } else {
308 fScaleX = fMatrix.getScaleX();
309 fTransX = fMatrix.getTranslateX();
310 fTransformedY = SkScalarMul(fY, fMatrix.getScaleY()) +
311 fMatrix.getTranslateY();
312 return (mtype & SkMatrix::kScale_Mask) ?
313 MapOnlyScaleXProc : MapOnlyTransXProc;
314 }
315 } else {
316 return MapXYProc;
317 }
318}
319
320///////////////////////////////////////////////////////////////////////////////
321
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000322void GrBitmapTextContext::drawPosText(const GrPaint& paint, const SkPaint& skPaint,
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000323 const char text[], size_t byteLength,
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000324 const SkScalar pos[], SkScalar constY,
325 int scalarsPerPosition) {
326 SkASSERT(byteLength == 0 || text != NULL);
327 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
328
329 // nothing to draw
330 if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) {
331 return;
332 }
333
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000334 this->init(paint, skPaint);
335
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000336 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
337
338 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, &fContext->getMatrix());
339 SkGlyphCache* cache = autoCache.getCache();
340 GrFontScaler* fontScaler = GetGrFontScaler(cache);
341
342 // store original matrix before we reset, so we can use it to transform positions
343 SkMatrix ctm = fContext->getMatrix();
344 GrContext::AutoMatrix autoMatrix;
345 autoMatrix.setIdentity(fContext, &fPaint);
346
347 const char* stop = text + byteLength;
348 AlignProc alignProc = pick_align_proc(fSkPaint.getTextAlign());
349 BitmapTextMapState tms(ctm, constY);
350 BitmapTextMapState::Proc tmsProc = tms.pickProc(scalarsPerPosition);
351 SkFixed halfSampleX = 0, halfSampleY = 0;
352
353 if (cache->isSubpixel()) {
354 // maybe we should skip the rounding if linearText is set
355 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(ctm);
356
357 SkFixed fxMask = ~0;
358 SkFixed fyMask = ~0;
359 if (kX_SkAxisAlignment == baseline) {
360 fyMask = 0;
361#ifndef SK_IGNORE_SUBPIXEL_AXIS_ALIGN_FIX
362 halfSampleY = SK_FixedHalf;
363#endif
364 } else if (kY_SkAxisAlignment == baseline) {
365 fxMask = 0;
366#ifndef SK_IGNORE_SUBPIXEL_AXIS_ALIGN_FIX
367 halfSampleX = SK_FixedHalf;
368#endif
369 }
370
371 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
372 while (text < stop) {
373 tmsProc(tms, pos);
374 SkFixed fx = SkScalarToFixed(tms.fLoc.fX) + halfSampleX;
375 SkFixed fy = SkScalarToFixed(tms.fLoc.fY) + halfSampleY;
376
377 const SkGlyph& glyph = glyphCacheProc(cache, &text,
378 fx & fxMask, fy & fyMask);
379
380 if (glyph.fWidth) {
381 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
382 glyph.getSubXFixed(),
383 glyph.getSubYFixed()),
384 SkFixedFloorToFixed(fx),
385 SkFixedFloorToFixed(fy),
386 fontScaler);
387 }
388 pos += scalarsPerPosition;
389 }
390 } else {
391 while (text < stop) {
392 const char* currentText = text;
393 const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
394
395 if (metricGlyph.fWidth) {
396 SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
397 SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
398
399 tmsProc(tms, pos);
400 SkIPoint fixedLoc;
401 alignProc(tms.fLoc, metricGlyph, &fixedLoc);
402
403 SkFixed fx = fixedLoc.fX + halfSampleX;
404 SkFixed fy = fixedLoc.fY + halfSampleY;
405
406 // have to call again, now that we've been "aligned"
407 const SkGlyph& glyph = glyphCacheProc(cache, &currentText,
408 fx & fxMask, fy & fyMask);
409 // the assumption is that the metrics haven't changed
410 SkASSERT(prevAdvX == glyph.fAdvanceX);
411 SkASSERT(prevAdvY == glyph.fAdvanceY);
412 SkASSERT(glyph.fWidth);
413
414 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
415 glyph.getSubXFixed(),
416 glyph.getSubYFixed()),
417 SkFixedFloorToFixed(fx),
418 SkFixedFloorToFixed(fy),
419 fontScaler);
420 }
421 pos += scalarsPerPosition;
422 }
423 }
424 } else { // not subpixel
425
426 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
427 while (text < stop) {
428 // the last 2 parameters are ignored
429 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
430
431 if (glyph.fWidth) {
432 tmsProc(tms, pos);
433
434 SkFixed fx = SkScalarToFixed(tms.fLoc.fX) + SK_FixedHalf; //halfSampleX;
435 SkFixed fy = SkScalarToFixed(tms.fLoc.fY) + SK_FixedHalf; //halfSampleY;
436 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
437 glyph.getSubXFixed(),
438 glyph.getSubYFixed()),
439 SkFixedFloorToFixed(fx),
440 SkFixedFloorToFixed(fy),
441 fontScaler);
442 }
443 pos += scalarsPerPosition;
444 }
445 } else {
446 while (text < stop) {
447 // the last 2 parameters are ignored
448 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
449
450 if (glyph.fWidth) {
451 tmsProc(tms, pos);
452
453 SkIPoint fixedLoc;
454 alignProc(tms.fLoc, glyph, &fixedLoc);
455
456 SkFixed fx = fixedLoc.fX + SK_FixedHalf; //halfSampleX;
457 SkFixed fy = fixedLoc.fY + SK_FixedHalf; //halfSampleY;
458 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
459 glyph.getSubXFixed(),
460 glyph.getSubYFixed()),
461 SkFixedFloorToFixed(fx),
462 SkFixedFloorToFixed(fy),
463 fontScaler);
464 }
465 pos += scalarsPerPosition;
466 }
467 }
468 }
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000469
470 this->finish();
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000471}
472
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000473namespace {
474
475// position + texture coord
476extern const GrVertexAttrib gTextVertexAttribs[] = {
477 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
478 {kVec2f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}
479};
480
481};
482
483void GrBitmapTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
484 GrFixed vx, GrFixed vy,
485 GrFontScaler* scaler) {
486 if (NULL == fDrawTarget) {
487 return;
488 }
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000489
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000490 if (NULL == fStrike) {
jvanverth@google.comd830d132013-11-11 20:54:09 +0000491#if SK_DISTANCEFIELD_FONTS
commit-bot@chromium.org75a22952013-11-21 15:09:33 +0000492 fStrike = fContext->getFontCache()->getStrike(scaler, false);
jvanverth@google.comd830d132013-11-11 20:54:09 +0000493#else
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000494 fStrike = fContext->getFontCache()->getStrike(scaler);
jvanverth@google.comd830d132013-11-11 20:54:09 +0000495#endif
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000496 }
497
498 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
499 if (NULL == glyph || glyph->fBounds.isEmpty()) {
500 return;
501 }
502
503 vx += SkIntToFixed(glyph->fBounds.fLeft);
504 vy += SkIntToFixed(glyph->fBounds.fTop);
505
506 // keep them as ints until we've done the clip-test
507 GrFixed width = glyph->fBounds.width();
508 GrFixed height = glyph->fBounds.height();
509
510 // check if we clipped out
511 if (true || NULL == glyph->fPlot) {
512 int x = vx >> 16;
513 int y = vy >> 16;
514 if (fClipRect.quickReject(x, y, x + width, y + height)) {
515// SkCLZ(3); // so we can set a break-point in the debugger
516 return;
517 }
518 }
519
520 if (NULL == glyph->fPlot) {
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000521 if (fStrike->addGlyphToAtlas(glyph, scaler)) {
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000522 goto HAS_ATLAS;
523 }
524
525 // try to clear out an unused plot before we flush
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000526 if (fContext->getFontCache()->freeUnusedPlot(fStrike) &&
527 fStrike->addGlyphToAtlas(glyph, scaler)) {
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000528 goto HAS_ATLAS;
529 }
530
531 if (c_DumpFontCache) {
532#ifdef SK_DEVELOPER
533 fContext->getFontCache()->dump();
534#endif
535 }
536
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000537 // flush any accumulated draws to allow us to free up a plot
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000538 this->flushGlyphs();
539 fContext->flush();
540
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000541 // we should have an unused plot now
542 if (fContext->getFontCache()->freeUnusedPlot(fStrike) &&
543 fStrike->addGlyphToAtlas(glyph, scaler)) {
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000544 goto HAS_ATLAS;
545 }
546
547 if (NULL == glyph->fPath) {
548 SkPath* path = SkNEW(SkPath);
549 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
550 // flag the glyph as being dead?
551 delete path;
552 return;
553 }
554 glyph->fPath = path;
555 }
556
557 GrContext::AutoMatrix am;
558 SkMatrix translate;
559 translate.setTranslate(SkFixedToScalar(vx - SkIntToFixed(glyph->fBounds.fLeft)),
560 SkFixedToScalar(vy - SkIntToFixed(glyph->fBounds.fTop)));
561 GrPaint tmpPaint(fPaint);
562 am.setPreConcat(fContext, translate, &tmpPaint);
563 SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
564 fContext->drawPath(tmpPaint, *glyph->fPath, stroke);
565 return;
566 }
567
568HAS_ATLAS:
569 SkASSERT(glyph->fPlot);
570 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
571 glyph->fPlot->setDrawToken(drawToken);
572
573 // now promote them to fixed (TODO: Rethink using fixed pt).
574 width = SkIntToFixed(width);
575 height = SkIntToFixed(height);
576
577 GrTexture* texture = glyph->fPlot->texture();
578 SkASSERT(texture);
579
580 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
581 this->flushGlyphs();
582 fCurrTexture = texture;
583 fCurrTexture->ref();
584 }
585
586 if (NULL == fVertices) {
587 // If we need to reserve vertices allow the draw target to suggest
588 // a number of verts to reserve and whether to perform a flush.
589 fMaxVertices = kMinRequestedVerts;
590 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
591 SK_ARRAY_COUNT(gTextVertexAttribs));
592 bool flush = fDrawTarget->geometryHints(&fMaxVertices, NULL);
593 if (flush) {
594 this->flushGlyphs();
595 fContext->flush();
596 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
597 SK_ARRAY_COUNT(gTextVertexAttribs));
598 }
599 fMaxVertices = kDefaultRequestedVerts;
600 // ignore return, no point in flushing again.
601 fDrawTarget->geometryHints(&fMaxVertices, NULL);
602
603 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
604 if (fMaxVertices < kMinRequestedVerts) {
605 fMaxVertices = kDefaultRequestedVerts;
606 } else if (fMaxVertices > maxQuadVertices) {
607 // don't exceed the limit of the index buffer
608 fMaxVertices = maxQuadVertices;
609 }
610 bool success = fDrawTarget->reserveVertexAndIndexSpace(fMaxVertices,
611 0,
612 GrTCast<void**>(&fVertices),
613 NULL);
614 GrAlwaysAssert(success);
615 SkASSERT(2*sizeof(GrPoint) == fDrawTarget->getDrawState().getVertexSize());
616 }
617
618 GrFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX);
619 GrFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY);
620
commit-bot@chromium.org3ae0e6c2014-02-11 18:24:25 +0000621 SkRect r;
622 r.fLeft = SkFixedToFloat(vx);
623 r.fTop = SkFixedToFloat(vy);
624 r.fRight = SkFixedToFloat(vx + width);
625 r.fBottom = SkFixedToFloat(vy + height);
626
627 fVertexBounds.growToInclude(r);
628
629 fVertices[2*fCurrVertex].setRectFan(r.fLeft, r.fTop, r.fRight, r.fBottom,
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000630 2 * sizeof(SkPoint));
631 fVertices[2*fCurrVertex+1].setRectFan(SkFixedToFloat(texture->normalizeFixedX(tx)),
632 SkFixedToFloat(texture->normalizeFixedY(ty)),
633 SkFixedToFloat(texture->normalizeFixedX(tx + width)),
634 SkFixedToFloat(texture->normalizeFixedY(ty + height)),
635 2 * sizeof(SkPoint));
636 fCurrVertex += 4;
637}