blob: 8620e97b190fce128573576a03345490ba499a76 [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
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +000019#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
egdaniel7b3d5ee2014-08-28 05:41:14 -070050static const size_t kTextVAColorSize = 2 * sizeof(SkPoint) + sizeof(GrColor);
51
bsalomon594069f2014-06-06 06:16:34 -070052};
53
skia.committer@gmail.come5d70152014-01-29 07:01:48 +000054GrBitmapTextContext::GrBitmapTextContext(GrContext* context,
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +000055 const SkDeviceProperties& properties)
Mike Klein6a25bd02014-08-29 10:03:59 -040056 : GrTextContext(context, properties) {
57 fStrike = NULL;
58
59 fCurrTexture = NULL;
60 fCurrVertex = 0;
61 fEffectTextureUniqueID = SK_InvalidUniqueID;
62
63 fVertices = NULL;
64 fMaxVertices = 0;
65
commit-bot@chromium.org3ae0e6c2014-02-11 18:24:25 +000066 fVertexBounds.setLargestInverted();
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000067}
68
jvanverth8c27a182014-10-14 08:45:50 -070069GrBitmapTextContext* GrBitmapTextContext::Create(GrContext* context,
70 const SkDeviceProperties& props) {
71 return SkNEW_ARGS(GrBitmapTextContext, (context, props));
72}
73
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000074GrBitmapTextContext::~GrBitmapTextContext() {
jvanverth0fedb192014-10-08 09:07:27 -070075 this->flush();
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000076}
77
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000078bool GrBitmapTextContext::canDraw(const SkPaint& paint) {
79 return !SkDraw::ShouldDrawTextAsPaths(paint, fContext->getMatrix());
80}
81
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000082inline void GrBitmapTextContext::init(const GrPaint& paint, const SkPaint& skPaint) {
83 GrTextContext::init(paint, skPaint);
84
85 fStrike = NULL;
86
Mike Klein6a25bd02014-08-29 10:03:59 -040087 fCurrTexture = NULL;
jvanverth63b9dc82014-08-28 10:39:40 -070088 fCurrVertex = 0;
Mike Klein6a25bd02014-08-29 10:03:59 -040089
90 fVertices = NULL;
91 fMaxVertices = 0;
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000092}
93
jvanverth8c27a182014-10-14 08:45:50 -070094void GrBitmapTextContext::onDrawText(const GrPaint& paint, const SkPaint& skPaint,
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000095 const char text[], size_t byteLength,
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +000096 SkScalar x, SkScalar y) {
97 SkASSERT(byteLength == 0 || text != NULL);
98
99 // nothing to draw
100 if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) {
101 return;
102 }
103
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000104 this->init(paint, skPaint);
105
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000106 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
107
108 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, &fContext->getMatrix());
109 SkGlyphCache* cache = autoCache.getCache();
110 GrFontScaler* fontScaler = GetGrFontScaler(cache);
Mike Klein6a25bd02014-08-29 10:03:59 -0400111
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000112 // transform our starting point
113 {
114 SkPoint loc;
115 fContext->getMatrix().mapXY(x, y, &loc);
116 x = loc.fX;
117 y = loc.fY;
118 }
119
120 // need to measure first
121 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
122 SkVector stop;
123
124 MeasureText(cache, glyphCacheProc, text, byteLength, &stop);
125
126 SkScalar stopX = stop.fX;
127 SkScalar stopY = stop.fY;
128
129 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
130 stopX = SkScalarHalf(stopX);
131 stopY = SkScalarHalf(stopY);
132 }
133 x -= stopX;
134 y -= stopY;
135 }
136
137 const char* stop = text + byteLength;
138
Mike Klein6a25bd02014-08-29 10:03:59 -0400139 SkAutoKern autokern;
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000140
141 SkFixed fxMask = ~0;
142 SkFixed fyMask = ~0;
143 SkFixed halfSampleX, halfSampleY;
144 if (cache->isSubpixel()) {
145 halfSampleX = halfSampleY = (SK_FixedHalf >> SkGlyph::kSubBits);
146 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(fContext->getMatrix());
147 if (kX_SkAxisAlignment == baseline) {
148 fyMask = 0;
149 halfSampleY = SK_FixedHalf;
150 } else if (kY_SkAxisAlignment == baseline) {
151 fxMask = 0;
152 halfSampleX = SK_FixedHalf;
153 }
154 } else {
155 halfSampleX = halfSampleY = SK_FixedHalf;
156 }
157
158 SkFixed fx = SkScalarToFixed(x) + halfSampleX;
159 SkFixed fy = SkScalarToFixed(y) + halfSampleY;
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000160
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000161 GrContext::AutoMatrix autoMatrix;
162 autoMatrix.setIdentity(fContext, &fPaint);
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000163
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000164 while (text < stop) {
165 const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
166
167 fx += autokern.adjust(glyph);
168
169 if (glyph.fWidth) {
jvanverth0fedb192014-10-08 09:07:27 -0700170 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
171 glyph.getSubXFixed(),
172 glyph.getSubYFixed()),
173 SkFixedFloorToFixed(fx),
174 SkFixedFloorToFixed(fy),
175 fontScaler);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000176 }
177
178 fx += glyph.fAdvanceX;
179 fy += glyph.fAdvanceY;
180 }
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000181
182 this->finish();
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000183}
184
jvanverth8c27a182014-10-14 08:45:50 -0700185void GrBitmapTextContext::onDrawPosText(const GrPaint& paint, const SkPaint& skPaint,
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000186 const char text[], size_t byteLength,
fmalita05c4a432014-09-29 06:29:53 -0700187 const SkScalar pos[], int scalarsPerPosition,
188 const SkPoint& offset) {
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000189 SkASSERT(byteLength == 0 || text != NULL);
190 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
191
192 // nothing to draw
193 if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) {
194 return;
195 }
196
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000197 this->init(paint, skPaint);
198
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000199 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
200
201 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, &fContext->getMatrix());
202 SkGlyphCache* cache = autoCache.getCache();
203 GrFontScaler* fontScaler = GetGrFontScaler(cache);
204
205 // store original matrix before we reset, so we can use it to transform positions
206 SkMatrix ctm = fContext->getMatrix();
207 GrContext::AutoMatrix autoMatrix;
208 autoMatrix.setIdentity(fContext, &fPaint);
209
210 const char* stop = text + byteLength;
kkinnunencb9a2c82014-06-12 23:06:28 -0700211 SkTextAlignProc alignProc(fSkPaint.getTextAlign());
fmalita05c4a432014-09-29 06:29:53 -0700212 SkTextMapStateProc tmsProc(ctm, offset, scalarsPerPosition);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000213 SkFixed halfSampleX = 0, halfSampleY = 0;
214
215 if (cache->isSubpixel()) {
216 // maybe we should skip the rounding if linearText is set
217 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(ctm);
218
219 SkFixed fxMask = ~0;
220 SkFixed fyMask = ~0;
221 if (kX_SkAxisAlignment == baseline) {
222 fyMask = 0;
223#ifndef SK_IGNORE_SUBPIXEL_AXIS_ALIGN_FIX
224 halfSampleY = SK_FixedHalf;
225#endif
226 } else if (kY_SkAxisAlignment == baseline) {
227 fxMask = 0;
228#ifndef SK_IGNORE_SUBPIXEL_AXIS_ALIGN_FIX
229 halfSampleX = SK_FixedHalf;
230#endif
231 }
232
233 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
234 while (text < stop) {
kkinnunencb9a2c82014-06-12 23:06:28 -0700235 SkPoint tmsLoc;
236 tmsProc(pos, &tmsLoc);
237 SkFixed fx = SkScalarToFixed(tmsLoc.fX) + halfSampleX;
238 SkFixed fy = SkScalarToFixed(tmsLoc.fY) + halfSampleY;
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000239
240 const SkGlyph& glyph = glyphCacheProc(cache, &text,
241 fx & fxMask, fy & fyMask);
242
243 if (glyph.fWidth) {
jvanverth0fedb192014-10-08 09:07:27 -0700244 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
245 glyph.getSubXFixed(),
246 glyph.getSubYFixed()),
247 SkFixedFloorToFixed(fx),
248 SkFixedFloorToFixed(fy),
249 fontScaler);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000250 }
251 pos += scalarsPerPosition;
252 }
253 } else {
254 while (text < stop) {
255 const char* currentText = text;
256 const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
257
258 if (metricGlyph.fWidth) {
259 SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
260 SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
kkinnunencb9a2c82014-06-12 23:06:28 -0700261 SkPoint tmsLoc;
262 tmsProc(pos, &tmsLoc);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000263 SkIPoint fixedLoc;
kkinnunencb9a2c82014-06-12 23:06:28 -0700264 alignProc(tmsLoc, metricGlyph, &fixedLoc);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000265
266 SkFixed fx = fixedLoc.fX + halfSampleX;
267 SkFixed fy = fixedLoc.fY + halfSampleY;
268
269 // have to call again, now that we've been "aligned"
270 const SkGlyph& glyph = glyphCacheProc(cache, &currentText,
271 fx & fxMask, fy & fyMask);
272 // the assumption is that the metrics haven't changed
273 SkASSERT(prevAdvX == glyph.fAdvanceX);
274 SkASSERT(prevAdvY == glyph.fAdvanceY);
275 SkASSERT(glyph.fWidth);
276
jvanverth0fedb192014-10-08 09:07:27 -0700277 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
278 glyph.getSubXFixed(),
279 glyph.getSubYFixed()),
280 SkFixedFloorToFixed(fx),
281 SkFixedFloorToFixed(fy),
282 fontScaler);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000283 }
284 pos += scalarsPerPosition;
285 }
286 }
287 } else { // not subpixel
288
289 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
290 while (text < stop) {
291 // the last 2 parameters are ignored
292 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
293
294 if (glyph.fWidth) {
kkinnunencb9a2c82014-06-12 23:06:28 -0700295 SkPoint tmsLoc;
296 tmsProc(pos, &tmsLoc);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000297
kkinnunencb9a2c82014-06-12 23:06:28 -0700298 SkFixed fx = SkScalarToFixed(tmsLoc.fX) + SK_FixedHalf; //halfSampleX;
299 SkFixed fy = SkScalarToFixed(tmsLoc.fY) + SK_FixedHalf; //halfSampleY;
jvanverth0fedb192014-10-08 09:07:27 -0700300 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
301 glyph.getSubXFixed(),
302 glyph.getSubYFixed()),
303 SkFixedFloorToFixed(fx),
304 SkFixedFloorToFixed(fy),
305 fontScaler);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000306 }
307 pos += scalarsPerPosition;
308 }
309 } else {
310 while (text < stop) {
311 // the last 2 parameters are ignored
312 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
313
314 if (glyph.fWidth) {
kkinnunencb9a2c82014-06-12 23:06:28 -0700315 SkPoint tmsLoc;
316 tmsProc(pos, &tmsLoc);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000317
318 SkIPoint fixedLoc;
kkinnunencb9a2c82014-06-12 23:06:28 -0700319 alignProc(tmsLoc, glyph, &fixedLoc);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000320
321 SkFixed fx = fixedLoc.fX + SK_FixedHalf; //halfSampleX;
322 SkFixed fy = fixedLoc.fY + SK_FixedHalf; //halfSampleY;
jvanverth0fedb192014-10-08 09:07:27 -0700323 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
324 glyph.getSubXFixed(),
325 glyph.getSubYFixed()),
326 SkFixedFloorToFixed(fx),
327 SkFixedFloorToFixed(fy),
328 fontScaler);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000329 }
330 pos += scalarsPerPosition;
331 }
332 }
333 }
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000334
335 this->finish();
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000336}
337
jvanverth0fedb192014-10-08 09:07:27 -0700338void GrBitmapTextContext::appendGlyph(GrGlyph::PackedID packed,
339 SkFixed vx, SkFixed vy,
340 GrFontScaler* scaler) {
Mike Klein6a25bd02014-08-29 10:03:59 -0400341 if (NULL == fDrawTarget) {
342 return;
343 }
344
345 if (NULL == fStrike) {
346 fStrike = fContext->getFontCache()->getStrike(scaler, false);
347 }
348
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000349 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
350 if (NULL == glyph || glyph->fBounds.isEmpty()) {
351 return;
352 }
353
354 vx += SkIntToFixed(glyph->fBounds.fLeft);
355 vy += SkIntToFixed(glyph->fBounds.fTop);
356
357 // keep them as ints until we've done the clip-test
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000358 SkFixed width = glyph->fBounds.width();
359 SkFixed height = glyph->fBounds.height();
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000360
361 // check if we clipped out
362 if (true || NULL == glyph->fPlot) {
363 int x = vx >> 16;
364 int y = vy >> 16;
365 if (fClipRect.quickReject(x, y, x + width, y + height)) {
366// SkCLZ(3); // so we can set a break-point in the debugger
367 return;
368 }
369 }
370
371 if (NULL == glyph->fPlot) {
jvanverth681e65b2014-09-19 13:07:38 -0700372 if (!fStrike->glyphTooLargeForAtlas(glyph)) {
373 if (fStrike->addGlyphToAtlas(glyph, scaler)) {
374 goto HAS_ATLAS;
375 }
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000376
jvanverth681e65b2014-09-19 13:07:38 -0700377 // try to clear out an unused plot before we flush
jvanverth294c3262014-10-10 11:36:12 -0700378 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
jvanverth681e65b2014-09-19 13:07:38 -0700379 fStrike->addGlyphToAtlas(glyph, scaler)) {
380 goto HAS_ATLAS;
381 }
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000382
jvanverth681e65b2014-09-19 13:07:38 -0700383 if (c_DumpFontCache) {
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000384#ifdef SK_DEVELOPER
jvanverth681e65b2014-09-19 13:07:38 -0700385 fContext->getFontCache()->dump();
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000386#endif
jvanverth681e65b2014-09-19 13:07:38 -0700387 }
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000388
jvanverth681e65b2014-09-19 13:07:38 -0700389 // flush any accumulated draws to allow us to free up a plot
jvanverth0fedb192014-10-08 09:07:27 -0700390 this->flush();
jvanverth681e65b2014-09-19 13:07:38 -0700391 fContext->flush();
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000392
jvanverth681e65b2014-09-19 13:07:38 -0700393 // we should have an unused plot now
jvanverth294c3262014-10-10 11:36:12 -0700394 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
jvanverth681e65b2014-09-19 13:07:38 -0700395 fStrike->addGlyphToAtlas(glyph, scaler)) {
396 goto HAS_ATLAS;
397 }
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000398 }
399
400 if (NULL == glyph->fPath) {
401 SkPath* path = SkNEW(SkPath);
402 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
403 // flag the glyph as being dead?
404 delete path;
405 return;
406 }
407 glyph->fPath = path;
408 }
409
410 GrContext::AutoMatrix am;
411 SkMatrix translate;
412 translate.setTranslate(SkFixedToScalar(vx - SkIntToFixed(glyph->fBounds.fLeft)),
413 SkFixedToScalar(vy - SkIntToFixed(glyph->fBounds.fTop)));
414 GrPaint tmpPaint(fPaint);
415 am.setPreConcat(fContext, translate, &tmpPaint);
egdanield58a0ba2014-06-11 10:30:05 -0700416 GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle);
417 fContext->drawPath(tmpPaint, *glyph->fPath, strokeInfo);
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000418 return;
419 }
420
421HAS_ATLAS:
422 SkASSERT(glyph->fPlot);
423 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
424 glyph->fPlot->setDrawToken(drawToken);
425
426 // now promote them to fixed (TODO: Rethink using fixed pt).
427 width = SkIntToFixed(width);
428 height = SkIntToFixed(height);
429
jvanverth294c3262014-10-10 11:36:12 -0700430 // the current texture/maskformat must match what the glyph needs
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000431 GrTexture* texture = glyph->fPlot->texture();
432 SkASSERT(texture);
433
Mike Klein6a25bd02014-08-29 10:03:59 -0400434 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
jvanverth0fedb192014-10-08 09:07:27 -0700435 this->flush();
Mike Klein6a25bd02014-08-29 10:03:59 -0400436 fCurrTexture = texture;
437 fCurrTexture->ref();
jvanverth294c3262014-10-10 11:36:12 -0700438 fCurrMaskFormat = glyph->fMaskFormat;
Mike Klein6a25bd02014-08-29 10:03:59 -0400439 }
440
jvanverth294c3262014-10-10 11:36:12 -0700441 bool useColorVerts = kA8_GrMaskFormat == fCurrMaskFormat;
Mike Klein6a25bd02014-08-29 10:03:59 -0400442
443 if (NULL == fVertices) {
444 // If we need to reserve vertices allow the draw target to suggest
445 // a number of verts to reserve and whether to perform a flush.
446 fMaxVertices = kMinRequestedVerts;
447 if (useColorVerts) {
448 fDrawTarget->drawState()->setVertexAttribs<gTextVertexWithColorAttribs>(
449 SK_ARRAY_COUNT(gTextVertexWithColorAttribs), kTextVAColorSize);
450 } else {
451 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
452 SK_ARRAY_COUNT(gTextVertexAttribs), kTextVASize);
453 }
454 bool flush = fDrawTarget->geometryHints(&fMaxVertices, NULL);
455 if (flush) {
jvanverth0fedb192014-10-08 09:07:27 -0700456 this->flush();
Mike Klein6a25bd02014-08-29 10:03:59 -0400457 fContext->flush();
458 if (useColorVerts) {
459 fDrawTarget->drawState()->setVertexAttribs<gTextVertexWithColorAttribs>(
460 SK_ARRAY_COUNT(gTextVertexWithColorAttribs), kTextVAColorSize);
461 } else {
462 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
463 SK_ARRAY_COUNT(gTextVertexAttribs), kTextVASize);
464 }
465 }
466 fMaxVertices = kDefaultRequestedVerts;
467 // ignore return, no point in flushing again.
468 fDrawTarget->geometryHints(&fMaxVertices, NULL);
469
470 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
471 if (fMaxVertices < kMinRequestedVerts) {
472 fMaxVertices = kDefaultRequestedVerts;
473 } else if (fMaxVertices > maxQuadVertices) {
474 // don't exceed the limit of the index buffer
475 fMaxVertices = maxQuadVertices;
476 }
477 bool success = fDrawTarget->reserveVertexAndIndexSpace(fMaxVertices,
478 0,
479 &fVertices,
480 NULL);
481 GrAlwaysAssert(success);
482 }
483
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000484 SkFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX);
485 SkFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY);
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000486
commit-bot@chromium.org3ae0e6c2014-02-11 18:24:25 +0000487 SkRect r;
488 r.fLeft = SkFixedToFloat(vx);
489 r.fTop = SkFixedToFloat(vy);
490 r.fRight = SkFixedToFloat(vx + width);
491 r.fBottom = SkFixedToFloat(vy + height);
492
reed10d03272014-10-01 09:24:12 -0700493 fVertexBounds.joinNonEmptyArg(r);
commit-bot@chromium.org3ae0e6c2014-02-11 18:24:25 +0000494
bsalomon594069f2014-06-06 06:16:34 -0700495 size_t vertSize = useColorVerts ? (2 * sizeof(SkPoint) + sizeof(GrColor)) :
496 (2 * sizeof(SkPoint));
497
egdaniel7b3d5ee2014-08-28 05:41:14 -0700498 SkASSERT(vertSize == fDrawTarget->getDrawState().getVertexStride());
bsalomon594069f2014-06-06 06:16:34 -0700499
500 SkPoint* positions = reinterpret_cast<SkPoint*>(
501 reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVertex);
502 positions->setRectFan(r.fLeft, r.fTop, r.fRight, r.fBottom, vertSize);
503
504 // The texture coords are last in both the with and without color vertex layouts.
505 SkPoint* textureCoords = reinterpret_cast<SkPoint*>(
506 reinterpret_cast<intptr_t>(positions) + vertSize - sizeof(SkPoint));
bsalomonafbf2d62014-09-30 12:18:44 -0700507 textureCoords->setRectFan(SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx)),
508 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty)),
509 SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx + width)),
510 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty + height)),
bsalomon594069f2014-06-06 06:16:34 -0700511 vertSize);
512 if (useColorVerts) {
bsalomon62c447d2014-08-08 08:08:50 -0700513 if (0xFF == GrColorUnpackA(fPaint.getColor())) {
514 fDrawTarget->drawState()->setHint(GrDrawState::kVertexColorsAreOpaque_Hint, true);
515 }
bsalomon594069f2014-06-06 06:16:34 -0700516 // color comes after position.
517 GrColor* colors = reinterpret_cast<GrColor*>(positions + 1);
518 for (int i = 0; i < 4; ++i) {
519 *colors = fPaint.getColor();
520 colors = reinterpret_cast<GrColor*>(reinterpret_cast<intptr_t>(colors) + vertSize);
521 }
522 }
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000523 fCurrVertex += 4;
524}
jvanverth0fedb192014-10-08 09:07:27 -0700525
526static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
527 unsigned r = SkColorGetR(c);
528 unsigned g = SkColorGetG(c);
529 unsigned b = SkColorGetB(c);
530 return GrColorPackRGBA(r, g, b, 0xff);
531}
532
533void GrBitmapTextContext::flush() {
534 if (NULL == fDrawTarget) {
535 return;
536 }
537
538 GrDrawState* drawState = fDrawTarget->drawState();
539 GrDrawState::AutoRestoreEffects are(drawState);
540 drawState->setFromPaint(fPaint, SkMatrix::I(), fContext->getRenderTarget());
541
542 if (fCurrVertex > 0) {
543 // setup our sampler state for our text texture/atlas
544 SkASSERT(SkIsAlign4(fCurrVertex));
545 SkASSERT(fCurrTexture);
546 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kNone_FilterMode);
547
548 uint32_t textureUniqueID = fCurrTexture->getUniqueID();
549
550 if (textureUniqueID != fEffectTextureUniqueID) {
551 fCachedGeometryProcessor.reset(GrCustomCoordsTextureEffect::Create(fCurrTexture,
552 params));
553 fEffectTextureUniqueID = textureUniqueID;
554 }
555
556 // This effect could be stored with one of the cache objects (atlas?)
557 drawState->setGeometryProcessor(fCachedGeometryProcessor.get());
558 SkASSERT(fStrike);
jvanverth294c3262014-10-10 11:36:12 -0700559 switch (fCurrMaskFormat) {
jvanverth0fedb192014-10-08 09:07:27 -0700560 // Color bitmap text
561 case kARGB_GrMaskFormat:
562 SkASSERT(!drawState->hasColorVertexAttribute());
563 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
jvanverth294c3262014-10-10 11:36:12 -0700564 drawState->setAlpha(fSkPaint.getAlpha());
jvanverth0fedb192014-10-08 09:07:27 -0700565 break;
566 // LCD text
567 case kA888_GrMaskFormat:
568 case kA565_GrMaskFormat: {
569 if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
570 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
571 fPaint.numColorStages()) {
572 GrPrintf("LCD Text will not draw correctly.\n");
573 }
574 SkASSERT(!drawState->hasColorVertexAttribute());
575 // We don't use the GrPaint's color in this case because it's been premultiplied by
576 // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by
577 // the mask texture color. The end result is that we get
578 // mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor
579 int a = SkColorGetA(fSkPaint.getColor());
580 // paintAlpha
581 drawState->setColor(SkColorSetARGB(a, a, a, a));
582 // paintColor
583 drawState->setBlendConstant(skcolor_to_grcolor_nopremultiply(fSkPaint.getColor()));
584 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
585 break;
586 }
587 // Grayscale/BW text
588 case kA8_GrMaskFormat:
589 // set back to normal in case we took LCD path previously.
590 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
591 // We're using per-vertex color.
592 SkASSERT(drawState->hasColorVertexAttribute());
593 break;
594 default:
jvanverth294c3262014-10-10 11:36:12 -0700595 SkFAIL("Unexpected mask format.");
jvanverth0fedb192014-10-08 09:07:27 -0700596 }
597 int nGlyphs = fCurrVertex / 4;
598 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
599 fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
600 nGlyphs,
601 4, 6, &fVertexBounds);
602
603 fDrawTarget->resetVertexSource();
604 fVertices = NULL;
605 fMaxVertices = 0;
606 fCurrVertex = 0;
607 fVertexBounds.setLargestInverted();
608 SkSafeSetNull(fCurrTexture);
609 }
610}
611
612inline void GrBitmapTextContext::finish() {
613 this->flush();
614
615 GrTextContext::finish();
616}
617