blob: f5e2c298f163b106d02fa990d082e2972f6afaf7 [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"
12#include "GrFontScaler.h"
13#include "GrIndexBuffer.h"
egdanield58a0ba2014-06-11 10:30:05 -070014#include "GrStrokeInfo.h"
bsalomonafbf2d62014-09-30 12:18:44 -070015#include "GrTexturePriv.h"
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000016#include "GrTextStrike.h"
17#include "GrTextStrike_impl.h"
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000018
jvanverthaab626c2014-10-16 08:04:39 -070019#include "SkAutoKern.h"
bsalomonafbf2d62014-09-30 12:18:44 -070020#include "SkColorPriv.h"
commit-bot@chromium.org9f94b912014-01-30 15:22:54 +000021#include "SkDraw.h"
kkinnunencb9a2c82014-06-12 23:06:28 -070022#include "SkDrawProcs.h"
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +000023#include "SkGlyphCache.h"
24#include "SkGpuDevice.h"
25#include "SkGr.h"
bsalomonafbf2d62014-09-30 12:18:44 -070026#include "SkPath.h"
27#include "SkRTConf.h"
28#include "SkStrokeRec.h"
kkinnunencb9a2c82014-06-12 23:06:28 -070029#include "SkTextMapStateProc.h"
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +000030
joshualitt5478d422014-11-14 16:00:38 -080031#include "effects/GrCustomCoordsTextureEffect.h"
32#include "effects/GrSimpleTextureEffect.h"
33
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000034SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
35 "Dump the contents of the font cache before every purge.");
36
bsalomon594069f2014-06-06 06:16:34 -070037namespace {
egdanieled3af662014-10-31 06:55:45 -070038static const size_t kLCDTextVASize = 2 * sizeof(SkPoint);
39
40// position + local coord
egdanieled3af662014-10-31 06:55:45 -070041static const size_t kColorTextVASize = 2 * sizeof(SkPoint);
egdaniel7b3d5ee2014-08-28 05:41:14 -070042
egdanieled3af662014-10-31 06:55:45 -070043static const size_t kGrayTextVASize = 2 * sizeof(SkPoint) + sizeof(GrColor);
egdaniel7b3d5ee2014-08-28 05:41:14 -070044
jvanverth73f10532014-10-23 11:57:12 -070045static const int kVerticesPerGlyph = 4;
46static const int kIndicesPerGlyph = 6;
bsalomon594069f2014-06-06 06:16:34 -070047};
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)
Mike Klein6a25bd02014-08-29 10:03:59 -040051 : GrTextContext(context, properties) {
52 fStrike = NULL;
53
54 fCurrTexture = NULL;
Mike Klein6a25bd02014-08-29 10:03:59 -040055 fEffectTextureUniqueID = SK_InvalidUniqueID;
56
57 fVertices = NULL;
jvanverth73f10532014-10-23 11:57:12 -070058 fCurrVertex = 0;
59 fAllocVertexCount = 0;
60 fTotalVertexCount = 0;
Mike Klein6a25bd02014-08-29 10:03:59 -040061
commit-bot@chromium.org3ae0e6c2014-02-11 18:24:25 +000062 fVertexBounds.setLargestInverted();
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000063}
64
jvanverth8c27a182014-10-14 08:45:50 -070065GrBitmapTextContext* GrBitmapTextContext::Create(GrContext* context,
66 const SkDeviceProperties& props) {
67 return SkNEW_ARGS(GrBitmapTextContext, (context, props));
68}
69
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000070bool GrBitmapTextContext::canDraw(const SkPaint& paint) {
71 return !SkDraw::ShouldDrawTextAsPaths(paint, fContext->getMatrix());
72}
73
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000074inline void GrBitmapTextContext::init(const GrPaint& paint, const SkPaint& skPaint) {
75 GrTextContext::init(paint, skPaint);
76
77 fStrike = NULL;
78
Mike Klein6a25bd02014-08-29 10:03:59 -040079 fCurrTexture = NULL;
jvanverth63b9dc82014-08-28 10:39:40 -070080 fCurrVertex = 0;
Mike Klein6a25bd02014-08-29 10:03:59 -040081
82 fVertices = NULL;
jvanverth73f10532014-10-23 11:57:12 -070083 fAllocVertexCount = 0;
84 fTotalVertexCount = 0;
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000085}
86
jvanverthaab626c2014-10-16 08:04:39 -070087void GrBitmapTextContext::onDrawText(const GrPaint& paint, const SkPaint& skPaint,
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
101 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, &fContext->getMatrix());
102 SkGlyphCache* cache = autoCache.getCache();
103 GrFontScaler* fontScaler = GetGrFontScaler(cache);
104
105 // transform our starting point
106 {
107 SkPoint loc;
108 fContext->getMatrix().mapXY(x, y, &loc);
109 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);
142 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(fContext->getMatrix());
143 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
157 GrContext::AutoMatrix autoMatrix;
158 autoMatrix.setIdentity(fContext, &fPaint);
159
160 while (text < stop) {
161 const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
162
163 fx += autokern.adjust(glyph);
164
165 if (glyph.fWidth) {
166 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
167 glyph.getSubXFixed(),
168 glyph.getSubYFixed()),
169 SkFixedFloorToFixed(fx),
170 SkFixedFloorToFixed(fy),
171 fontScaler);
172 }
173
174 fx += glyph.fAdvanceX;
175 fy += glyph.fAdvanceY;
176 }
177
178 this->finish();
179}
180
jvanverth8c27a182014-10-14 08:45:50 -0700181void GrBitmapTextContext::onDrawPosText(const GrPaint& paint, const SkPaint& skPaint,
joshualitt9853cce2014-11-17 14:22:48 -0800182 const char text[], size_t byteLength,
183 const SkScalar pos[], int scalarsPerPosition,
184 const SkPoint& offset) {
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000185 SkASSERT(byteLength == 0 || text != NULL);
186 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
187
188 // nothing to draw
189 if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) {
190 return;
191 }
192
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000193 this->init(paint, skPaint);
194
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000195 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
196
197 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, &fContext->getMatrix());
198 SkGlyphCache* cache = autoCache.getCache();
199 GrFontScaler* fontScaler = GetGrFontScaler(cache);
200
201 // store original matrix before we reset, so we can use it to transform positions
202 SkMatrix ctm = fContext->getMatrix();
203 GrContext::AutoMatrix autoMatrix;
204 autoMatrix.setIdentity(fContext, &fPaint);
205
jvanverth73f10532014-10-23 11:57:12 -0700206 int numGlyphs = fSkPaint.textToGlyphs(text, byteLength, NULL);
207 fTotalVertexCount = kVerticesPerGlyph*numGlyphs;
208
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000209 const char* stop = text + byteLength;
kkinnunencb9a2c82014-06-12 23:06:28 -0700210 SkTextAlignProc alignProc(fSkPaint.getTextAlign());
fmalita05c4a432014-09-29 06:29:53 -0700211 SkTextMapStateProc tmsProc(ctm, offset, scalarsPerPosition);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000212 SkFixed halfSampleX = 0, halfSampleY = 0;
213
214 if (cache->isSubpixel()) {
215 // maybe we should skip the rounding if linearText is set
216 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(ctm);
217
218 SkFixed fxMask = ~0;
219 SkFixed fyMask = ~0;
220 if (kX_SkAxisAlignment == baseline) {
221 fyMask = 0;
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000222 halfSampleY = SK_FixedHalf;
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000223 } else if (kY_SkAxisAlignment == baseline) {
224 fxMask = 0;
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000225 halfSampleX = SK_FixedHalf;
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000226 }
227
228 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
229 while (text < stop) {
kkinnunencb9a2c82014-06-12 23:06:28 -0700230 SkPoint tmsLoc;
231 tmsProc(pos, &tmsLoc);
232 SkFixed fx = SkScalarToFixed(tmsLoc.fX) + halfSampleX;
233 SkFixed fy = SkScalarToFixed(tmsLoc.fY) + halfSampleY;
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000234
235 const SkGlyph& glyph = glyphCacheProc(cache, &text,
236 fx & fxMask, fy & fyMask);
237
238 if (glyph.fWidth) {
jvanverth0fedb192014-10-08 09:07:27 -0700239 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
240 glyph.getSubXFixed(),
241 glyph.getSubYFixed()),
242 SkFixedFloorToFixed(fx),
243 SkFixedFloorToFixed(fy),
244 fontScaler);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000245 }
246 pos += scalarsPerPosition;
247 }
248 } else {
249 while (text < stop) {
250 const char* currentText = text;
251 const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
252
253 if (metricGlyph.fWidth) {
254 SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
255 SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
kkinnunencb9a2c82014-06-12 23:06:28 -0700256 SkPoint tmsLoc;
257 tmsProc(pos, &tmsLoc);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000258 SkIPoint fixedLoc;
kkinnunencb9a2c82014-06-12 23:06:28 -0700259 alignProc(tmsLoc, metricGlyph, &fixedLoc);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000260
261 SkFixed fx = fixedLoc.fX + halfSampleX;
262 SkFixed fy = fixedLoc.fY + halfSampleY;
263
264 // have to call again, now that we've been "aligned"
265 const SkGlyph& glyph = glyphCacheProc(cache, &currentText,
266 fx & fxMask, fy & fyMask);
267 // the assumption is that the metrics haven't changed
268 SkASSERT(prevAdvX == glyph.fAdvanceX);
269 SkASSERT(prevAdvY == glyph.fAdvanceY);
270 SkASSERT(glyph.fWidth);
271
jvanverth0fedb192014-10-08 09:07:27 -0700272 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
273 glyph.getSubXFixed(),
274 glyph.getSubYFixed()),
275 SkFixedFloorToFixed(fx),
276 SkFixedFloorToFixed(fy),
277 fontScaler);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000278 }
279 pos += scalarsPerPosition;
280 }
281 }
282 } else { // not subpixel
283
284 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
285 while (text < stop) {
286 // the last 2 parameters are ignored
287 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
288
289 if (glyph.fWidth) {
kkinnunencb9a2c82014-06-12 23:06:28 -0700290 SkPoint tmsLoc;
291 tmsProc(pos, &tmsLoc);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000292
kkinnunencb9a2c82014-06-12 23:06:28 -0700293 SkFixed fx = SkScalarToFixed(tmsLoc.fX) + SK_FixedHalf; //halfSampleX;
294 SkFixed fy = SkScalarToFixed(tmsLoc.fY) + SK_FixedHalf; //halfSampleY;
jvanverth0fedb192014-10-08 09:07:27 -0700295 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
296 glyph.getSubXFixed(),
297 glyph.getSubYFixed()),
298 SkFixedFloorToFixed(fx),
299 SkFixedFloorToFixed(fy),
300 fontScaler);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000301 }
302 pos += scalarsPerPosition;
303 }
304 } else {
305 while (text < stop) {
306 // the last 2 parameters are ignored
307 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
308
309 if (glyph.fWidth) {
kkinnunencb9a2c82014-06-12 23:06:28 -0700310 SkPoint tmsLoc;
311 tmsProc(pos, &tmsLoc);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000312
313 SkIPoint fixedLoc;
kkinnunencb9a2c82014-06-12 23:06:28 -0700314 alignProc(tmsLoc, glyph, &fixedLoc);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000315
316 SkFixed fx = fixedLoc.fX + SK_FixedHalf; //halfSampleX;
317 SkFixed fy = fixedLoc.fY + SK_FixedHalf; //halfSampleY;
jvanverth0fedb192014-10-08 09:07:27 -0700318 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
319 glyph.getSubXFixed(),
320 glyph.getSubYFixed()),
321 SkFixedFloorToFixed(fx),
322 SkFixedFloorToFixed(fy),
323 fontScaler);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000324 }
325 pos += scalarsPerPosition;
326 }
327 }
328 }
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000329
330 this->finish();
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000331}
332
joshualitt9853cce2014-11-17 14:22:48 -0800333static size_t get_vertex_stride(GrMaskFormat maskFormat) {
334 switch (maskFormat) {
335 case kA8_GrMaskFormat:
336 return kGrayTextVASize;
337 case kARGB_GrMaskFormat:
338 return kColorTextVASize;
339 default:
340 return kLCDTextVASize;
341 }
342}
343
joshualitt9853cce2014-11-17 14:22:48 -0800344static void* alloc_vertices(GrDrawTarget* drawTarget,
345 int numVertices,
346 GrMaskFormat maskFormat) {
jvanverth73f10532014-10-23 11:57:12 -0700347 if (numVertices <= 0) {
348 return NULL;
349 }
350
351 // set up attributes
jvanverth73f10532014-10-23 11:57:12 -0700352 void* vertices = NULL;
353 bool success = drawTarget->reserveVertexAndIndexSpace(numVertices,
joshualitt9853cce2014-11-17 14:22:48 -0800354 get_vertex_stride(maskFormat),
jvanverth73f10532014-10-23 11:57:12 -0700355 0,
356 &vertices,
357 NULL);
358 GrAlwaysAssert(success);
359 return vertices;
360}
361
jvanverth0fedb192014-10-08 09:07:27 -0700362void GrBitmapTextContext::appendGlyph(GrGlyph::PackedID packed,
363 SkFixed vx, SkFixed vy,
364 GrFontScaler* scaler) {
Mike Klein6a25bd02014-08-29 10:03:59 -0400365 if (NULL == fDrawTarget) {
366 return;
367 }
368
369 if (NULL == fStrike) {
370 fStrike = fContext->getFontCache()->getStrike(scaler, false);
371 }
372
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000373 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
374 if (NULL == glyph || glyph->fBounds.isEmpty()) {
375 return;
376 }
377
378 vx += SkIntToFixed(glyph->fBounds.fLeft);
379 vy += SkIntToFixed(glyph->fBounds.fTop);
380
381 // keep them as ints until we've done the clip-test
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000382 SkFixed width = glyph->fBounds.width();
383 SkFixed height = glyph->fBounds.height();
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000384
385 // check if we clipped out
386 if (true || NULL == glyph->fPlot) {
387 int x = vx >> 16;
388 int y = vy >> 16;
389 if (fClipRect.quickReject(x, y, x + width, y + height)) {
390// SkCLZ(3); // so we can set a break-point in the debugger
391 return;
392 }
393 }
394
395 if (NULL == glyph->fPlot) {
jvanverth681e65b2014-09-19 13:07:38 -0700396 if (!fStrike->glyphTooLargeForAtlas(glyph)) {
397 if (fStrike->addGlyphToAtlas(glyph, scaler)) {
398 goto HAS_ATLAS;
399 }
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000400
jvanverth681e65b2014-09-19 13:07:38 -0700401 // try to clear out an unused plot before we flush
jvanverth294c3262014-10-10 11:36:12 -0700402 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
jvanverth681e65b2014-09-19 13:07:38 -0700403 fStrike->addGlyphToAtlas(glyph, scaler)) {
404 goto HAS_ATLAS;
405 }
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000406
jvanverth681e65b2014-09-19 13:07:38 -0700407 if (c_DumpFontCache) {
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000408#ifdef SK_DEVELOPER
jvanverth681e65b2014-09-19 13:07:38 -0700409 fContext->getFontCache()->dump();
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000410#endif
jvanverth681e65b2014-09-19 13:07:38 -0700411 }
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000412
jvanverth681e65b2014-09-19 13:07:38 -0700413 // flush any accumulated draws to allow us to free up a plot
jvanverth0fedb192014-10-08 09:07:27 -0700414 this->flush();
jvanverth681e65b2014-09-19 13:07:38 -0700415 fContext->flush();
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000416
jvanverth681e65b2014-09-19 13:07:38 -0700417 // we should have an unused plot now
jvanverth294c3262014-10-10 11:36:12 -0700418 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
jvanverth681e65b2014-09-19 13:07:38 -0700419 fStrike->addGlyphToAtlas(glyph, scaler)) {
420 goto HAS_ATLAS;
421 }
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000422 }
423
424 if (NULL == glyph->fPath) {
425 SkPath* path = SkNEW(SkPath);
426 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
427 // flag the glyph as being dead?
428 delete path;
429 return;
430 }
431 glyph->fPath = path;
432 }
433
bsalomonec87dc62014-10-14 10:52:00 -0700434 // flush any accumulated draws before drawing this glyph as a path.
435 this->flush();
436
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000437 GrContext::AutoMatrix am;
438 SkMatrix translate;
439 translate.setTranslate(SkFixedToScalar(vx - SkIntToFixed(glyph->fBounds.fLeft)),
440 SkFixedToScalar(vy - SkIntToFixed(glyph->fBounds.fTop)));
441 GrPaint tmpPaint(fPaint);
442 am.setPreConcat(fContext, translate, &tmpPaint);
egdanield58a0ba2014-06-11 10:30:05 -0700443 GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle);
444 fContext->drawPath(tmpPaint, *glyph->fPath, strokeInfo);
jvanverth73f10532014-10-23 11:57:12 -0700445
446 // remove this glyph from the vertices we need to allocate
447 fTotalVertexCount -= kVerticesPerGlyph;
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000448 return;
449 }
450
451HAS_ATLAS:
452 SkASSERT(glyph->fPlot);
453 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
454 glyph->fPlot->setDrawToken(drawToken);
455
456 // now promote them to fixed (TODO: Rethink using fixed pt).
457 width = SkIntToFixed(width);
458 height = SkIntToFixed(height);
459
jvanverth294c3262014-10-10 11:36:12 -0700460 // the current texture/maskformat must match what the glyph needs
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000461 GrTexture* texture = glyph->fPlot->texture();
462 SkASSERT(texture);
463
jvanverth73f10532014-10-23 11:57:12 -0700464 if (fCurrTexture != texture || fCurrVertex + kVerticesPerGlyph > fAllocVertexCount) {
jvanverth0fedb192014-10-08 09:07:27 -0700465 this->flush();
Mike Klein6a25bd02014-08-29 10:03:59 -0400466 fCurrTexture = texture;
467 fCurrTexture->ref();
jvanverth294c3262014-10-10 11:36:12 -0700468 fCurrMaskFormat = glyph->fMaskFormat;
Mike Klein6a25bd02014-08-29 10:03:59 -0400469 }
470
Mike Klein6a25bd02014-08-29 10:03:59 -0400471 if (NULL == fVertices) {
jvanverth73f10532014-10-23 11:57:12 -0700472 int maxQuadVertices = kVerticesPerGlyph * fContext->getQuadIndexBuffer()->maxQuads();
473 fAllocVertexCount = SkMin32(fTotalVertexCount, maxQuadVertices);
egdanieled3af662014-10-31 06:55:45 -0700474 fVertices = alloc_vertices(fDrawTarget, fAllocVertexCount, fCurrMaskFormat);
Mike Klein6a25bd02014-08-29 10:03:59 -0400475 }
476
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000477 SkFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX);
478 SkFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY);
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000479
commit-bot@chromium.org3ae0e6c2014-02-11 18:24:25 +0000480 SkRect r;
481 r.fLeft = SkFixedToFloat(vx);
482 r.fTop = SkFixedToFloat(vy);
483 r.fRight = SkFixedToFloat(vx + width);
484 r.fBottom = SkFixedToFloat(vy + height);
485
reed10d03272014-10-01 09:24:12 -0700486 fVertexBounds.joinNonEmptyArg(r);
commit-bot@chromium.org3ae0e6c2014-02-11 18:24:25 +0000487
joshualitt9853cce2014-11-17 14:22:48 -0800488 size_t vertSize = get_vertex_stride(fCurrMaskFormat);
bsalomon594069f2014-06-06 06:16:34 -0700489
490 SkPoint* positions = reinterpret_cast<SkPoint*>(
491 reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVertex);
492 positions->setRectFan(r.fLeft, r.fTop, r.fRight, r.fBottom, vertSize);
493
494 // The texture coords are last in both the with and without color vertex layouts.
495 SkPoint* textureCoords = reinterpret_cast<SkPoint*>(
496 reinterpret_cast<intptr_t>(positions) + vertSize - sizeof(SkPoint));
bsalomonafbf2d62014-09-30 12:18:44 -0700497 textureCoords->setRectFan(SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx)),
498 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty)),
499 SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx + width)),
500 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty + height)),
bsalomon594069f2014-06-06 06:16:34 -0700501 vertSize);
egdanieled3af662014-10-31 06:55:45 -0700502 if (kA8_GrMaskFormat == fCurrMaskFormat) {
bsalomon594069f2014-06-06 06:16:34 -0700503 // color comes after position.
504 GrColor* colors = reinterpret_cast<GrColor*>(positions + 1);
505 for (int i = 0; i < 4; ++i) {
506 *colors = fPaint.getColor();
507 colors = reinterpret_cast<GrColor*>(reinterpret_cast<intptr_t>(colors) + vertSize);
508 }
509 }
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000510 fCurrVertex += 4;
511}
jvanverth0fedb192014-10-08 09:07:27 -0700512
513static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
514 unsigned r = SkColorGetR(c);
515 unsigned g = SkColorGetG(c);
516 unsigned b = SkColorGetB(c);
517 return GrColorPackRGBA(r, g, b, 0xff);
518}
519
520void GrBitmapTextContext::flush() {
521 if (NULL == fDrawTarget) {
522 return;
523 }
524
jvanverth0fedb192014-10-08 09:07:27 -0700525 if (fCurrVertex > 0) {
joshualitt780b11e2014-11-18 09:40:40 -0800526 GrDrawState drawState;
527 drawState.setFromPaint(fPaint, SkMatrix::I(), fContext->getRenderTarget());
528
jvanverth0fedb192014-10-08 09:07:27 -0700529 // setup our sampler state for our text texture/atlas
530 SkASSERT(SkIsAlign4(fCurrVertex));
531 SkASSERT(fCurrTexture);
jvanverth0fedb192014-10-08 09:07:27 -0700532
egdanieled3af662014-10-31 06:55:45 -0700533 // This effect could be stored with one of the cache objects (atlas?)
joshualitt2dd1ae02014-12-03 06:24:10 -0800534 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kNone_FilterMode);
egdanieled3af662014-10-31 06:55:45 -0700535 if (kARGB_GrMaskFormat == fCurrMaskFormat) {
joshualitt2dd1ae02014-12-03 06:24:10 -0800536 uint32_t flags = GrDefaultGeoProcFactory::kLocalCoord_GPType;
537 drawState.setGeometryProcessor(GrDefaultGeoProcFactory::Create(flags))->unref();
egdanieled3af662014-10-31 06:55:45 -0700538 GrFragmentProcessor* fragProcessor = GrSimpleTextureEffect::Create(fCurrTexture,
539 SkMatrix::I(),
540 params);
joshualitt9853cce2014-11-17 14:22:48 -0800541 drawState.addColorProcessor(fragProcessor)->unref();
egdanieled3af662014-10-31 06:55:45 -0700542 } else {
543 uint32_t textureUniqueID = fCurrTexture->getUniqueID();
544 if (textureUniqueID != fEffectTextureUniqueID) {
joshualitt2dd1ae02014-12-03 06:24:10 -0800545 bool hasColor = kA8_GrMaskFormat == fCurrMaskFormat;
egdanieled3af662014-10-31 06:55:45 -0700546 fCachedGeometryProcessor.reset(GrCustomCoordsTextureEffect::Create(fCurrTexture,
joshualitt2dd1ae02014-12-03 06:24:10 -0800547 params,
548 hasColor));
egdanieled3af662014-10-31 06:55:45 -0700549 fEffectTextureUniqueID = textureUniqueID;
550 }
joshualitt9853cce2014-11-17 14:22:48 -0800551 drawState.setGeometryProcessor(fCachedGeometryProcessor.get());
jvanverth0fedb192014-10-08 09:07:27 -0700552 }
553
jvanverth0fedb192014-10-08 09:07:27 -0700554 SkASSERT(fStrike);
jvanverth294c3262014-10-10 11:36:12 -0700555 switch (fCurrMaskFormat) {
jvanverth0fedb192014-10-08 09:07:27 -0700556 // Color bitmap text
557 case kARGB_GrMaskFormat:
joshualitt9853cce2014-11-17 14:22:48 -0800558 SkASSERT(!drawState.hasColorVertexAttribute());
559 drawState.setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
560 drawState.setAlpha(fSkPaint.getAlpha());
jvanverth0fedb192014-10-08 09:07:27 -0700561 break;
562 // LCD text
jvanverth0fedb192014-10-08 09:07:27 -0700563 case kA565_GrMaskFormat: {
egdaniel378092f2014-12-03 10:40:13 -0800564 // TODO: move supportsRGBCoverage check to setupCoverageEffect and only add LCD
565 // processor if the xp can support it. For now we will simply assume that if
566 // fUseLCDText is true, then we have a known color output.
567 if (!drawState.getXPFactory()->supportsRGBCoverage(0, kRGBA_GrColorComponentFlags)) {
tfarina38406c82014-10-31 07:11:12 -0700568 SkDebugf("LCD Text will not draw correctly.\n");
jvanverth0fedb192014-10-08 09:07:27 -0700569 }
joshualitt9853cce2014-11-17 14:22:48 -0800570 SkASSERT(!drawState.hasColorVertexAttribute());
jvanverth0fedb192014-10-08 09:07:27 -0700571 // We don't use the GrPaint's color in this case because it's been premultiplied by
572 // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by
573 // the mask texture color. The end result is that we get
574 // mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor
575 int a = SkColorGetA(fSkPaint.getColor());
576 // paintAlpha
joshualitt9853cce2014-11-17 14:22:48 -0800577 drawState.setColor(SkColorSetARGB(a, a, a, a));
jvanverth0fedb192014-10-08 09:07:27 -0700578 // paintColor
joshualitt9853cce2014-11-17 14:22:48 -0800579 drawState.setBlendConstant(skcolor_to_grcolor_nopremultiply(fSkPaint.getColor()));
580 drawState.setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
jvanverth0fedb192014-10-08 09:07:27 -0700581 break;
582 }
583 // Grayscale/BW text
584 case kA8_GrMaskFormat:
joshualitt9853cce2014-11-17 14:22:48 -0800585 drawState.setHint(GrDrawState::kVertexColorsAreOpaque_Hint,
586 0xFF == GrColorUnpackA(fPaint.getColor()));
jvanverth0fedb192014-10-08 09:07:27 -0700587 // set back to normal in case we took LCD path previously.
joshualitt9853cce2014-11-17 14:22:48 -0800588 drawState.setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
jvanverth0fedb192014-10-08 09:07:27 -0700589 // We're using per-vertex color.
joshualitt9853cce2014-11-17 14:22:48 -0800590 SkASSERT(drawState.hasColorVertexAttribute());
jvanverth0fedb192014-10-08 09:07:27 -0700591 break;
592 default:
jvanverth294c3262014-10-10 11:36:12 -0700593 SkFAIL("Unexpected mask format.");
jvanverth0fedb192014-10-08 09:07:27 -0700594 }
jvanverth73f10532014-10-23 11:57:12 -0700595 int nGlyphs = fCurrVertex / kVerticesPerGlyph;
jvanverth0fedb192014-10-08 09:07:27 -0700596 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
joshualitt9853cce2014-11-17 14:22:48 -0800597 fDrawTarget->drawIndexedInstances(&drawState,
598 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