blob: 744d97f4268a9e6c75769b3a8e4abb791b2f26fb [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) {
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +000071 fContext->getFontCache()->updateTextures();
72
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000073 // setup our sampler state for our text texture/atlas
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000074 SkASSERT(SkIsAlign4(fCurrVertex));
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000075 SkASSERT(fCurrTexture);
76 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kNone_FilterMode);
77
78 // This effect could be stored with one of the cache objects (atlas?)
79 drawState->addCoverageEffect(
80 GrCustomCoordsTextureEffect::Create(fCurrTexture, params),
81 kGlyphCoordsAttributeIndex)->unref();
82
commit-bot@chromium.orgf8cb1842013-12-03 19:45:22 +000083 if (NULL != fStrike && kARGB_GrMaskFormat == fStrike->getMaskFormat()) {
84 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
85 drawState->setColor(0xffffffff);
86 } else if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000087 if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
88 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
89 fPaint.numColorStages()) {
90 GrPrintf("LCD Text will not draw correctly.\n");
91 }
commit-bot@chromium.org42a89572013-10-28 15:13:50 +000092 // We don't use the GrPaint's color in this case because it's been premultiplied by
93 // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by
skia.committer@gmail.com70402c32013-10-29 07:01:50 +000094 // the mask texture color. The end result is that we get
commit-bot@chromium.org42a89572013-10-28 15:13:50 +000095 // mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor
commit-bot@chromium.orgcc40f062014-01-24 14:38:27 +000096 int a = SkColorGetA(fSkPaint.getColor());
commit-bot@chromium.org42a89572013-10-28 15:13:50 +000097 // paintAlpha
98 drawState->setColor(SkColorSetARGB(a, a, a, a));
99 // paintColor
commit-bot@chromium.orgcc40f062014-01-24 14:38:27 +0000100 drawState->setBlendConstant(skcolor_to_grcolor_nopremultiply(fSkPaint.getColor()));
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000101 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000102 } else {
103 // set back to normal in case we took LCD path previously.
104 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
105 drawState->setColor(fPaint.getColor());
106 }
107
108 int nGlyphs = fCurrVertex / 4;
109 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
110 fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
111 nGlyphs,
commit-bot@chromium.org3ae0e6c2014-02-11 18:24:25 +0000112 4, 6, &fVertexBounds);
commit-bot@chromium.org42a89572013-10-28 15:13:50 +0000113
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000114 fDrawTarget->resetVertexSource();
115 fVertices = NULL;
116 fMaxVertices = 0;
117 fCurrVertex = 0;
commit-bot@chromium.org3ae0e6c2014-02-11 18:24:25 +0000118 fVertexBounds.setLargestInverted();
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000119 SkSafeSetNull(fCurrTexture);
120 }
121}
122
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000123inline void GrBitmapTextContext::init(const GrPaint& paint, const SkPaint& skPaint) {
124 GrTextContext::init(paint, skPaint);
125
126 fStrike = NULL;
127
128 fCurrTexture = NULL;
129 fCurrVertex = 0;
130
131 fVertices = NULL;
132 fMaxVertices = 0;
133}
134
135inline void GrBitmapTextContext::finish() {
136 flushGlyphs();
137
138 GrTextContext::finish();
139}
140
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000141void GrBitmapTextContext::drawText(const GrPaint& paint, const SkPaint& skPaint,
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000142 const char text[], size_t byteLength,
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000143 SkScalar x, SkScalar y) {
144 SkASSERT(byteLength == 0 || text != NULL);
145
146 // nothing to draw
147 if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) {
148 return;
149 }
150
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000151 this->init(paint, skPaint);
152
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000153 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
154
155 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, &fContext->getMatrix());
156 SkGlyphCache* cache = autoCache.getCache();
157 GrFontScaler* fontScaler = GetGrFontScaler(cache);
158
159 // transform our starting point
160 {
161 SkPoint loc;
162 fContext->getMatrix().mapXY(x, y, &loc);
163 x = loc.fX;
164 y = loc.fY;
165 }
166
167 // need to measure first
168 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
169 SkVector stop;
170
171 MeasureText(cache, glyphCacheProc, text, byteLength, &stop);
172
173 SkScalar stopX = stop.fX;
174 SkScalar stopY = stop.fY;
175
176 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
177 stopX = SkScalarHalf(stopX);
178 stopY = SkScalarHalf(stopY);
179 }
180 x -= stopX;
181 y -= stopY;
182 }
183
184 const char* stop = text + byteLength;
185
186 SkAutoKern autokern;
187
188 SkFixed fxMask = ~0;
189 SkFixed fyMask = ~0;
190 SkFixed halfSampleX, halfSampleY;
191 if (cache->isSubpixel()) {
192 halfSampleX = halfSampleY = (SK_FixedHalf >> SkGlyph::kSubBits);
193 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(fContext->getMatrix());
194 if (kX_SkAxisAlignment == baseline) {
195 fyMask = 0;
196 halfSampleY = SK_FixedHalf;
197 } else if (kY_SkAxisAlignment == baseline) {
198 fxMask = 0;
199 halfSampleX = SK_FixedHalf;
200 }
201 } else {
202 halfSampleX = halfSampleY = SK_FixedHalf;
203 }
204
205 SkFixed fx = SkScalarToFixed(x) + halfSampleX;
206 SkFixed fy = SkScalarToFixed(y) + halfSampleY;
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000207
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000208 GrContext::AutoMatrix autoMatrix;
209 autoMatrix.setIdentity(fContext, &fPaint);
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000210
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000211 while (text < stop) {
212 const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
213
214 fx += autokern.adjust(glyph);
215
216 if (glyph.fWidth) {
217 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
218 glyph.getSubXFixed(),
219 glyph.getSubYFixed()),
220 SkFixedFloorToFixed(fx),
221 SkFixedFloorToFixed(fy),
222 fontScaler);
223 }
224
225 fx += glyph.fAdvanceX;
226 fy += glyph.fAdvanceY;
227 }
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000228
229 this->finish();
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000230}
231
232///////////////////////////////////////////////////////////////////////////////
233// Copied from SkDraw
234
235// last parameter is interpreted as SkFixed [x, y]
236// return the fixed position, which may be rounded or not by the caller
237// e.g. subpixel doesn't round
238typedef void (*AlignProc)(const SkPoint&, const SkGlyph&, SkIPoint*);
239
240static void leftAlignProc(const SkPoint& loc, const SkGlyph& glyph, SkIPoint* dst) {
241 dst->set(SkScalarToFixed(loc.fX), SkScalarToFixed(loc.fY));
242}
243
244static void centerAlignProc(const SkPoint& loc, const SkGlyph& glyph, SkIPoint* dst) {
245 dst->set(SkScalarToFixed(loc.fX) - (glyph.fAdvanceX >> 1),
246 SkScalarToFixed(loc.fY) - (glyph.fAdvanceY >> 1));
247}
248
249static void rightAlignProc(const SkPoint& loc, const SkGlyph& glyph, SkIPoint* dst) {
250 dst->set(SkScalarToFixed(loc.fX) - glyph.fAdvanceX,
251 SkScalarToFixed(loc.fY) - glyph.fAdvanceY);
252}
253
254static AlignProc pick_align_proc(SkPaint::Align align) {
255 static const AlignProc gProcs[] = {
256 leftAlignProc, centerAlignProc, rightAlignProc
257 };
258
259 SkASSERT((unsigned)align < SK_ARRAY_COUNT(gProcs));
260
261 return gProcs[align];
262}
263
264class BitmapTextMapState {
265public:
266 mutable SkPoint fLoc;
267
268 BitmapTextMapState(const SkMatrix& matrix, SkScalar y)
269 : fMatrix(matrix), fProc(matrix.getMapXYProc()), fY(y) {}
270
271 typedef void (*Proc)(const BitmapTextMapState&, const SkScalar pos[]);
272
273 Proc pickProc(int scalarsPerPosition);
274
275private:
276 const SkMatrix& fMatrix;
277 SkMatrix::MapXYProc fProc;
278 SkScalar fY; // ignored by MapXYProc
279 // these are only used by Only... procs
280 SkScalar fScaleX, fTransX, fTransformedY;
281
282 static void MapXProc(const BitmapTextMapState& state, const SkScalar pos[]) {
283 state.fProc(state.fMatrix, *pos, state.fY, &state.fLoc);
284 }
285
286 static void MapXYProc(const BitmapTextMapState& state, const SkScalar pos[]) {
287 state.fProc(state.fMatrix, pos[0], pos[1], &state.fLoc);
288 }
289
290 static void MapOnlyScaleXProc(const BitmapTextMapState& state,
291 const SkScalar pos[]) {
292 state.fLoc.set(SkScalarMul(state.fScaleX, *pos) + state.fTransX,
293 state.fTransformedY);
294 }
295
296 static void MapOnlyTransXProc(const BitmapTextMapState& state,
297 const SkScalar pos[]) {
298 state.fLoc.set(*pos + state.fTransX, state.fTransformedY);
299 }
300};
301
302BitmapTextMapState::Proc BitmapTextMapState::pickProc(int scalarsPerPosition) {
303 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
304
305 if (1 == scalarsPerPosition) {
306 unsigned mtype = fMatrix.getType();
307 if (mtype & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)) {
308 return MapXProc;
309 } else {
310 fScaleX = fMatrix.getScaleX();
311 fTransX = fMatrix.getTranslateX();
312 fTransformedY = SkScalarMul(fY, fMatrix.getScaleY()) +
313 fMatrix.getTranslateY();
314 return (mtype & SkMatrix::kScale_Mask) ?
315 MapOnlyScaleXProc : MapOnlyTransXProc;
316 }
317 } else {
318 return MapXYProc;
319 }
320}
321
322///////////////////////////////////////////////////////////////////////////////
323
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000324void GrBitmapTextContext::drawPosText(const GrPaint& paint, const SkPaint& skPaint,
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000325 const char text[], size_t byteLength,
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000326 const SkScalar pos[], SkScalar constY,
327 int scalarsPerPosition) {
328 SkASSERT(byteLength == 0 || text != NULL);
329 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
330
331 // nothing to draw
332 if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) {
333 return;
334 }
335
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000336 this->init(paint, skPaint);
337
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000338 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
339
340 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, &fContext->getMatrix());
341 SkGlyphCache* cache = autoCache.getCache();
342 GrFontScaler* fontScaler = GetGrFontScaler(cache);
343
344 // store original matrix before we reset, so we can use it to transform positions
345 SkMatrix ctm = fContext->getMatrix();
346 GrContext::AutoMatrix autoMatrix;
347 autoMatrix.setIdentity(fContext, &fPaint);
348
349 const char* stop = text + byteLength;
350 AlignProc alignProc = pick_align_proc(fSkPaint.getTextAlign());
351 BitmapTextMapState tms(ctm, constY);
352 BitmapTextMapState::Proc tmsProc = tms.pickProc(scalarsPerPosition);
353 SkFixed halfSampleX = 0, halfSampleY = 0;
354
355 if (cache->isSubpixel()) {
356 // maybe we should skip the rounding if linearText is set
357 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(ctm);
358
359 SkFixed fxMask = ~0;
360 SkFixed fyMask = ~0;
361 if (kX_SkAxisAlignment == baseline) {
362 fyMask = 0;
363#ifndef SK_IGNORE_SUBPIXEL_AXIS_ALIGN_FIX
364 halfSampleY = SK_FixedHalf;
365#endif
366 } else if (kY_SkAxisAlignment == baseline) {
367 fxMask = 0;
368#ifndef SK_IGNORE_SUBPIXEL_AXIS_ALIGN_FIX
369 halfSampleX = SK_FixedHalf;
370#endif
371 }
372
373 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
374 while (text < stop) {
375 tmsProc(tms, pos);
376 SkFixed fx = SkScalarToFixed(tms.fLoc.fX) + halfSampleX;
377 SkFixed fy = SkScalarToFixed(tms.fLoc.fY) + halfSampleY;
378
379 const SkGlyph& glyph = glyphCacheProc(cache, &text,
380 fx & fxMask, fy & fyMask);
381
382 if (glyph.fWidth) {
383 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
384 glyph.getSubXFixed(),
385 glyph.getSubYFixed()),
386 SkFixedFloorToFixed(fx),
387 SkFixedFloorToFixed(fy),
388 fontScaler);
389 }
390 pos += scalarsPerPosition;
391 }
392 } else {
393 while (text < stop) {
394 const char* currentText = text;
395 const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
396
397 if (metricGlyph.fWidth) {
398 SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
399 SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
400
401 tmsProc(tms, pos);
402 SkIPoint fixedLoc;
403 alignProc(tms.fLoc, metricGlyph, &fixedLoc);
404
405 SkFixed fx = fixedLoc.fX + halfSampleX;
406 SkFixed fy = fixedLoc.fY + halfSampleY;
407
408 // have to call again, now that we've been "aligned"
409 const SkGlyph& glyph = glyphCacheProc(cache, &currentText,
410 fx & fxMask, fy & fyMask);
411 // the assumption is that the metrics haven't changed
412 SkASSERT(prevAdvX == glyph.fAdvanceX);
413 SkASSERT(prevAdvY == glyph.fAdvanceY);
414 SkASSERT(glyph.fWidth);
415
416 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
417 glyph.getSubXFixed(),
418 glyph.getSubYFixed()),
419 SkFixedFloorToFixed(fx),
420 SkFixedFloorToFixed(fy),
421 fontScaler);
422 }
423 pos += scalarsPerPosition;
424 }
425 }
426 } else { // not subpixel
427
428 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
429 while (text < stop) {
430 // the last 2 parameters are ignored
431 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
432
433 if (glyph.fWidth) {
434 tmsProc(tms, pos);
435
436 SkFixed fx = SkScalarToFixed(tms.fLoc.fX) + SK_FixedHalf; //halfSampleX;
437 SkFixed fy = SkScalarToFixed(tms.fLoc.fY) + SK_FixedHalf; //halfSampleY;
438 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
439 glyph.getSubXFixed(),
440 glyph.getSubYFixed()),
441 SkFixedFloorToFixed(fx),
442 SkFixedFloorToFixed(fy),
443 fontScaler);
444 }
445 pos += scalarsPerPosition;
446 }
447 } else {
448 while (text < stop) {
449 // the last 2 parameters are ignored
450 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
451
452 if (glyph.fWidth) {
453 tmsProc(tms, pos);
454
455 SkIPoint fixedLoc;
456 alignProc(tms.fLoc, glyph, &fixedLoc);
457
458 SkFixed fx = fixedLoc.fX + SK_FixedHalf; //halfSampleX;
459 SkFixed fy = fixedLoc.fY + SK_FixedHalf; //halfSampleY;
460 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
461 glyph.getSubXFixed(),
462 glyph.getSubYFixed()),
463 SkFixedFloorToFixed(fx),
464 SkFixedFloorToFixed(fy),
465 fontScaler);
466 }
467 pos += scalarsPerPosition;
468 }
469 }
470 }
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000471
472 this->finish();
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000473}
474
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000475namespace {
476
477// position + texture coord
478extern const GrVertexAttrib gTextVertexAttribs[] = {
479 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000480 {kVec2f_GrVertexAttribType, sizeof(SkPoint), kEffect_GrVertexAttribBinding}
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000481};
482
483};
484
485void GrBitmapTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000486 SkFixed vx, SkFixed vy,
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000487 GrFontScaler* scaler) {
488 if (NULL == fDrawTarget) {
489 return;
490 }
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000491
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000492 if (NULL == fStrike) {
commit-bot@chromium.org75a22952013-11-21 15:09:33 +0000493 fStrike = fContext->getFontCache()->getStrike(scaler, false);
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000494 }
495
496 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
497 if (NULL == glyph || glyph->fBounds.isEmpty()) {
498 return;
499 }
500
501 vx += SkIntToFixed(glyph->fBounds.fLeft);
502 vy += SkIntToFixed(glyph->fBounds.fTop);
503
504 // keep them as ints until we've done the clip-test
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000505 SkFixed width = glyph->fBounds.width();
506 SkFixed height = glyph->fBounds.height();
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000507
508 // check if we clipped out
509 if (true || NULL == glyph->fPlot) {
510 int x = vx >> 16;
511 int y = vy >> 16;
512 if (fClipRect.quickReject(x, y, x + width, y + height)) {
513// SkCLZ(3); // so we can set a break-point in the debugger
514 return;
515 }
516 }
517
518 if (NULL == glyph->fPlot) {
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000519 if (fStrike->addGlyphToAtlas(glyph, scaler)) {
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000520 goto HAS_ATLAS;
521 }
522
523 // try to clear out an unused plot before we flush
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000524 if (fContext->getFontCache()->freeUnusedPlot(fStrike) &&
525 fStrike->addGlyphToAtlas(glyph, scaler)) {
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000526 goto HAS_ATLAS;
527 }
528
529 if (c_DumpFontCache) {
530#ifdef SK_DEVELOPER
531 fContext->getFontCache()->dump();
532#endif
533 }
534
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000535 // flush any accumulated draws to allow us to free up a plot
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000536 this->flushGlyphs();
537 fContext->flush();
538
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000539 // we should have an unused plot now
540 if (fContext->getFontCache()->freeUnusedPlot(fStrike) &&
541 fStrike->addGlyphToAtlas(glyph, scaler)) {
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000542 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);
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000613 SkASSERT(2*sizeof(SkPoint) == fDrawTarget->getDrawState().getVertexSize());
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000614 }
615
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000616 SkFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX);
617 SkFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY);
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000618
commit-bot@chromium.org3ae0e6c2014-02-11 18:24:25 +0000619 SkRect r;
620 r.fLeft = SkFixedToFloat(vx);
621 r.fTop = SkFixedToFloat(vy);
622 r.fRight = SkFixedToFloat(vx + width);
623 r.fBottom = SkFixedToFloat(vy + height);
624
625 fVertexBounds.growToInclude(r);
626
627 fVertices[2*fCurrVertex].setRectFan(r.fLeft, r.fTop, r.fRight, r.fBottom,
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000628 2 * sizeof(SkPoint));
629 fVertices[2*fCurrVertex+1].setRectFan(SkFixedToFloat(texture->normalizeFixedX(tx)),
630 SkFixedToFloat(texture->normalizeFixedY(ty)),
631 SkFixedToFloat(texture->normalizeFixedX(tx + width)),
632 SkFixedToFloat(texture->normalizeFixedY(ty + height)),
633 2 * sizeof(SkPoint));
634 fCurrVertex += 4;
635}