blob: afecead65d28446d75417663446bf6cbc2c59f51 [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"
egdanield58a0ba2014-06-11 10:30:05 -070013#include "GrStrokeInfo.h"
bsalomonafbf2d62014-09-30 12:18:44 -070014#include "GrTexturePriv.h"
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000015#include "GrTextStrike.h"
16#include "GrTextStrike_impl.h"
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000017#include "effects/GrCustomCoordsTextureEffect.h"
18
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
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000031SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
32 "Dump the contents of the font cache before every purge.");
33
bsalomon594069f2014-06-06 06:16:34 -070034namespace {
35// position + texture coord
36extern const GrVertexAttrib gTextVertexAttribs[] = {
37 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
joshualittb0a8a372014-09-23 09:50:21 -070038 {kVec2f_GrVertexAttribType, sizeof(SkPoint), kGeometryProcessor_GrVertexAttribBinding}
bsalomon594069f2014-06-06 06:16:34 -070039};
40
egdaniel7b3d5ee2014-08-28 05:41:14 -070041static const size_t kTextVASize = 2 * sizeof(SkPoint);
42
bsalomon594069f2014-06-06 06:16:34 -070043// position + color + texture coord
44extern const GrVertexAttrib gTextVertexWithColorAttribs[] = {
45 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
46 {kVec4ub_GrVertexAttribType, sizeof(SkPoint), kColor_GrVertexAttribBinding},
joshualittb0a8a372014-09-23 09:50:21 -070047 {kVec2f_GrVertexAttribType, sizeof(SkPoint) + sizeof(GrColor), kGeometryProcessor_GrVertexAttribBinding}
bsalomon594069f2014-06-06 06:16:34 -070048};
49
jvanverth73f10532014-10-23 11:57:12 -070050static const size_t kTextVAColorSize = 2 * sizeof(SkPoint) + sizeof(GrColor);
egdaniel7b3d5ee2014-08-28 05:41:14 -070051
jvanverth73f10532014-10-23 11:57:12 -070052static const int kVerticesPerGlyph = 4;
53static const int kIndicesPerGlyph = 6;
bsalomon594069f2014-06-06 06:16:34 -070054};
55
skia.committer@gmail.come5d70152014-01-29 07:01:48 +000056GrBitmapTextContext::GrBitmapTextContext(GrContext* context,
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +000057 const SkDeviceProperties& properties)
Mike Klein6a25bd02014-08-29 10:03:59 -040058 : GrTextContext(context, properties) {
59 fStrike = NULL;
60
61 fCurrTexture = NULL;
Mike Klein6a25bd02014-08-29 10:03:59 -040062 fEffectTextureUniqueID = SK_InvalidUniqueID;
63
64 fVertices = NULL;
jvanverth73f10532014-10-23 11:57:12 -070065 fCurrVertex = 0;
66 fAllocVertexCount = 0;
67 fTotalVertexCount = 0;
Mike Klein6a25bd02014-08-29 10:03:59 -040068
commit-bot@chromium.org3ae0e6c2014-02-11 18:24:25 +000069 fVertexBounds.setLargestInverted();
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000070}
71
jvanverth8c27a182014-10-14 08:45:50 -070072GrBitmapTextContext* GrBitmapTextContext::Create(GrContext* context,
73 const SkDeviceProperties& props) {
74 return SkNEW_ARGS(GrBitmapTextContext, (context, props));
75}
76
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000077GrBitmapTextContext::~GrBitmapTextContext() {
jvanverth73f10532014-10-23 11:57:12 -070078 this->finish();
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000079}
80
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000081bool GrBitmapTextContext::canDraw(const SkPaint& paint) {
82 return !SkDraw::ShouldDrawTextAsPaths(paint, fContext->getMatrix());
83}
84
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000085inline void GrBitmapTextContext::init(const GrPaint& paint, const SkPaint& skPaint) {
86 GrTextContext::init(paint, skPaint);
87
88 fStrike = NULL;
89
Mike Klein6a25bd02014-08-29 10:03:59 -040090 fCurrTexture = NULL;
jvanverth63b9dc82014-08-28 10:39:40 -070091 fCurrVertex = 0;
Mike Klein6a25bd02014-08-29 10:03:59 -040092
93 fVertices = NULL;
jvanverth73f10532014-10-23 11:57:12 -070094 fAllocVertexCount = 0;
95 fTotalVertexCount = 0;
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000096}
97
jvanverthaab626c2014-10-16 08:04:39 -070098void GrBitmapTextContext::onDrawText(const GrPaint& paint, const SkPaint& skPaint,
99 const char text[], size_t byteLength,
100 SkScalar x, SkScalar y) {
101 SkASSERT(byteLength == 0 || text != NULL);
102
103 // nothing to draw
104 if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) {
105 return;
106 }
107
108 this->init(paint, skPaint);
109
110 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
111
112 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, &fContext->getMatrix());
113 SkGlyphCache* cache = autoCache.getCache();
114 GrFontScaler* fontScaler = GetGrFontScaler(cache);
115
116 // transform our starting point
117 {
118 SkPoint loc;
119 fContext->getMatrix().mapXY(x, y, &loc);
120 x = loc.fX;
121 y = loc.fY;
122 }
123
124 // need to measure first
jvanverth73f10532014-10-23 11:57:12 -0700125 int numGlyphs;
jvanverthaab626c2014-10-16 08:04:39 -0700126 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
jvanverth73f10532014-10-23 11:57:12 -0700127 SkVector stopVector;
128 numGlyphs = MeasureText(cache, glyphCacheProc, text, byteLength, &stopVector);
jvanverthaab626c2014-10-16 08:04:39 -0700129
jvanverth73f10532014-10-23 11:57:12 -0700130 SkScalar stopX = stopVector.fX;
131 SkScalar stopY = stopVector.fY;
jvanverthaab626c2014-10-16 08:04:39 -0700132
133 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
134 stopX = SkScalarHalf(stopX);
135 stopY = SkScalarHalf(stopY);
136 }
137 x -= stopX;
138 y -= stopY;
jvanverth73f10532014-10-23 11:57:12 -0700139 } else {
140 numGlyphs = fSkPaint.textToGlyphs(text, byteLength, NULL);
jvanverthaab626c2014-10-16 08:04:39 -0700141 }
jvanverth73f10532014-10-23 11:57:12 -0700142 fTotalVertexCount = kVerticesPerGlyph*numGlyphs;
jvanverthaab626c2014-10-16 08:04:39 -0700143
144 const char* stop = text + byteLength;
145
146 SkAutoKern autokern;
147
148 SkFixed fxMask = ~0;
149 SkFixed fyMask = ~0;
150 SkFixed halfSampleX, halfSampleY;
151 if (cache->isSubpixel()) {
152 halfSampleX = halfSampleY = (SK_FixedHalf >> SkGlyph::kSubBits);
153 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(fContext->getMatrix());
154 if (kX_SkAxisAlignment == baseline) {
155 fyMask = 0;
156 halfSampleY = SK_FixedHalf;
157 } else if (kY_SkAxisAlignment == baseline) {
158 fxMask = 0;
159 halfSampleX = SK_FixedHalf;
160 }
161 } else {
162 halfSampleX = halfSampleY = SK_FixedHalf;
163 }
164
165 SkFixed fx = SkScalarToFixed(x) + halfSampleX;
166 SkFixed fy = SkScalarToFixed(y) + halfSampleY;
167
168 GrContext::AutoMatrix autoMatrix;
169 autoMatrix.setIdentity(fContext, &fPaint);
170
171 while (text < stop) {
172 const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
173
174 fx += autokern.adjust(glyph);
175
176 if (glyph.fWidth) {
177 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
178 glyph.getSubXFixed(),
179 glyph.getSubYFixed()),
180 SkFixedFloorToFixed(fx),
181 SkFixedFloorToFixed(fy),
182 fontScaler);
183 }
184
185 fx += glyph.fAdvanceX;
186 fy += glyph.fAdvanceY;
187 }
188
189 this->finish();
190}
191
jvanverth8c27a182014-10-14 08:45:50 -0700192void GrBitmapTextContext::onDrawPosText(const GrPaint& paint, const SkPaint& skPaint,
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000193 const char text[], size_t byteLength,
fmalita05c4a432014-09-29 06:29:53 -0700194 const SkScalar pos[], int scalarsPerPosition,
195 const SkPoint& offset) {
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000196 SkASSERT(byteLength == 0 || text != NULL);
197 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
198
199 // nothing to draw
200 if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) {
201 return;
202 }
203
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000204 this->init(paint, skPaint);
205
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000206 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
207
208 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, &fContext->getMatrix());
209 SkGlyphCache* cache = autoCache.getCache();
210 GrFontScaler* fontScaler = GetGrFontScaler(cache);
211
212 // store original matrix before we reset, so we can use it to transform positions
213 SkMatrix ctm = fContext->getMatrix();
214 GrContext::AutoMatrix autoMatrix;
215 autoMatrix.setIdentity(fContext, &fPaint);
216
jvanverth73f10532014-10-23 11:57:12 -0700217 int numGlyphs = fSkPaint.textToGlyphs(text, byteLength, NULL);
218 fTotalVertexCount = kVerticesPerGlyph*numGlyphs;
219
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000220 const char* stop = text + byteLength;
kkinnunencb9a2c82014-06-12 23:06:28 -0700221 SkTextAlignProc alignProc(fSkPaint.getTextAlign());
fmalita05c4a432014-09-29 06:29:53 -0700222 SkTextMapStateProc tmsProc(ctm, offset, scalarsPerPosition);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000223 SkFixed halfSampleX = 0, halfSampleY = 0;
224
225 if (cache->isSubpixel()) {
226 // maybe we should skip the rounding if linearText is set
227 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(ctm);
228
229 SkFixed fxMask = ~0;
230 SkFixed fyMask = ~0;
231 if (kX_SkAxisAlignment == baseline) {
232 fyMask = 0;
233#ifndef SK_IGNORE_SUBPIXEL_AXIS_ALIGN_FIX
234 halfSampleY = SK_FixedHalf;
235#endif
236 } else if (kY_SkAxisAlignment == baseline) {
237 fxMask = 0;
238#ifndef SK_IGNORE_SUBPIXEL_AXIS_ALIGN_FIX
239 halfSampleX = SK_FixedHalf;
240#endif
241 }
242
243 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
244 while (text < stop) {
kkinnunencb9a2c82014-06-12 23:06:28 -0700245 SkPoint tmsLoc;
246 tmsProc(pos, &tmsLoc);
247 SkFixed fx = SkScalarToFixed(tmsLoc.fX) + halfSampleX;
248 SkFixed fy = SkScalarToFixed(tmsLoc.fY) + halfSampleY;
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000249
250 const SkGlyph& glyph = glyphCacheProc(cache, &text,
251 fx & fxMask, fy & fyMask);
252
253 if (glyph.fWidth) {
jvanverth0fedb192014-10-08 09:07:27 -0700254 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
255 glyph.getSubXFixed(),
256 glyph.getSubYFixed()),
257 SkFixedFloorToFixed(fx),
258 SkFixedFloorToFixed(fy),
259 fontScaler);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000260 }
261 pos += scalarsPerPosition;
262 }
263 } else {
264 while (text < stop) {
265 const char* currentText = text;
266 const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
267
268 if (metricGlyph.fWidth) {
269 SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
270 SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
kkinnunencb9a2c82014-06-12 23:06:28 -0700271 SkPoint tmsLoc;
272 tmsProc(pos, &tmsLoc);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000273 SkIPoint fixedLoc;
kkinnunencb9a2c82014-06-12 23:06:28 -0700274 alignProc(tmsLoc, metricGlyph, &fixedLoc);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000275
276 SkFixed fx = fixedLoc.fX + halfSampleX;
277 SkFixed fy = fixedLoc.fY + halfSampleY;
278
279 // have to call again, now that we've been "aligned"
280 const SkGlyph& glyph = glyphCacheProc(cache, &currentText,
281 fx & fxMask, fy & fyMask);
282 // the assumption is that the metrics haven't changed
283 SkASSERT(prevAdvX == glyph.fAdvanceX);
284 SkASSERT(prevAdvY == glyph.fAdvanceY);
285 SkASSERT(glyph.fWidth);
286
jvanverth0fedb192014-10-08 09:07:27 -0700287 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
288 glyph.getSubXFixed(),
289 glyph.getSubYFixed()),
290 SkFixedFloorToFixed(fx),
291 SkFixedFloorToFixed(fy),
292 fontScaler);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000293 }
294 pos += scalarsPerPosition;
295 }
296 }
297 } else { // not subpixel
298
299 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
300 while (text < stop) {
301 // the last 2 parameters are ignored
302 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
303
304 if (glyph.fWidth) {
kkinnunencb9a2c82014-06-12 23:06:28 -0700305 SkPoint tmsLoc;
306 tmsProc(pos, &tmsLoc);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000307
kkinnunencb9a2c82014-06-12 23:06:28 -0700308 SkFixed fx = SkScalarToFixed(tmsLoc.fX) + SK_FixedHalf; //halfSampleX;
309 SkFixed fy = SkScalarToFixed(tmsLoc.fY) + SK_FixedHalf; //halfSampleY;
jvanverth0fedb192014-10-08 09:07:27 -0700310 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
311 glyph.getSubXFixed(),
312 glyph.getSubYFixed()),
313 SkFixedFloorToFixed(fx),
314 SkFixedFloorToFixed(fy),
315 fontScaler);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000316 }
317 pos += scalarsPerPosition;
318 }
319 } else {
320 while (text < stop) {
321 // the last 2 parameters are ignored
322 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
323
324 if (glyph.fWidth) {
kkinnunencb9a2c82014-06-12 23:06:28 -0700325 SkPoint tmsLoc;
326 tmsProc(pos, &tmsLoc);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000327
328 SkIPoint fixedLoc;
kkinnunencb9a2c82014-06-12 23:06:28 -0700329 alignProc(tmsLoc, glyph, &fixedLoc);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000330
331 SkFixed fx = fixedLoc.fX + SK_FixedHalf; //halfSampleX;
332 SkFixed fy = fixedLoc.fY + SK_FixedHalf; //halfSampleY;
jvanverth0fedb192014-10-08 09:07:27 -0700333 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
334 glyph.getSubXFixed(),
335 glyph.getSubYFixed()),
336 SkFixedFloorToFixed(fx),
337 SkFixedFloorToFixed(fy),
338 fontScaler);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000339 }
340 pos += scalarsPerPosition;
341 }
342 }
343 }
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000344
345 this->finish();
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000346}
347
jvanverth73f10532014-10-23 11:57:12 -0700348static void* alloc_vertices(GrDrawTarget* drawTarget, int numVertices, bool useColorVerts) {
349 if (numVertices <= 0) {
350 return NULL;
351 }
352
353 // set up attributes
354 if (useColorVerts) {
355 drawTarget->drawState()->setVertexAttribs<gTextVertexWithColorAttribs>(
356 SK_ARRAY_COUNT(gTextVertexWithColorAttribs), kTextVAColorSize);
357 } else {
358 drawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
359 SK_ARRAY_COUNT(gTextVertexAttribs), kTextVASize);
360 }
361 void* vertices = NULL;
362 bool success = drawTarget->reserveVertexAndIndexSpace(numVertices,
363 0,
364 &vertices,
365 NULL);
366 GrAlwaysAssert(success);
367 return vertices;
368}
369
jvanverth0fedb192014-10-08 09:07:27 -0700370void GrBitmapTextContext::appendGlyph(GrGlyph::PackedID packed,
371 SkFixed vx, SkFixed vy,
372 GrFontScaler* scaler) {
Mike Klein6a25bd02014-08-29 10:03:59 -0400373 if (NULL == fDrawTarget) {
374 return;
375 }
376
377 if (NULL == fStrike) {
378 fStrike = fContext->getFontCache()->getStrike(scaler, false);
379 }
380
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000381 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
382 if (NULL == glyph || glyph->fBounds.isEmpty()) {
383 return;
384 }
385
386 vx += SkIntToFixed(glyph->fBounds.fLeft);
387 vy += SkIntToFixed(glyph->fBounds.fTop);
388
389 // keep them as ints until we've done the clip-test
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000390 SkFixed width = glyph->fBounds.width();
391 SkFixed height = glyph->fBounds.height();
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000392
393 // check if we clipped out
394 if (true || NULL == glyph->fPlot) {
395 int x = vx >> 16;
396 int y = vy >> 16;
397 if (fClipRect.quickReject(x, y, x + width, y + height)) {
398// SkCLZ(3); // so we can set a break-point in the debugger
399 return;
400 }
401 }
402
403 if (NULL == glyph->fPlot) {
jvanverth681e65b2014-09-19 13:07:38 -0700404 if (!fStrike->glyphTooLargeForAtlas(glyph)) {
405 if (fStrike->addGlyphToAtlas(glyph, scaler)) {
406 goto HAS_ATLAS;
407 }
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000408
jvanverth681e65b2014-09-19 13:07:38 -0700409 // try to clear out an unused plot before we flush
jvanverth294c3262014-10-10 11:36:12 -0700410 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
jvanverth681e65b2014-09-19 13:07:38 -0700411 fStrike->addGlyphToAtlas(glyph, scaler)) {
412 goto HAS_ATLAS;
413 }
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000414
jvanverth681e65b2014-09-19 13:07:38 -0700415 if (c_DumpFontCache) {
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000416#ifdef SK_DEVELOPER
jvanverth681e65b2014-09-19 13:07:38 -0700417 fContext->getFontCache()->dump();
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000418#endif
jvanverth681e65b2014-09-19 13:07:38 -0700419 }
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000420
jvanverth681e65b2014-09-19 13:07:38 -0700421 // flush any accumulated draws to allow us to free up a plot
jvanverth0fedb192014-10-08 09:07:27 -0700422 this->flush();
jvanverth681e65b2014-09-19 13:07:38 -0700423 fContext->flush();
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000424
jvanverth681e65b2014-09-19 13:07:38 -0700425 // we should have an unused plot now
jvanverth294c3262014-10-10 11:36:12 -0700426 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
jvanverth681e65b2014-09-19 13:07:38 -0700427 fStrike->addGlyphToAtlas(glyph, scaler)) {
428 goto HAS_ATLAS;
429 }
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000430 }
431
432 if (NULL == glyph->fPath) {
433 SkPath* path = SkNEW(SkPath);
434 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
435 // flag the glyph as being dead?
436 delete path;
437 return;
438 }
439 glyph->fPath = path;
440 }
441
bsalomonec87dc62014-10-14 10:52:00 -0700442 // flush any accumulated draws before drawing this glyph as a path.
443 this->flush();
444
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000445 GrContext::AutoMatrix am;
446 SkMatrix translate;
447 translate.setTranslate(SkFixedToScalar(vx - SkIntToFixed(glyph->fBounds.fLeft)),
448 SkFixedToScalar(vy - SkIntToFixed(glyph->fBounds.fTop)));
449 GrPaint tmpPaint(fPaint);
450 am.setPreConcat(fContext, translate, &tmpPaint);
egdanield58a0ba2014-06-11 10:30:05 -0700451 GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle);
452 fContext->drawPath(tmpPaint, *glyph->fPath, strokeInfo);
jvanverth73f10532014-10-23 11:57:12 -0700453
454 // remove this glyph from the vertices we need to allocate
455 fTotalVertexCount -= kVerticesPerGlyph;
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000456 return;
457 }
458
459HAS_ATLAS:
460 SkASSERT(glyph->fPlot);
461 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
462 glyph->fPlot->setDrawToken(drawToken);
463
464 // now promote them to fixed (TODO: Rethink using fixed pt).
465 width = SkIntToFixed(width);
466 height = SkIntToFixed(height);
467
jvanverth294c3262014-10-10 11:36:12 -0700468 // the current texture/maskformat must match what the glyph needs
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000469 GrTexture* texture = glyph->fPlot->texture();
470 SkASSERT(texture);
471
jvanverth73f10532014-10-23 11:57:12 -0700472 if (fCurrTexture != texture || fCurrVertex + kVerticesPerGlyph > fAllocVertexCount) {
jvanverth0fedb192014-10-08 09:07:27 -0700473 this->flush();
Mike Klein6a25bd02014-08-29 10:03:59 -0400474 fCurrTexture = texture;
475 fCurrTexture->ref();
jvanverth294c3262014-10-10 11:36:12 -0700476 fCurrMaskFormat = glyph->fMaskFormat;
Mike Klein6a25bd02014-08-29 10:03:59 -0400477 }
478
jvanverth294c3262014-10-10 11:36:12 -0700479 bool useColorVerts = kA8_GrMaskFormat == fCurrMaskFormat;
Mike Klein6a25bd02014-08-29 10:03:59 -0400480
481 if (NULL == fVertices) {
jvanverth73f10532014-10-23 11:57:12 -0700482 int maxQuadVertices = kVerticesPerGlyph * fContext->getQuadIndexBuffer()->maxQuads();
483 fAllocVertexCount = SkMin32(fTotalVertexCount, maxQuadVertices);
484 fVertices = alloc_vertices(fDrawTarget, fAllocVertexCount, useColorVerts);
Mike Klein6a25bd02014-08-29 10:03:59 -0400485 }
486
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000487 SkFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX);
488 SkFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY);
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000489
commit-bot@chromium.org3ae0e6c2014-02-11 18:24:25 +0000490 SkRect r;
491 r.fLeft = SkFixedToFloat(vx);
492 r.fTop = SkFixedToFloat(vy);
493 r.fRight = SkFixedToFloat(vx + width);
494 r.fBottom = SkFixedToFloat(vy + height);
495
reed10d03272014-10-01 09:24:12 -0700496 fVertexBounds.joinNonEmptyArg(r);
commit-bot@chromium.org3ae0e6c2014-02-11 18:24:25 +0000497
bsalomon594069f2014-06-06 06:16:34 -0700498 size_t vertSize = useColorVerts ? (2 * sizeof(SkPoint) + sizeof(GrColor)) :
499 (2 * sizeof(SkPoint));
500
egdaniel7b3d5ee2014-08-28 05:41:14 -0700501 SkASSERT(vertSize == fDrawTarget->getDrawState().getVertexStride());
bsalomon594069f2014-06-06 06:16:34 -0700502
503 SkPoint* positions = reinterpret_cast<SkPoint*>(
504 reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVertex);
505 positions->setRectFan(r.fLeft, r.fTop, r.fRight, r.fBottom, vertSize);
506
507 // The texture coords are last in both the with and without color vertex layouts.
508 SkPoint* textureCoords = reinterpret_cast<SkPoint*>(
509 reinterpret_cast<intptr_t>(positions) + vertSize - sizeof(SkPoint));
bsalomonafbf2d62014-09-30 12:18:44 -0700510 textureCoords->setRectFan(SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx)),
511 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty)),
512 SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx + width)),
513 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty + height)),
bsalomon594069f2014-06-06 06:16:34 -0700514 vertSize);
515 if (useColorVerts) {
bsalomon62c447d2014-08-08 08:08:50 -0700516 if (0xFF == GrColorUnpackA(fPaint.getColor())) {
517 fDrawTarget->drawState()->setHint(GrDrawState::kVertexColorsAreOpaque_Hint, true);
518 }
bsalomon594069f2014-06-06 06:16:34 -0700519 // color comes after position.
520 GrColor* colors = reinterpret_cast<GrColor*>(positions + 1);
521 for (int i = 0; i < 4; ++i) {
522 *colors = fPaint.getColor();
523 colors = reinterpret_cast<GrColor*>(reinterpret_cast<intptr_t>(colors) + vertSize);
524 }
525 }
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000526 fCurrVertex += 4;
527}
jvanverth0fedb192014-10-08 09:07:27 -0700528
529static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
530 unsigned r = SkColorGetR(c);
531 unsigned g = SkColorGetG(c);
532 unsigned b = SkColorGetB(c);
533 return GrColorPackRGBA(r, g, b, 0xff);
534}
535
536void GrBitmapTextContext::flush() {
537 if (NULL == fDrawTarget) {
538 return;
539 }
540
541 GrDrawState* drawState = fDrawTarget->drawState();
542 GrDrawState::AutoRestoreEffects are(drawState);
543 drawState->setFromPaint(fPaint, SkMatrix::I(), fContext->getRenderTarget());
544
545 if (fCurrVertex > 0) {
546 // setup our sampler state for our text texture/atlas
547 SkASSERT(SkIsAlign4(fCurrVertex));
548 SkASSERT(fCurrTexture);
549 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kNone_FilterMode);
550
551 uint32_t textureUniqueID = fCurrTexture->getUniqueID();
552
553 if (textureUniqueID != fEffectTextureUniqueID) {
554 fCachedGeometryProcessor.reset(GrCustomCoordsTextureEffect::Create(fCurrTexture,
555 params));
556 fEffectTextureUniqueID = textureUniqueID;
557 }
558
559 // This effect could be stored with one of the cache objects (atlas?)
560 drawState->setGeometryProcessor(fCachedGeometryProcessor.get());
561 SkASSERT(fStrike);
jvanverth294c3262014-10-10 11:36:12 -0700562 switch (fCurrMaskFormat) {
jvanverth0fedb192014-10-08 09:07:27 -0700563 // Color bitmap text
564 case kARGB_GrMaskFormat:
565 SkASSERT(!drawState->hasColorVertexAttribute());
566 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
jvanverth294c3262014-10-10 11:36:12 -0700567 drawState->setAlpha(fSkPaint.getAlpha());
jvanverth0fedb192014-10-08 09:07:27 -0700568 break;
569 // LCD text
570 case kA888_GrMaskFormat:
571 case kA565_GrMaskFormat: {
572 if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
573 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
574 fPaint.numColorStages()) {
575 GrPrintf("LCD Text will not draw correctly.\n");
576 }
577 SkASSERT(!drawState->hasColorVertexAttribute());
578 // We don't use the GrPaint's color in this case because it's been premultiplied by
579 // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by
580 // the mask texture color. The end result is that we get
581 // mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor
582 int a = SkColorGetA(fSkPaint.getColor());
583 // paintAlpha
584 drawState->setColor(SkColorSetARGB(a, a, a, a));
585 // paintColor
586 drawState->setBlendConstant(skcolor_to_grcolor_nopremultiply(fSkPaint.getColor()));
587 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
588 break;
589 }
590 // Grayscale/BW text
591 case kA8_GrMaskFormat:
592 // set back to normal in case we took LCD path previously.
593 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
594 // We're using per-vertex color.
595 SkASSERT(drawState->hasColorVertexAttribute());
596 break;
597 default:
jvanverth294c3262014-10-10 11:36:12 -0700598 SkFAIL("Unexpected mask format.");
jvanverth0fedb192014-10-08 09:07:27 -0700599 }
jvanverth73f10532014-10-23 11:57:12 -0700600 int nGlyphs = fCurrVertex / kVerticesPerGlyph;
jvanverth0fedb192014-10-08 09:07:27 -0700601 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
602 fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
603 nGlyphs,
jvanverth73f10532014-10-23 11:57:12 -0700604 kVerticesPerGlyph, kIndicesPerGlyph, &fVertexBounds);
jvanverth0fedb192014-10-08 09:07:27 -0700605
606 fDrawTarget->resetVertexSource();
607 fVertices = NULL;
jvanverth73f10532014-10-23 11:57:12 -0700608 fAllocVertexCount = 0;
609 // reset to be those that are left
610 fTotalVertexCount -= fCurrVertex;
jvanverth0fedb192014-10-08 09:07:27 -0700611 fCurrVertex = 0;
612 fVertexBounds.setLargestInverted();
613 SkSafeSetNull(fCurrTexture);
614 }
615}
616
617inline void GrBitmapTextContext::finish() {
618 this->flush();
jvanverth73f10532014-10-23 11:57:12 -0700619 fTotalVertexCount = 0;
jvanverth0fedb192014-10-08 09:07:27 -0700620
621 GrTextContext::finish();
622}
623