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