blob: 790590dbcc2c75707cae674aee366f230407bcc3 [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"
joshualitt5478d422014-11-14 16:00:38 -080010#include "GrDefaultGeoProcFactory.h"
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000011#include "GrDrawTarget.h"
jvanverth787cdf92014-12-04 10:46:50 -080012#include "GrFontCache.h"
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000013#include "GrFontScaler.h"
14#include "GrIndexBuffer.h"
egdanield58a0ba2014-06-11 10:30:05 -070015#include "GrStrokeInfo.h"
bsalomonafbf2d62014-09-30 12:18:44 -070016#include "GrTexturePriv.h"
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000017
jvanverthaab626c2014-10-16 08:04:39 -070018#include "SkAutoKern.h"
bsalomonafbf2d62014-09-30 12:18:44 -070019#include "SkColorPriv.h"
commit-bot@chromium.org9f94b912014-01-30 15:22:54 +000020#include "SkDraw.h"
kkinnunencb9a2c82014-06-12 23:06:28 -070021#include "SkDrawProcs.h"
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +000022#include "SkGlyphCache.h"
23#include "SkGpuDevice.h"
24#include "SkGr.h"
bsalomonafbf2d62014-09-30 12:18:44 -070025#include "SkPath.h"
26#include "SkRTConf.h"
27#include "SkStrokeRec.h"
kkinnunencb9a2c82014-06-12 23:06:28 -070028#include "SkTextMapStateProc.h"
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +000029
egdaniel309e3462014-12-09 10:35:58 -080030#include "effects/GrBitmapTextGeoProc.h"
joshualitt5478d422014-11-14 16:00:38 -080031#include "effects/GrSimpleTextureEffect.h"
32
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000033SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
34 "Dump the contents of the font cache before every purge.");
35
bsalomon594069f2014-06-06 06:16:34 -070036namespace {
egdanieled3af662014-10-31 06:55:45 -070037static const size_t kLCDTextVASize = 2 * sizeof(SkPoint);
38
39// position + local coord
egdanieled3af662014-10-31 06:55:45 -070040static const size_t kColorTextVASize = 2 * sizeof(SkPoint);
egdaniel7b3d5ee2014-08-28 05:41:14 -070041
egdanieled3af662014-10-31 06:55:45 -070042static const size_t kGrayTextVASize = 2 * sizeof(SkPoint) + sizeof(GrColor);
egdaniel7b3d5ee2014-08-28 05:41:14 -070043
jvanverth73f10532014-10-23 11:57:12 -070044static const int kVerticesPerGlyph = 4;
45static const int kIndicesPerGlyph = 6;
bsalomon594069f2014-06-06 06:16:34 -070046};
47
skia.committer@gmail.come5d70152014-01-29 07:01:48 +000048GrBitmapTextContext::GrBitmapTextContext(GrContext* context,
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +000049 const SkDeviceProperties& properties)
Mike Klein6a25bd02014-08-29 10:03:59 -040050 : GrTextContext(context, properties) {
51 fStrike = NULL;
52
53 fCurrTexture = NULL;
Mike Klein6a25bd02014-08-29 10:03:59 -040054 fEffectTextureUniqueID = SK_InvalidUniqueID;
55
56 fVertices = NULL;
jvanverth73f10532014-10-23 11:57:12 -070057 fCurrVertex = 0;
58 fAllocVertexCount = 0;
59 fTotalVertexCount = 0;
Mike Klein6a25bd02014-08-29 10:03:59 -040060
commit-bot@chromium.org3ae0e6c2014-02-11 18:24:25 +000061 fVertexBounds.setLargestInverted();
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000062}
63
jvanverth8c27a182014-10-14 08:45:50 -070064GrBitmapTextContext* GrBitmapTextContext::Create(GrContext* context,
65 const SkDeviceProperties& props) {
66 return SkNEW_ARGS(GrBitmapTextContext, (context, props));
67}
68
joshualitt5531d512014-12-17 15:50:11 -080069bool GrBitmapTextContext::canDraw(const SkPaint& paint, const SkMatrix& viewMatrix) {
70 return !SkDraw::ShouldDrawTextAsPaths(paint, viewMatrix);
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000071}
72
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000073inline void GrBitmapTextContext::init(const GrPaint& paint, const SkPaint& skPaint) {
74 GrTextContext::init(paint, skPaint);
75
76 fStrike = NULL;
77
Mike Klein6a25bd02014-08-29 10:03:59 -040078 fCurrTexture = NULL;
jvanverth63b9dc82014-08-28 10:39:40 -070079 fCurrVertex = 0;
Mike Klein6a25bd02014-08-29 10:03:59 -040080
81 fVertices = NULL;
jvanverth73f10532014-10-23 11:57:12 -070082 fAllocVertexCount = 0;
83 fTotalVertexCount = 0;
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000084}
85
jvanverthaab626c2014-10-16 08:04:39 -070086void GrBitmapTextContext::onDrawText(const GrPaint& paint, const SkPaint& skPaint,
joshualitt5531d512014-12-17 15:50:11 -080087 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -080088 const char text[], size_t byteLength,
89 SkScalar x, SkScalar y) {
jvanverthaab626c2014-10-16 08:04:39 -070090 SkASSERT(byteLength == 0 || text != NULL);
91
92 // nothing to draw
93 if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) {
94 return;
95 }
96
97 this->init(paint, skPaint);
98
99 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
100
joshualitt5531d512014-12-17 15:50:11 -0800101 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, &viewMatrix);
jvanverthaab626c2014-10-16 08:04:39 -0700102 SkGlyphCache* cache = autoCache.getCache();
103 GrFontScaler* fontScaler = GetGrFontScaler(cache);
104
105 // transform our starting point
106 {
107 SkPoint loc;
joshualitt5531d512014-12-17 15:50:11 -0800108 viewMatrix.mapXY(x, y, &loc);
jvanverthaab626c2014-10-16 08:04:39 -0700109 x = loc.fX;
110 y = loc.fY;
111 }
112
113 // need to measure first
jvanverth73f10532014-10-23 11:57:12 -0700114 int numGlyphs;
jvanverthaab626c2014-10-16 08:04:39 -0700115 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
jvanverth73f10532014-10-23 11:57:12 -0700116 SkVector stopVector;
117 numGlyphs = MeasureText(cache, glyphCacheProc, text, byteLength, &stopVector);
jvanverthaab626c2014-10-16 08:04:39 -0700118
jvanverth73f10532014-10-23 11:57:12 -0700119 SkScalar stopX = stopVector.fX;
120 SkScalar stopY = stopVector.fY;
jvanverthaab626c2014-10-16 08:04:39 -0700121
122 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
123 stopX = SkScalarHalf(stopX);
124 stopY = SkScalarHalf(stopY);
125 }
126 x -= stopX;
127 y -= stopY;
jvanverth73f10532014-10-23 11:57:12 -0700128 } else {
129 numGlyphs = fSkPaint.textToGlyphs(text, byteLength, NULL);
jvanverthaab626c2014-10-16 08:04:39 -0700130 }
jvanverth73f10532014-10-23 11:57:12 -0700131 fTotalVertexCount = kVerticesPerGlyph*numGlyphs;
jvanverthaab626c2014-10-16 08:04:39 -0700132
133 const char* stop = text + byteLength;
134
135 SkAutoKern autokern;
136
137 SkFixed fxMask = ~0;
138 SkFixed fyMask = ~0;
139 SkFixed halfSampleX, halfSampleY;
140 if (cache->isSubpixel()) {
141 halfSampleX = halfSampleY = (SK_FixedHalf >> SkGlyph::kSubBits);
joshualitt5531d512014-12-17 15:50:11 -0800142 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
jvanverthaab626c2014-10-16 08:04:39 -0700143 if (kX_SkAxisAlignment == baseline) {
144 fyMask = 0;
145 halfSampleY = SK_FixedHalf;
146 } else if (kY_SkAxisAlignment == baseline) {
147 fxMask = 0;
148 halfSampleX = SK_FixedHalf;
149 }
150 } else {
151 halfSampleX = halfSampleY = SK_FixedHalf;
152 }
153
154 SkFixed fx = SkScalarToFixed(x) + halfSampleX;
155 SkFixed fy = SkScalarToFixed(y) + halfSampleY;
156
joshualitt8fc6c2d2014-12-22 15:27:05 -0800157 // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix
158 if (kARGB_GrMaskFormat != fCurrMaskFormat && !viewMatrix.invert(&fLocalMatrix)) {
joshualitt5531d512014-12-17 15:50:11 -0800159 SkDebugf("Cannot invert viewmatrix\n");
160 }
jvanverthaab626c2014-10-16 08:04:39 -0700161
162 while (text < stop) {
163 const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
164
165 fx += autokern.adjust(glyph);
166
167 if (glyph.fWidth) {
168 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
169 glyph.getSubXFixed(),
170 glyph.getSubYFixed()),
171 SkFixedFloorToFixed(fx),
172 SkFixedFloorToFixed(fy),
173 fontScaler);
174 }
175
176 fx += glyph.fAdvanceX;
177 fy += glyph.fAdvanceY;
178 }
179
180 this->finish();
181}
182
jvanverth8c27a182014-10-14 08:45:50 -0700183void GrBitmapTextContext::onDrawPosText(const GrPaint& paint, const SkPaint& skPaint,
joshualitt5531d512014-12-17 15:50:11 -0800184 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800185 const char text[], size_t byteLength,
186 const SkScalar pos[], int scalarsPerPosition,
187 const SkPoint& offset) {
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000188 SkASSERT(byteLength == 0 || text != NULL);
189 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
190
191 // nothing to draw
192 if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) {
193 return;
194 }
195
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000196 this->init(paint, skPaint);
197
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000198 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
199
joshualitt5531d512014-12-17 15:50:11 -0800200 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, &viewMatrix);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000201 SkGlyphCache* cache = autoCache.getCache();
202 GrFontScaler* fontScaler = GetGrFontScaler(cache);
203
204 // store original matrix before we reset, so we can use it to transform positions
joshualitt5531d512014-12-17 15:50:11 -0800205 SkMatrix ctm = viewMatrix;
joshualitt8fc6c2d2014-12-22 15:27:05 -0800206
207 // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix
208 if (kARGB_GrMaskFormat != fCurrMaskFormat && !viewMatrix.invert(&fLocalMatrix)) {
joshualitt5531d512014-12-17 15:50:11 -0800209 SkDebugf("Cannot invert viewmatrix\n");
210 }
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000211
jvanverth73f10532014-10-23 11:57:12 -0700212 int numGlyphs = fSkPaint.textToGlyphs(text, byteLength, NULL);
213 fTotalVertexCount = kVerticesPerGlyph*numGlyphs;
214
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000215 const char* stop = text + byteLength;
kkinnunencb9a2c82014-06-12 23:06:28 -0700216 SkTextAlignProc alignProc(fSkPaint.getTextAlign());
fmalita05c4a432014-09-29 06:29:53 -0700217 SkTextMapStateProc tmsProc(ctm, offset, scalarsPerPosition);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000218 SkFixed halfSampleX = 0, halfSampleY = 0;
219
220 if (cache->isSubpixel()) {
221 // maybe we should skip the rounding if linearText is set
222 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(ctm);
223
224 SkFixed fxMask = ~0;
225 SkFixed fyMask = ~0;
226 if (kX_SkAxisAlignment == baseline) {
227 fyMask = 0;
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000228 halfSampleY = SK_FixedHalf;
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000229 } else if (kY_SkAxisAlignment == baseline) {
230 fxMask = 0;
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000231 halfSampleX = SK_FixedHalf;
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000232 }
233
234 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
235 while (text < stop) {
kkinnunencb9a2c82014-06-12 23:06:28 -0700236 SkPoint tmsLoc;
237 tmsProc(pos, &tmsLoc);
238 SkFixed fx = SkScalarToFixed(tmsLoc.fX) + halfSampleX;
239 SkFixed fy = SkScalarToFixed(tmsLoc.fY) + halfSampleY;
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000240
241 const SkGlyph& glyph = glyphCacheProc(cache, &text,
242 fx & fxMask, fy & fyMask);
243
244 if (glyph.fWidth) {
jvanverth0fedb192014-10-08 09:07:27 -0700245 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
246 glyph.getSubXFixed(),
247 glyph.getSubYFixed()),
248 SkFixedFloorToFixed(fx),
249 SkFixedFloorToFixed(fy),
250 fontScaler);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000251 }
252 pos += scalarsPerPosition;
253 }
254 } else {
255 while (text < stop) {
256 const char* currentText = text;
257 const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
258
259 if (metricGlyph.fWidth) {
260 SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
261 SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
kkinnunencb9a2c82014-06-12 23:06:28 -0700262 SkPoint tmsLoc;
263 tmsProc(pos, &tmsLoc);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000264 SkIPoint fixedLoc;
kkinnunencb9a2c82014-06-12 23:06:28 -0700265 alignProc(tmsLoc, metricGlyph, &fixedLoc);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000266
267 SkFixed fx = fixedLoc.fX + halfSampleX;
268 SkFixed fy = fixedLoc.fY + halfSampleY;
269
270 // have to call again, now that we've been "aligned"
271 const SkGlyph& glyph = glyphCacheProc(cache, &currentText,
272 fx & fxMask, fy & fyMask);
273 // the assumption is that the metrics haven't changed
274 SkASSERT(prevAdvX == glyph.fAdvanceX);
275 SkASSERT(prevAdvY == glyph.fAdvanceY);
276 SkASSERT(glyph.fWidth);
277
jvanverth0fedb192014-10-08 09:07:27 -0700278 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
279 glyph.getSubXFixed(),
280 glyph.getSubYFixed()),
281 SkFixedFloorToFixed(fx),
282 SkFixedFloorToFixed(fy),
283 fontScaler);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000284 }
285 pos += scalarsPerPosition;
286 }
287 }
288 } else { // not subpixel
289
290 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
291 while (text < stop) {
292 // the last 2 parameters are ignored
293 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
294
295 if (glyph.fWidth) {
kkinnunencb9a2c82014-06-12 23:06:28 -0700296 SkPoint tmsLoc;
297 tmsProc(pos, &tmsLoc);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000298
kkinnunencb9a2c82014-06-12 23:06:28 -0700299 SkFixed fx = SkScalarToFixed(tmsLoc.fX) + SK_FixedHalf; //halfSampleX;
300 SkFixed fy = SkScalarToFixed(tmsLoc.fY) + SK_FixedHalf; //halfSampleY;
jvanverth0fedb192014-10-08 09:07:27 -0700301 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
302 glyph.getSubXFixed(),
303 glyph.getSubYFixed()),
304 SkFixedFloorToFixed(fx),
305 SkFixedFloorToFixed(fy),
306 fontScaler);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000307 }
308 pos += scalarsPerPosition;
309 }
310 } else {
311 while (text < stop) {
312 // the last 2 parameters are ignored
313 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
314
315 if (glyph.fWidth) {
kkinnunencb9a2c82014-06-12 23:06:28 -0700316 SkPoint tmsLoc;
317 tmsProc(pos, &tmsLoc);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000318
319 SkIPoint fixedLoc;
kkinnunencb9a2c82014-06-12 23:06:28 -0700320 alignProc(tmsLoc, glyph, &fixedLoc);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000321
322 SkFixed fx = fixedLoc.fX + SK_FixedHalf; //halfSampleX;
323 SkFixed fy = fixedLoc.fY + SK_FixedHalf; //halfSampleY;
jvanverth0fedb192014-10-08 09:07:27 -0700324 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
325 glyph.getSubXFixed(),
326 glyph.getSubYFixed()),
327 SkFixedFloorToFixed(fx),
328 SkFixedFloorToFixed(fy),
329 fontScaler);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000330 }
331 pos += scalarsPerPosition;
332 }
333 }
334 }
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000335
336 this->finish();
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000337}
338
joshualitt9853cce2014-11-17 14:22:48 -0800339static size_t get_vertex_stride(GrMaskFormat maskFormat) {
340 switch (maskFormat) {
341 case kA8_GrMaskFormat:
342 return kGrayTextVASize;
343 case kARGB_GrMaskFormat:
344 return kColorTextVASize;
345 default:
346 return kLCDTextVASize;
347 }
348}
349
joshualitt9853cce2014-11-17 14:22:48 -0800350static void* alloc_vertices(GrDrawTarget* drawTarget,
351 int numVertices,
352 GrMaskFormat maskFormat) {
jvanverth73f10532014-10-23 11:57:12 -0700353 if (numVertices <= 0) {
354 return NULL;
355 }
356
357 // set up attributes
jvanverth73f10532014-10-23 11:57:12 -0700358 void* vertices = NULL;
359 bool success = drawTarget->reserveVertexAndIndexSpace(numVertices,
joshualitt9853cce2014-11-17 14:22:48 -0800360 get_vertex_stride(maskFormat),
jvanverth73f10532014-10-23 11:57:12 -0700361 0,
362 &vertices,
363 NULL);
364 GrAlwaysAssert(success);
365 return vertices;
366}
367
jvanverth787cdf92014-12-04 10:46:50 -0800368inline bool GrBitmapTextContext::uploadGlyph(GrGlyph* glyph, GrFontScaler* scaler) {
369 if (!fStrike->glyphTooLargeForAtlas(glyph)) {
370 if (fStrike->addGlyphToAtlas(glyph, scaler)) {
371 return true;
372 }
373
374 // try to clear out an unused plot before we flush
375 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
376 fStrike->addGlyphToAtlas(glyph, scaler)) {
377 return true;
378 }
379
380 if (c_DumpFontCache) {
381#ifdef SK_DEVELOPER
382 fContext->getFontCache()->dump();
383#endif
384 }
385
386 // before we purge the cache, we must flush any accumulated draws
387 this->flush();
388 fContext->flush();
389
390 // we should have an unused plot now
391 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
392 fStrike->addGlyphToAtlas(glyph, scaler)) {
393 return true;
394 }
395
396 // we should never get here
397 SkASSERT(false);
398 }
399
400 return false;
401}
402
jvanverth0fedb192014-10-08 09:07:27 -0700403void GrBitmapTextContext::appendGlyph(GrGlyph::PackedID packed,
404 SkFixed vx, SkFixed vy,
405 GrFontScaler* scaler) {
Mike Klein6a25bd02014-08-29 10:03:59 -0400406 if (NULL == fDrawTarget) {
407 return;
408 }
409
410 if (NULL == fStrike) {
411 fStrike = fContext->getFontCache()->getStrike(scaler, false);
412 }
413
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000414 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
415 if (NULL == glyph || glyph->fBounds.isEmpty()) {
416 return;
417 }
418
419 vx += SkIntToFixed(glyph->fBounds.fLeft);
420 vy += SkIntToFixed(glyph->fBounds.fTop);
421
422 // keep them as ints until we've done the clip-test
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000423 SkFixed width = glyph->fBounds.width();
424 SkFixed height = glyph->fBounds.height();
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000425
426 // check if we clipped out
427 if (true || NULL == glyph->fPlot) {
428 int x = vx >> 16;
429 int y = vy >> 16;
430 if (fClipRect.quickReject(x, y, x + width, y + height)) {
431// SkCLZ(3); // so we can set a break-point in the debugger
432 return;
433 }
434 }
435
joshualittc2625822014-12-18 16:40:54 -0800436 // If the glyph is too large we fall back to paths
jvanverth787cdf92014-12-04 10:46:50 -0800437 if (NULL == glyph->fPlot && !uploadGlyph(glyph, scaler)) {
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000438 if (NULL == glyph->fPath) {
439 SkPath* path = SkNEW(SkPath);
440 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
441 // flag the glyph as being dead?
442 delete path;
443 return;
444 }
445 glyph->fPath = path;
446 }
447
bsalomonec87dc62014-10-14 10:52:00 -0700448 // flush any accumulated draws before drawing this glyph as a path.
449 this->flush();
450
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000451 SkMatrix translate;
452 translate.setTranslate(SkFixedToScalar(vx - SkIntToFixed(glyph->fBounds.fLeft)),
453 SkFixedToScalar(vy - SkIntToFixed(glyph->fBounds.fTop)));
joshualittc2625822014-12-18 16:40:54 -0800454 SkPath tmpPath(*glyph->fPath);
455 tmpPath.transform(translate);
egdanield58a0ba2014-06-11 10:30:05 -0700456 GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle);
joshualittc2625822014-12-18 16:40:54 -0800457 fContext->drawPath(fPaint, SkMatrix::I(), tmpPath, strokeInfo);
jvanverth73f10532014-10-23 11:57:12 -0700458
459 // remove this glyph from the vertices we need to allocate
460 fTotalVertexCount -= kVerticesPerGlyph;
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000461 return;
462 }
463
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000464 SkASSERT(glyph->fPlot);
465 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
466 glyph->fPlot->setDrawToken(drawToken);
467
468 // now promote them to fixed (TODO: Rethink using fixed pt).
469 width = SkIntToFixed(width);
470 height = SkIntToFixed(height);
471
jvanverth294c3262014-10-10 11:36:12 -0700472 // the current texture/maskformat must match what the glyph needs
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000473 GrTexture* texture = glyph->fPlot->texture();
474 SkASSERT(texture);
475
jvanverth73f10532014-10-23 11:57:12 -0700476 if (fCurrTexture != texture || fCurrVertex + kVerticesPerGlyph > fAllocVertexCount) {
jvanverth0fedb192014-10-08 09:07:27 -0700477 this->flush();
Mike Klein6a25bd02014-08-29 10:03:59 -0400478 fCurrTexture = texture;
479 fCurrTexture->ref();
jvanverth294c3262014-10-10 11:36:12 -0700480 fCurrMaskFormat = glyph->fMaskFormat;
Mike Klein6a25bd02014-08-29 10:03:59 -0400481 }
482
Mike Klein6a25bd02014-08-29 10:03:59 -0400483 if (NULL == fVertices) {
jvanverth73f10532014-10-23 11:57:12 -0700484 int maxQuadVertices = kVerticesPerGlyph * fContext->getQuadIndexBuffer()->maxQuads();
485 fAllocVertexCount = SkMin32(fTotalVertexCount, maxQuadVertices);
egdanieled3af662014-10-31 06:55:45 -0700486 fVertices = alloc_vertices(fDrawTarget, fAllocVertexCount, fCurrMaskFormat);
Mike Klein6a25bd02014-08-29 10:03:59 -0400487 }
488
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000489 SkFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX);
490 SkFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY);
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000491
commit-bot@chromium.org3ae0e6c2014-02-11 18:24:25 +0000492 SkRect r;
493 r.fLeft = SkFixedToFloat(vx);
494 r.fTop = SkFixedToFloat(vy);
495 r.fRight = SkFixedToFloat(vx + width);
496 r.fBottom = SkFixedToFloat(vy + height);
497
reed10d03272014-10-01 09:24:12 -0700498 fVertexBounds.joinNonEmptyArg(r);
commit-bot@chromium.org3ae0e6c2014-02-11 18:24:25 +0000499
joshualitt9853cce2014-11-17 14:22:48 -0800500 size_t vertSize = get_vertex_stride(fCurrMaskFormat);
bsalomon594069f2014-06-06 06:16:34 -0700501
502 SkPoint* positions = reinterpret_cast<SkPoint*>(
503 reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVertex);
504 positions->setRectFan(r.fLeft, r.fTop, r.fRight, r.fBottom, vertSize);
505
506 // The texture coords are last in both the with and without color vertex layouts.
507 SkPoint* textureCoords = reinterpret_cast<SkPoint*>(
508 reinterpret_cast<intptr_t>(positions) + vertSize - sizeof(SkPoint));
bsalomonafbf2d62014-09-30 12:18:44 -0700509 textureCoords->setRectFan(SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx)),
510 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty)),
511 SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx + width)),
512 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty + height)),
bsalomon594069f2014-06-06 06:16:34 -0700513 vertSize);
egdanieled3af662014-10-31 06:55:45 -0700514 if (kA8_GrMaskFormat == fCurrMaskFormat) {
bsalomon594069f2014-06-06 06:16:34 -0700515 // color comes after position.
516 GrColor* colors = reinterpret_cast<GrColor*>(positions + 1);
517 for (int i = 0; i < 4; ++i) {
518 *colors = fPaint.getColor();
519 colors = reinterpret_cast<GrColor*>(reinterpret_cast<intptr_t>(colors) + vertSize);
520 }
521 }
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000522 fCurrVertex += 4;
523}
jvanverth0fedb192014-10-08 09:07:27 -0700524
jvanverth0fedb192014-10-08 09:07:27 -0700525void GrBitmapTextContext::flush() {
526 if (NULL == fDrawTarget) {
527 return;
528 }
529
jvanverth0fedb192014-10-08 09:07:27 -0700530 if (fCurrVertex > 0) {
joshualitt780b11e2014-11-18 09:40:40 -0800531 GrDrawState drawState;
532 drawState.setFromPaint(fPaint, SkMatrix::I(), fContext->getRenderTarget());
533
jvanverth0fedb192014-10-08 09:07:27 -0700534 // setup our sampler state for our text texture/atlas
535 SkASSERT(SkIsAlign4(fCurrVertex));
536 SkASSERT(fCurrTexture);
jvanverth0fedb192014-10-08 09:07:27 -0700537
jvanverth0fedb192014-10-08 09:07:27 -0700538 SkASSERT(fStrike);
joshualitt2e3b3e32014-12-09 13:31:14 -0800539 GrColor color = fPaint.getColor();
jvanverth294c3262014-10-10 11:36:12 -0700540 switch (fCurrMaskFormat) {
jvanverth0fedb192014-10-08 09:07:27 -0700541 // Color bitmap text
joshualitt2e3b3e32014-12-09 13:31:14 -0800542 case kARGB_GrMaskFormat: {
543 int a = fSkPaint.getAlpha();
544 color = SkColorSetARGB(a, a, a, a);
jvanverth0fedb192014-10-08 09:07:27 -0700545 break;
joshualitt2e3b3e32014-12-09 13:31:14 -0800546 }
jvanverth0fedb192014-10-08 09:07:27 -0700547 // LCD text
jvanverth0fedb192014-10-08 09:07:27 -0700548 case kA565_GrMaskFormat: {
egdaniel378092f2014-12-03 10:40:13 -0800549 // TODO: move supportsRGBCoverage check to setupCoverageEffect and only add LCD
550 // processor if the xp can support it. For now we will simply assume that if
551 // fUseLCDText is true, then we have a known color output.
552 if (!drawState.getXPFactory()->supportsRGBCoverage(0, kRGBA_GrColorComponentFlags)) {
tfarina38406c82014-10-31 07:11:12 -0700553 SkDebugf("LCD Text will not draw correctly.\n");
jvanverth0fedb192014-10-08 09:07:27 -0700554 }
jvanverth0fedb192014-10-08 09:07:27 -0700555 break;
556 }
557 // Grayscale/BW text
558 case kA8_GrMaskFormat:
jvanverth0fedb192014-10-08 09:07:27 -0700559 break;
560 default:
jvanverth294c3262014-10-10 11:36:12 -0700561 SkFAIL("Unexpected mask format.");
jvanverth0fedb192014-10-08 09:07:27 -0700562 }
joshualitt2e3b3e32014-12-09 13:31:14 -0800563
564 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kNone_FilterMode);
joshualitt2e3b3e32014-12-09 13:31:14 -0800565 if (kARGB_GrMaskFormat == fCurrMaskFormat) {
566 uint32_t textureUniqueID = fCurrTexture->getUniqueID();
567 if (textureUniqueID != fEffectTextureUniqueID ||
joshualitt56995b52014-12-11 15:44:02 -0800568 fCachedGeometryProcessor->color() != color) {
joshualitt2e3b3e32014-12-09 13:31:14 -0800569 uint32_t flags = GrDefaultGeoProcFactory::kLocalCoord_GPType;
570 fCachedGeometryProcessor.reset(GrDefaultGeoProcFactory::Create(color, flags));
571 fCachedTextureProcessor.reset(GrSimpleTextureEffect::Create(fCurrTexture,
572 SkMatrix::I(),
573 params));
joshualitt8fc6c2d2014-12-22 15:27:05 -0800574 fEffectTextureUniqueID = textureUniqueID;
joshualitt2e3b3e32014-12-09 13:31:14 -0800575 }
joshualitt2e3b3e32014-12-09 13:31:14 -0800576 drawState.addColorProcessor(fCachedTextureProcessor.get());
577 } else {
578 uint32_t textureUniqueID = fCurrTexture->getUniqueID();
579 if (textureUniqueID != fEffectTextureUniqueID ||
joshualitt8fc6c2d2014-12-22 15:27:05 -0800580 fCachedGeometryProcessor->color() != color ||
581 !fCachedGeometryProcessor->localMatrix().cheapEqualTo(fLocalMatrix)) {
joshualitt2e3b3e32014-12-09 13:31:14 -0800582 bool hasColor = kA8_GrMaskFormat == fCurrMaskFormat;
joshualitt56995b52014-12-11 15:44:02 -0800583 bool opaqueVertexColors = GrColorIsOpaque(fPaint.getColor());
joshualitt2e3b3e32014-12-09 13:31:14 -0800584 fCachedGeometryProcessor.reset(GrBitmapTextGeoProc::Create(color,
joshualitt56995b52014-12-11 15:44:02 -0800585 fCurrTexture,
586 params,
587 hasColor,
joshualitt8fc6c2d2014-12-22 15:27:05 -0800588 opaqueVertexColors,
589 fLocalMatrix));
joshualitt2e3b3e32014-12-09 13:31:14 -0800590 fEffectTextureUniqueID = textureUniqueID;
591 }
joshualitt2e3b3e32014-12-09 13:31:14 -0800592 }
593
jvanverth73f10532014-10-23 11:57:12 -0700594 int nGlyphs = fCurrVertex / kVerticesPerGlyph;
jvanverth0fedb192014-10-08 09:07:27 -0700595 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
joshualitt9853cce2014-11-17 14:22:48 -0800596 fDrawTarget->drawIndexedInstances(&drawState,
joshualitt56995b52014-12-11 15:44:02 -0800597 fCachedGeometryProcessor.get(),
joshualitt9853cce2014-11-17 14:22:48 -0800598 kTriangles_GrPrimitiveType,
jvanverth0fedb192014-10-08 09:07:27 -0700599 nGlyphs,
joshualitt9853cce2014-11-17 14:22:48 -0800600 kVerticesPerGlyph,
601 kIndicesPerGlyph,
602 &fVertexBounds);
jvanverth0fedb192014-10-08 09:07:27 -0700603
604 fDrawTarget->resetVertexSource();
605 fVertices = NULL;
jvanverth73f10532014-10-23 11:57:12 -0700606 fAllocVertexCount = 0;
607 // reset to be those that are left
608 fTotalVertexCount -= fCurrVertex;
jvanverth0fedb192014-10-08 09:07:27 -0700609 fCurrVertex = 0;
610 fVertexBounds.setLargestInverted();
611 SkSafeSetNull(fCurrTexture);
612 }
613}
614
615inline void GrBitmapTextContext::finish() {
616 this->flush();
jvanverth73f10532014-10-23 11:57:12 -0700617 fTotalVertexCount = 0;
jvanverth0fedb192014-10-08 09:07:27 -0700618
619 GrTextContext::finish();
620}
621