blob: a3e9cb3d794905ce6e673dbd21ac3453585740a5 [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 +000027SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
28 "Dump the contents of the font cache before every purge.");
29
bsalomon594069f2014-06-06 06:16:34 -070030static const int kGlyphCoordsNoColorAttributeIndex = 1;
31static const int kGlyphCoordsWithColorAttributeIndex = 2;
32
33namespace {
34// position + texture coord
35extern const GrVertexAttrib gTextVertexAttribs[] = {
36 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
37 {kVec2f_GrVertexAttribType, sizeof(SkPoint) , kEffect_GrVertexAttribBinding}
38};
39
40// position + color + texture coord
41extern const GrVertexAttrib gTextVertexWithColorAttribs[] = {
42 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
43 {kVec4ub_GrVertexAttribType, sizeof(SkPoint), kColor_GrVertexAttribBinding},
44 {kVec2f_GrVertexAttribType, sizeof(SkPoint) + sizeof(GrColor), kEffect_GrVertexAttribBinding}
45};
46
47};
48
skia.committer@gmail.come5d70152014-01-29 07:01:48 +000049GrBitmapTextContext::GrBitmapTextContext(GrContext* context,
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +000050 const SkDeviceProperties& properties)
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000051 : GrTextContext(context, properties) {
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000052 fStrike = NULL;
53
54 fCurrTexture = NULL;
55 fCurrVertex = 0;
56
57 fVertices = NULL;
58 fMaxVertices = 0;
commit-bot@chromium.org3ae0e6c2014-02-11 18:24:25 +000059
60 fVertexBounds.setLargestInverted();
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000061}
62
63GrBitmapTextContext::~GrBitmapTextContext() {
64 this->flushGlyphs();
65}
66
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000067bool GrBitmapTextContext::canDraw(const SkPaint& paint) {
68 return !SkDraw::ShouldDrawTextAsPaths(paint, fContext->getMatrix());
69}
70
commit-bot@chromium.org42a89572013-10-28 15:13:50 +000071static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
72 unsigned r = SkColorGetR(c);
73 unsigned g = SkColorGetG(c);
74 unsigned b = SkColorGetB(c);
75 return GrColorPackRGBA(r, g, b, 0xff);
76}
77
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000078void GrBitmapTextContext::flushGlyphs() {
79 if (NULL == fDrawTarget) {
80 return;
81 }
82
83 GrDrawState* drawState = fDrawTarget->drawState();
84 GrDrawState::AutoRestoreEffects are(drawState);
85 drawState->setFromPaint(fPaint, SkMatrix::I(), fContext->getRenderTarget());
86
87 if (fCurrVertex > 0) {
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +000088 fContext->getFontCache()->updateTextures();
89
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000090 // setup our sampler state for our text texture/atlas
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000091 SkASSERT(SkIsAlign4(fCurrVertex));
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000092 SkASSERT(fCurrTexture);
93 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kNone_FilterMode);
94
95 // This effect could be stored with one of the cache objects (atlas?)
bsalomon594069f2014-06-06 06:16:34 -070096 int coordsIdx = drawState->hasColorVertexAttribute() ? kGlyphCoordsWithColorAttributeIndex :
97 kGlyphCoordsNoColorAttributeIndex;
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000098 drawState->addCoverageEffect(
99 GrCustomCoordsTextureEffect::Create(fCurrTexture, params),
bsalomon594069f2014-06-06 06:16:34 -0700100 coordsIdx)->unref();
101 SkASSERT(NULL != fStrike);
102 switch (fStrike->getMaskFormat()) {
103 // Color bitmap text
104 case kARGB_GrMaskFormat:
105 SkASSERT(!drawState->hasColorVertexAttribute());
106 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
107 drawState->setColor(0xffffffff);
108 break;
109 // LCD text
110 case kA888_GrMaskFormat:
111 case kA565_GrMaskFormat: {
112 if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
113 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
114 fPaint.numColorStages()) {
115 GrPrintf("LCD Text will not draw correctly.\n");
116 }
117 SkASSERT(!drawState->hasColorVertexAttribute());
118 // We don't use the GrPaint's color in this case because it's been premultiplied by
119 // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by
120 // the mask texture color. The end result is that we get
121 // mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor
122 int a = SkColorGetA(fSkPaint.getColor());
123 // paintAlpha
124 drawState->setColor(SkColorSetARGB(a, a, a, a));
125 // paintColor
126 drawState->setBlendConstant(skcolor_to_grcolor_nopremultiply(fSkPaint.getColor()));
127 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
128 break;
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000129 }
bsalomon594069f2014-06-06 06:16:34 -0700130 // Grayscale/BW text
131 case kA8_GrMaskFormat:
132 // set back to normal in case we took LCD path previously.
133 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
134 //drawState->setColor(fPaint.getColor());
135 // We're using per-vertex color.
136 SkASSERT(drawState->hasColorVertexAttribute());
137 drawState->setColor(0xFFFFFFFF);
138 break;
139 default:
140 SkFAIL("Unexepected mask format.");
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000141 }
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000142 int nGlyphs = fCurrVertex / 4;
143 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
144 fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
145 nGlyphs,
commit-bot@chromium.org3ae0e6c2014-02-11 18:24:25 +0000146 4, 6, &fVertexBounds);
commit-bot@chromium.org42a89572013-10-28 15:13:50 +0000147
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000148 fDrawTarget->resetVertexSource();
149 fVertices = NULL;
150 fMaxVertices = 0;
151 fCurrVertex = 0;
commit-bot@chromium.org3ae0e6c2014-02-11 18:24:25 +0000152 fVertexBounds.setLargestInverted();
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000153 SkSafeSetNull(fCurrTexture);
154 }
155}
156
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000157inline void GrBitmapTextContext::init(const GrPaint& paint, const SkPaint& skPaint) {
158 GrTextContext::init(paint, skPaint);
159
160 fStrike = NULL;
161
162 fCurrTexture = NULL;
163 fCurrVertex = 0;
164
165 fVertices = NULL;
166 fMaxVertices = 0;
167}
168
169inline void GrBitmapTextContext::finish() {
bsalomon594069f2014-06-06 06:16:34 -0700170 this->flushGlyphs();
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000171
172 GrTextContext::finish();
173}
174
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000175void GrBitmapTextContext::drawText(const GrPaint& paint, const SkPaint& skPaint,
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000176 const char text[], size_t byteLength,
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000177 SkScalar x, SkScalar y) {
178 SkASSERT(byteLength == 0 || text != NULL);
179
180 // nothing to draw
181 if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) {
182 return;
183 }
184
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000185 this->init(paint, skPaint);
186
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000187 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
188
189 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, &fContext->getMatrix());
190 SkGlyphCache* cache = autoCache.getCache();
191 GrFontScaler* fontScaler = GetGrFontScaler(cache);
192
193 // transform our starting point
194 {
195 SkPoint loc;
196 fContext->getMatrix().mapXY(x, y, &loc);
197 x = loc.fX;
198 y = loc.fY;
199 }
200
201 // need to measure first
202 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
203 SkVector stop;
204
205 MeasureText(cache, glyphCacheProc, text, byteLength, &stop);
206
207 SkScalar stopX = stop.fX;
208 SkScalar stopY = stop.fY;
209
210 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
211 stopX = SkScalarHalf(stopX);
212 stopY = SkScalarHalf(stopY);
213 }
214 x -= stopX;
215 y -= stopY;
216 }
217
218 const char* stop = text + byteLength;
219
220 SkAutoKern autokern;
221
222 SkFixed fxMask = ~0;
223 SkFixed fyMask = ~0;
224 SkFixed halfSampleX, halfSampleY;
225 if (cache->isSubpixel()) {
226 halfSampleX = halfSampleY = (SK_FixedHalf >> SkGlyph::kSubBits);
227 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(fContext->getMatrix());
228 if (kX_SkAxisAlignment == baseline) {
229 fyMask = 0;
230 halfSampleY = SK_FixedHalf;
231 } else if (kY_SkAxisAlignment == baseline) {
232 fxMask = 0;
233 halfSampleX = SK_FixedHalf;
234 }
235 } else {
236 halfSampleX = halfSampleY = SK_FixedHalf;
237 }
238
239 SkFixed fx = SkScalarToFixed(x) + halfSampleX;
240 SkFixed fy = SkScalarToFixed(y) + halfSampleY;
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000241
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000242 GrContext::AutoMatrix autoMatrix;
243 autoMatrix.setIdentity(fContext, &fPaint);
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000244
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000245 while (text < stop) {
246 const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
247
248 fx += autokern.adjust(glyph);
249
250 if (glyph.fWidth) {
251 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
252 glyph.getSubXFixed(),
253 glyph.getSubYFixed()),
254 SkFixedFloorToFixed(fx),
255 SkFixedFloorToFixed(fy),
256 fontScaler);
257 }
258
259 fx += glyph.fAdvanceX;
260 fy += glyph.fAdvanceY;
261 }
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000262
263 this->finish();
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000264}
265
266///////////////////////////////////////////////////////////////////////////////
267// Copied from SkDraw
268
269// last parameter is interpreted as SkFixed [x, y]
270// return the fixed position, which may be rounded or not by the caller
271// e.g. subpixel doesn't round
272typedef void (*AlignProc)(const SkPoint&, const SkGlyph&, SkIPoint*);
273
274static void leftAlignProc(const SkPoint& loc, const SkGlyph& glyph, SkIPoint* dst) {
275 dst->set(SkScalarToFixed(loc.fX), SkScalarToFixed(loc.fY));
276}
277
278static void centerAlignProc(const SkPoint& loc, const SkGlyph& glyph, SkIPoint* dst) {
279 dst->set(SkScalarToFixed(loc.fX) - (glyph.fAdvanceX >> 1),
280 SkScalarToFixed(loc.fY) - (glyph.fAdvanceY >> 1));
281}
282
283static void rightAlignProc(const SkPoint& loc, const SkGlyph& glyph, SkIPoint* dst) {
284 dst->set(SkScalarToFixed(loc.fX) - glyph.fAdvanceX,
285 SkScalarToFixed(loc.fY) - glyph.fAdvanceY);
286}
287
288static AlignProc pick_align_proc(SkPaint::Align align) {
289 static const AlignProc gProcs[] = {
290 leftAlignProc, centerAlignProc, rightAlignProc
291 };
292
293 SkASSERT((unsigned)align < SK_ARRAY_COUNT(gProcs));
294
295 return gProcs[align];
296}
297
298class BitmapTextMapState {
299public:
300 mutable SkPoint fLoc;
301
302 BitmapTextMapState(const SkMatrix& matrix, SkScalar y)
303 : fMatrix(matrix), fProc(matrix.getMapXYProc()), fY(y) {}
304
305 typedef void (*Proc)(const BitmapTextMapState&, const SkScalar pos[]);
306
307 Proc pickProc(int scalarsPerPosition);
308
309private:
310 const SkMatrix& fMatrix;
311 SkMatrix::MapXYProc fProc;
312 SkScalar fY; // ignored by MapXYProc
313 // these are only used by Only... procs
314 SkScalar fScaleX, fTransX, fTransformedY;
315
316 static void MapXProc(const BitmapTextMapState& state, const SkScalar pos[]) {
317 state.fProc(state.fMatrix, *pos, state.fY, &state.fLoc);
318 }
319
320 static void MapXYProc(const BitmapTextMapState& state, const SkScalar pos[]) {
321 state.fProc(state.fMatrix, pos[0], pos[1], &state.fLoc);
322 }
323
324 static void MapOnlyScaleXProc(const BitmapTextMapState& state,
325 const SkScalar pos[]) {
326 state.fLoc.set(SkScalarMul(state.fScaleX, *pos) + state.fTransX,
327 state.fTransformedY);
328 }
329
330 static void MapOnlyTransXProc(const BitmapTextMapState& state,
331 const SkScalar pos[]) {
332 state.fLoc.set(*pos + state.fTransX, state.fTransformedY);
333 }
334};
335
336BitmapTextMapState::Proc BitmapTextMapState::pickProc(int scalarsPerPosition) {
337 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
338
339 if (1 == scalarsPerPosition) {
340 unsigned mtype = fMatrix.getType();
341 if (mtype & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)) {
342 return MapXProc;
343 } else {
344 fScaleX = fMatrix.getScaleX();
345 fTransX = fMatrix.getTranslateX();
346 fTransformedY = SkScalarMul(fY, fMatrix.getScaleY()) +
347 fMatrix.getTranslateY();
348 return (mtype & SkMatrix::kScale_Mask) ?
349 MapOnlyScaleXProc : MapOnlyTransXProc;
350 }
351 } else {
352 return MapXYProc;
353 }
354}
355
356///////////////////////////////////////////////////////////////////////////////
357
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000358void GrBitmapTextContext::drawPosText(const GrPaint& paint, const SkPaint& skPaint,
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000359 const char text[], size_t byteLength,
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000360 const SkScalar pos[], SkScalar constY,
361 int scalarsPerPosition) {
362 SkASSERT(byteLength == 0 || text != NULL);
363 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
364
365 // nothing to draw
366 if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) {
367 return;
368 }
369
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000370 this->init(paint, skPaint);
371
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000372 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
373
374 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, &fContext->getMatrix());
375 SkGlyphCache* cache = autoCache.getCache();
376 GrFontScaler* fontScaler = GetGrFontScaler(cache);
377
378 // store original matrix before we reset, so we can use it to transform positions
379 SkMatrix ctm = fContext->getMatrix();
380 GrContext::AutoMatrix autoMatrix;
381 autoMatrix.setIdentity(fContext, &fPaint);
382
383 const char* stop = text + byteLength;
384 AlignProc alignProc = pick_align_proc(fSkPaint.getTextAlign());
385 BitmapTextMapState tms(ctm, constY);
386 BitmapTextMapState::Proc tmsProc = tms.pickProc(scalarsPerPosition);
387 SkFixed halfSampleX = 0, halfSampleY = 0;
388
389 if (cache->isSubpixel()) {
390 // maybe we should skip the rounding if linearText is set
391 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(ctm);
392
393 SkFixed fxMask = ~0;
394 SkFixed fyMask = ~0;
395 if (kX_SkAxisAlignment == baseline) {
396 fyMask = 0;
397#ifndef SK_IGNORE_SUBPIXEL_AXIS_ALIGN_FIX
398 halfSampleY = SK_FixedHalf;
399#endif
400 } else if (kY_SkAxisAlignment == baseline) {
401 fxMask = 0;
402#ifndef SK_IGNORE_SUBPIXEL_AXIS_ALIGN_FIX
403 halfSampleX = SK_FixedHalf;
404#endif
405 }
406
407 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
408 while (text < stop) {
409 tmsProc(tms, pos);
410 SkFixed fx = SkScalarToFixed(tms.fLoc.fX) + halfSampleX;
411 SkFixed fy = SkScalarToFixed(tms.fLoc.fY) + halfSampleY;
412
413 const SkGlyph& glyph = glyphCacheProc(cache, &text,
414 fx & fxMask, fy & fyMask);
415
416 if (glyph.fWidth) {
417 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
418 glyph.getSubXFixed(),
419 glyph.getSubYFixed()),
420 SkFixedFloorToFixed(fx),
421 SkFixedFloorToFixed(fy),
422 fontScaler);
423 }
424 pos += scalarsPerPosition;
425 }
426 } else {
427 while (text < stop) {
428 const char* currentText = text;
429 const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
430
431 if (metricGlyph.fWidth) {
432 SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
433 SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
434
435 tmsProc(tms, pos);
436 SkIPoint fixedLoc;
437 alignProc(tms.fLoc, metricGlyph, &fixedLoc);
438
439 SkFixed fx = fixedLoc.fX + halfSampleX;
440 SkFixed fy = fixedLoc.fY + halfSampleY;
441
442 // have to call again, now that we've been "aligned"
443 const SkGlyph& glyph = glyphCacheProc(cache, &currentText,
444 fx & fxMask, fy & fyMask);
445 // the assumption is that the metrics haven't changed
446 SkASSERT(prevAdvX == glyph.fAdvanceX);
447 SkASSERT(prevAdvY == glyph.fAdvanceY);
448 SkASSERT(glyph.fWidth);
449
450 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
451 glyph.getSubXFixed(),
452 glyph.getSubYFixed()),
453 SkFixedFloorToFixed(fx),
454 SkFixedFloorToFixed(fy),
455 fontScaler);
456 }
457 pos += scalarsPerPosition;
458 }
459 }
460 } else { // not subpixel
461
462 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
463 while (text < stop) {
464 // the last 2 parameters are ignored
465 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
466
467 if (glyph.fWidth) {
468 tmsProc(tms, pos);
469
470 SkFixed fx = SkScalarToFixed(tms.fLoc.fX) + SK_FixedHalf; //halfSampleX;
471 SkFixed fy = SkScalarToFixed(tms.fLoc.fY) + SK_FixedHalf; //halfSampleY;
472 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
473 glyph.getSubXFixed(),
474 glyph.getSubYFixed()),
475 SkFixedFloorToFixed(fx),
476 SkFixedFloorToFixed(fy),
477 fontScaler);
478 }
479 pos += scalarsPerPosition;
480 }
481 } else {
482 while (text < stop) {
483 // the last 2 parameters are ignored
484 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
485
486 if (glyph.fWidth) {
487 tmsProc(tms, pos);
488
489 SkIPoint fixedLoc;
490 alignProc(tms.fLoc, glyph, &fixedLoc);
491
492 SkFixed fx = fixedLoc.fX + SK_FixedHalf; //halfSampleX;
493 SkFixed fy = fixedLoc.fY + SK_FixedHalf; //halfSampleY;
494 this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
495 glyph.getSubXFixed(),
496 glyph.getSubYFixed()),
497 SkFixedFloorToFixed(fx),
498 SkFixedFloorToFixed(fy),
499 fontScaler);
500 }
501 pos += scalarsPerPosition;
502 }
503 }
504 }
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000505
506 this->finish();
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000507}
508
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000509void GrBitmapTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000510 SkFixed vx, SkFixed vy,
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000511 GrFontScaler* scaler) {
512 if (NULL == fDrawTarget) {
513 return;
514 }
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000515
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000516 if (NULL == fStrike) {
commit-bot@chromium.org75a22952013-11-21 15:09:33 +0000517 fStrike = fContext->getFontCache()->getStrike(scaler, false);
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000518 }
519
520 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
521 if (NULL == glyph || glyph->fBounds.isEmpty()) {
522 return;
523 }
524
525 vx += SkIntToFixed(glyph->fBounds.fLeft);
526 vy += SkIntToFixed(glyph->fBounds.fTop);
527
528 // keep them as ints until we've done the clip-test
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000529 SkFixed width = glyph->fBounds.width();
530 SkFixed height = glyph->fBounds.height();
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000531
532 // check if we clipped out
533 if (true || NULL == glyph->fPlot) {
534 int x = vx >> 16;
535 int y = vy >> 16;
536 if (fClipRect.quickReject(x, y, x + width, y + height)) {
537// SkCLZ(3); // so we can set a break-point in the debugger
538 return;
539 }
540 }
541
542 if (NULL == glyph->fPlot) {
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000543 if (fStrike->addGlyphToAtlas(glyph, scaler)) {
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000544 goto HAS_ATLAS;
545 }
546
547 // try to clear out an unused plot before we flush
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000548 if (fContext->getFontCache()->freeUnusedPlot(fStrike) &&
549 fStrike->addGlyphToAtlas(glyph, scaler)) {
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000550 goto HAS_ATLAS;
551 }
552
553 if (c_DumpFontCache) {
554#ifdef SK_DEVELOPER
555 fContext->getFontCache()->dump();
556#endif
557 }
558
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000559 // flush any accumulated draws to allow us to free up a plot
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000560 this->flushGlyphs();
561 fContext->flush();
562
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000563 // we should have an unused plot now
564 if (fContext->getFontCache()->freeUnusedPlot(fStrike) &&
565 fStrike->addGlyphToAtlas(glyph, scaler)) {
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000566 goto HAS_ATLAS;
567 }
568
569 if (NULL == glyph->fPath) {
570 SkPath* path = SkNEW(SkPath);
571 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
572 // flag the glyph as being dead?
573 delete path;
574 return;
575 }
576 glyph->fPath = path;
577 }
578
579 GrContext::AutoMatrix am;
580 SkMatrix translate;
581 translate.setTranslate(SkFixedToScalar(vx - SkIntToFixed(glyph->fBounds.fLeft)),
582 SkFixedToScalar(vy - SkIntToFixed(glyph->fBounds.fTop)));
583 GrPaint tmpPaint(fPaint);
584 am.setPreConcat(fContext, translate, &tmpPaint);
585 SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
586 fContext->drawPath(tmpPaint, *glyph->fPath, stroke);
587 return;
588 }
589
590HAS_ATLAS:
591 SkASSERT(glyph->fPlot);
592 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
593 glyph->fPlot->setDrawToken(drawToken);
594
595 // now promote them to fixed (TODO: Rethink using fixed pt).
596 width = SkIntToFixed(width);
597 height = SkIntToFixed(height);
598
599 GrTexture* texture = glyph->fPlot->texture();
600 SkASSERT(texture);
601
602 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
603 this->flushGlyphs();
604 fCurrTexture = texture;
605 fCurrTexture->ref();
606 }
607
bsalomon594069f2014-06-06 06:16:34 -0700608 bool useColorVerts = kA8_GrMaskFormat == fStrike->getMaskFormat();
609
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000610 if (NULL == fVertices) {
611 // If we need to reserve vertices allow the draw target to suggest
612 // a number of verts to reserve and whether to perform a flush.
613 fMaxVertices = kMinRequestedVerts;
bsalomon594069f2014-06-06 06:16:34 -0700614 if (useColorVerts) {
615 fDrawTarget->drawState()->setVertexAttribs<gTextVertexWithColorAttribs>(
616 SK_ARRAY_COUNT(gTextVertexWithColorAttribs));
617 } else {
618 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
619 SK_ARRAY_COUNT(gTextVertexAttribs));
620 }
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000621 bool flush = fDrawTarget->geometryHints(&fMaxVertices, NULL);
622 if (flush) {
623 this->flushGlyphs();
624 fContext->flush();
bsalomon594069f2014-06-06 06:16:34 -0700625 if (useColorVerts) {
626 fDrawTarget->drawState()->setVertexAttribs<gTextVertexWithColorAttribs>(
627 SK_ARRAY_COUNT(gTextVertexWithColorAttribs));
628 } else {
629 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
630 SK_ARRAY_COUNT(gTextVertexAttribs));
631 }
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000632 }
633 fMaxVertices = kDefaultRequestedVerts;
634 // ignore return, no point in flushing again.
635 fDrawTarget->geometryHints(&fMaxVertices, NULL);
636
637 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
638 if (fMaxVertices < kMinRequestedVerts) {
639 fMaxVertices = kDefaultRequestedVerts;
640 } else if (fMaxVertices > maxQuadVertices) {
641 // don't exceed the limit of the index buffer
642 fMaxVertices = maxQuadVertices;
643 }
644 bool success = fDrawTarget->reserveVertexAndIndexSpace(fMaxVertices,
645 0,
bsalomon594069f2014-06-06 06:16:34 -0700646 &fVertices,
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000647 NULL);
648 GrAlwaysAssert(success);
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000649 }
650
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000651 SkFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX);
652 SkFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY);
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000653
commit-bot@chromium.org3ae0e6c2014-02-11 18:24:25 +0000654 SkRect r;
655 r.fLeft = SkFixedToFloat(vx);
656 r.fTop = SkFixedToFloat(vy);
657 r.fRight = SkFixedToFloat(vx + width);
658 r.fBottom = SkFixedToFloat(vy + height);
659
660 fVertexBounds.growToInclude(r);
661
bsalomon594069f2014-06-06 06:16:34 -0700662 size_t vertSize = useColorVerts ? (2 * sizeof(SkPoint) + sizeof(GrColor)) :
663 (2 * sizeof(SkPoint));
664
665 SkASSERT(vertSize == fDrawTarget->getDrawState().getVertexSize());
666
667 SkPoint* positions = reinterpret_cast<SkPoint*>(
668 reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVertex);
669 positions->setRectFan(r.fLeft, r.fTop, r.fRight, r.fBottom, vertSize);
670
671 // The texture coords are last in both the with and without color vertex layouts.
672 SkPoint* textureCoords = reinterpret_cast<SkPoint*>(
673 reinterpret_cast<intptr_t>(positions) + vertSize - sizeof(SkPoint));
674 textureCoords->setRectFan(SkFixedToFloat(texture->normalizeFixedX(tx)),
675 SkFixedToFloat(texture->normalizeFixedY(ty)),
676 SkFixedToFloat(texture->normalizeFixedX(tx + width)),
677 SkFixedToFloat(texture->normalizeFixedY(ty + height)),
678 vertSize);
679 if (useColorVerts) {
680 // color comes after position.
681 GrColor* colors = reinterpret_cast<GrColor*>(positions + 1);
682 for (int i = 0; i < 4; ++i) {
683 *colors = fPaint.getColor();
684 colors = reinterpret_cast<GrColor*>(reinterpret_cast<intptr_t>(colors) + vertSize);
685 }
686 }
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000687 fCurrVertex += 4;
688}