blob: 7cfe91771f72e43ed97885ffc86ca45c0d7a6cc1 [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
69GrBitmapTextContext::~GrBitmapTextContext() {
jvanverth0fedb192014-10-08 09:07:27 -070070 this->flush();
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000071}
72
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000073bool GrBitmapTextContext::canDraw(const SkPaint& paint) {
74 return !SkDraw::ShouldDrawTextAsPaths(paint, fContext->getMatrix());
75}
76
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000077inline void GrBitmapTextContext::init(const GrPaint& paint, const SkPaint& skPaint) {
78 GrTextContext::init(paint, skPaint);
79
80 fStrike = NULL;
81
Mike Klein6a25bd02014-08-29 10:03:59 -040082 fCurrTexture = NULL;
jvanverth63b9dc82014-08-28 10:39:40 -070083 fCurrVertex = 0;
Mike Klein6a25bd02014-08-29 10:03:59 -040084
85 fVertices = NULL;
86 fMaxVertices = 0;
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000087}
88
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +000089void GrBitmapTextContext::drawText(const GrPaint& paint, const SkPaint& skPaint,
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000090 const char text[], size_t byteLength,
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +000091 SkScalar x, SkScalar y) {
92 SkASSERT(byteLength == 0 || text != NULL);
93
94 // nothing to draw
95 if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) {
96 return;
97 }
98
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000099 this->init(paint, skPaint);
100
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000101 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
102
103 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, &fContext->getMatrix());
104 SkGlyphCache* cache = autoCache.getCache();
105 GrFontScaler* fontScaler = GetGrFontScaler(cache);
Mike Klein6a25bd02014-08-29 10:03:59 -0400106
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000107 // transform our starting point
108 {
109 SkPoint loc;
110 fContext->getMatrix().mapXY(x, y, &loc);
111 x = loc.fX;
112 y = loc.fY;
113 }
114
115 // need to measure first
116 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
117 SkVector stop;
118
119 MeasureText(cache, glyphCacheProc, text, byteLength, &stop);
120
121 SkScalar stopX = stop.fX;
122 SkScalar stopY = stop.fY;
123
124 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
125 stopX = SkScalarHalf(stopX);
126 stopY = SkScalarHalf(stopY);
127 }
128 x -= stopX;
129 y -= stopY;
130 }
131
132 const char* stop = text + byteLength;
133
Mike Klein6a25bd02014-08-29 10:03:59 -0400134 SkAutoKern autokern;
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000135
136 SkFixed fxMask = ~0;
137 SkFixed fyMask = ~0;
138 SkFixed halfSampleX, halfSampleY;
139 if (cache->isSubpixel()) {
140 halfSampleX = halfSampleY = (SK_FixedHalf >> SkGlyph::kSubBits);
141 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(fContext->getMatrix());
142 if (kX_SkAxisAlignment == baseline) {
143 fyMask = 0;
144 halfSampleY = SK_FixedHalf;
145 } else if (kY_SkAxisAlignment == baseline) {
146 fxMask = 0;
147 halfSampleX = SK_FixedHalf;
148 }
149 } else {
150 halfSampleX = halfSampleY = SK_FixedHalf;
151 }
152
153 SkFixed fx = SkScalarToFixed(x) + halfSampleX;
154 SkFixed fy = SkScalarToFixed(y) + halfSampleY;
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000155
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000156 GrContext::AutoMatrix autoMatrix;
157 autoMatrix.setIdentity(fContext, &fPaint);
skia.committer@gmail.come5d70152014-01-29 07:01:48 +0000158
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000159 while (text < stop) {
160 const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
161
162 fx += autokern.adjust(glyph);
163
164 if (glyph.fWidth) {
jvanverth0fedb192014-10-08 09:07:27 -0700165 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
166 glyph.getSubXFixed(),
167 glyph.getSubYFixed()),
168 SkFixedFloorToFixed(fx),
169 SkFixedFloorToFixed(fy),
170 fontScaler);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000171 }
172
173 fx += glyph.fAdvanceX;
174 fy += glyph.fAdvanceY;
175 }
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000176
177 this->finish();
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000178}
179
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000180void GrBitmapTextContext::drawPosText(const GrPaint& paint, const SkPaint& skPaint,
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000181 const char text[], size_t byteLength,
fmalita05c4a432014-09-29 06:29:53 -0700182 const SkScalar pos[], int scalarsPerPosition,
183 const SkPoint& offset) {
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000184 SkASSERT(byteLength == 0 || text != NULL);
185 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
186
187 // nothing to draw
188 if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) {
189 return;
190 }
191
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000192 this->init(paint, skPaint);
193
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000194 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
195
196 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, &fContext->getMatrix());
197 SkGlyphCache* cache = autoCache.getCache();
198 GrFontScaler* fontScaler = GetGrFontScaler(cache);
199
200 // store original matrix before we reset, so we can use it to transform positions
201 SkMatrix ctm = fContext->getMatrix();
202 GrContext::AutoMatrix autoMatrix;
203 autoMatrix.setIdentity(fContext, &fPaint);
204
205 const char* stop = text + byteLength;
kkinnunencb9a2c82014-06-12 23:06:28 -0700206 SkTextAlignProc alignProc(fSkPaint.getTextAlign());
fmalita05c4a432014-09-29 06:29:53 -0700207 SkTextMapStateProc tmsProc(ctm, offset, scalarsPerPosition);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000208 SkFixed halfSampleX = 0, halfSampleY = 0;
209
210 if (cache->isSubpixel()) {
211 // maybe we should skip the rounding if linearText is set
212 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(ctm);
213
214 SkFixed fxMask = ~0;
215 SkFixed fyMask = ~0;
216 if (kX_SkAxisAlignment == baseline) {
217 fyMask = 0;
218#ifndef SK_IGNORE_SUBPIXEL_AXIS_ALIGN_FIX
219 halfSampleY = SK_FixedHalf;
220#endif
221 } else if (kY_SkAxisAlignment == baseline) {
222 fxMask = 0;
223#ifndef SK_IGNORE_SUBPIXEL_AXIS_ALIGN_FIX
224 halfSampleX = SK_FixedHalf;
225#endif
226 }
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
jvanverth0fedb192014-10-08 09:07:27 -0700333void GrBitmapTextContext::appendGlyph(GrGlyph::PackedID packed,
334 SkFixed vx, SkFixed vy,
335 GrFontScaler* scaler) {
Mike Klein6a25bd02014-08-29 10:03:59 -0400336 if (NULL == fDrawTarget) {
337 return;
338 }
339
340 if (NULL == fStrike) {
341 fStrike = fContext->getFontCache()->getStrike(scaler, false);
342 }
343
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000344 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
345 if (NULL == glyph || glyph->fBounds.isEmpty()) {
346 return;
347 }
348
349 vx += SkIntToFixed(glyph->fBounds.fLeft);
350 vy += SkIntToFixed(glyph->fBounds.fTop);
351
352 // keep them as ints until we've done the clip-test
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000353 SkFixed width = glyph->fBounds.width();
354 SkFixed height = glyph->fBounds.height();
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000355
356 // check if we clipped out
357 if (true || NULL == glyph->fPlot) {
358 int x = vx >> 16;
359 int y = vy >> 16;
360 if (fClipRect.quickReject(x, y, x + width, y + height)) {
361// SkCLZ(3); // so we can set a break-point in the debugger
362 return;
363 }
364 }
365
366 if (NULL == glyph->fPlot) {
jvanverth681e65b2014-09-19 13:07:38 -0700367 if (!fStrike->glyphTooLargeForAtlas(glyph)) {
368 if (fStrike->addGlyphToAtlas(glyph, scaler)) {
369 goto HAS_ATLAS;
370 }
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000371
jvanverth681e65b2014-09-19 13:07:38 -0700372 // try to clear out an unused plot before we flush
jvanverth294c3262014-10-10 11:36:12 -0700373 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
jvanverth681e65b2014-09-19 13:07:38 -0700374 fStrike->addGlyphToAtlas(glyph, scaler)) {
375 goto HAS_ATLAS;
376 }
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000377
jvanverth681e65b2014-09-19 13:07:38 -0700378 if (c_DumpFontCache) {
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000379#ifdef SK_DEVELOPER
jvanverth681e65b2014-09-19 13:07:38 -0700380 fContext->getFontCache()->dump();
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000381#endif
jvanverth681e65b2014-09-19 13:07:38 -0700382 }
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000383
jvanverth681e65b2014-09-19 13:07:38 -0700384 // flush any accumulated draws to allow us to free up a plot
jvanverth0fedb192014-10-08 09:07:27 -0700385 this->flush();
jvanverth681e65b2014-09-19 13:07:38 -0700386 fContext->flush();
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000387
jvanverth681e65b2014-09-19 13:07:38 -0700388 // we should have an unused plot now
jvanverth294c3262014-10-10 11:36:12 -0700389 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
jvanverth681e65b2014-09-19 13:07:38 -0700390 fStrike->addGlyphToAtlas(glyph, scaler)) {
391 goto HAS_ATLAS;
392 }
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000393 }
394
395 if (NULL == glyph->fPath) {
396 SkPath* path = SkNEW(SkPath);
397 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
398 // flag the glyph as being dead?
399 delete path;
400 return;
401 }
402 glyph->fPath = path;
403 }
404
405 GrContext::AutoMatrix am;
406 SkMatrix translate;
407 translate.setTranslate(SkFixedToScalar(vx - SkIntToFixed(glyph->fBounds.fLeft)),
408 SkFixedToScalar(vy - SkIntToFixed(glyph->fBounds.fTop)));
409 GrPaint tmpPaint(fPaint);
410 am.setPreConcat(fContext, translate, &tmpPaint);
egdanield58a0ba2014-06-11 10:30:05 -0700411 GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle);
412 fContext->drawPath(tmpPaint, *glyph->fPath, strokeInfo);
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000413 return;
414 }
415
416HAS_ATLAS:
417 SkASSERT(glyph->fPlot);
418 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
419 glyph->fPlot->setDrawToken(drawToken);
420
421 // now promote them to fixed (TODO: Rethink using fixed pt).
422 width = SkIntToFixed(width);
423 height = SkIntToFixed(height);
424
jvanverth294c3262014-10-10 11:36:12 -0700425 // the current texture/maskformat must match what the glyph needs
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000426 GrTexture* texture = glyph->fPlot->texture();
427 SkASSERT(texture);
428
Mike Klein6a25bd02014-08-29 10:03:59 -0400429 if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
jvanverth0fedb192014-10-08 09:07:27 -0700430 this->flush();
Mike Klein6a25bd02014-08-29 10:03:59 -0400431 fCurrTexture = texture;
432 fCurrTexture->ref();
jvanverth294c3262014-10-10 11:36:12 -0700433 fCurrMaskFormat = glyph->fMaskFormat;
Mike Klein6a25bd02014-08-29 10:03:59 -0400434 }
435
jvanverth294c3262014-10-10 11:36:12 -0700436 bool useColorVerts = kA8_GrMaskFormat == fCurrMaskFormat;
Mike Klein6a25bd02014-08-29 10:03:59 -0400437
438 if (NULL == fVertices) {
439 // If we need to reserve vertices allow the draw target to suggest
440 // a number of verts to reserve and whether to perform a flush.
441 fMaxVertices = kMinRequestedVerts;
442 if (useColorVerts) {
443 fDrawTarget->drawState()->setVertexAttribs<gTextVertexWithColorAttribs>(
444 SK_ARRAY_COUNT(gTextVertexWithColorAttribs), kTextVAColorSize);
445 } else {
446 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
447 SK_ARRAY_COUNT(gTextVertexAttribs), kTextVASize);
448 }
449 bool flush = fDrawTarget->geometryHints(&fMaxVertices, NULL);
450 if (flush) {
jvanverth0fedb192014-10-08 09:07:27 -0700451 this->flush();
Mike Klein6a25bd02014-08-29 10:03:59 -0400452 fContext->flush();
453 if (useColorVerts) {
454 fDrawTarget->drawState()->setVertexAttribs<gTextVertexWithColorAttribs>(
455 SK_ARRAY_COUNT(gTextVertexWithColorAttribs), kTextVAColorSize);
456 } else {
457 fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>(
458 SK_ARRAY_COUNT(gTextVertexAttribs), kTextVASize);
459 }
460 }
461 fMaxVertices = kDefaultRequestedVerts;
462 // ignore return, no point in flushing again.
463 fDrawTarget->geometryHints(&fMaxVertices, NULL);
464
465 int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
466 if (fMaxVertices < kMinRequestedVerts) {
467 fMaxVertices = kDefaultRequestedVerts;
468 } else if (fMaxVertices > maxQuadVertices) {
469 // don't exceed the limit of the index buffer
470 fMaxVertices = maxQuadVertices;
471 }
472 bool success = fDrawTarget->reserveVertexAndIndexSpace(fMaxVertices,
473 0,
474 &fVertices,
475 NULL);
476 GrAlwaysAssert(success);
477 }
478
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000479 SkFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX);
480 SkFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY);
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000481
commit-bot@chromium.org3ae0e6c2014-02-11 18:24:25 +0000482 SkRect r;
483 r.fLeft = SkFixedToFloat(vx);
484 r.fTop = SkFixedToFloat(vy);
485 r.fRight = SkFixedToFloat(vx + width);
486 r.fBottom = SkFixedToFloat(vy + height);
487
reed10d03272014-10-01 09:24:12 -0700488 fVertexBounds.joinNonEmptyArg(r);
commit-bot@chromium.org3ae0e6c2014-02-11 18:24:25 +0000489
bsalomon594069f2014-06-06 06:16:34 -0700490 size_t vertSize = useColorVerts ? (2 * sizeof(SkPoint) + sizeof(GrColor)) :
491 (2 * sizeof(SkPoint));
492
egdaniel7b3d5ee2014-08-28 05:41:14 -0700493 SkASSERT(vertSize == fDrawTarget->getDrawState().getVertexStride());
bsalomon594069f2014-06-06 06:16:34 -0700494
495 SkPoint* positions = reinterpret_cast<SkPoint*>(
496 reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVertex);
497 positions->setRectFan(r.fLeft, r.fTop, r.fRight, r.fBottom, vertSize);
498
499 // The texture coords are last in both the with and without color vertex layouts.
500 SkPoint* textureCoords = reinterpret_cast<SkPoint*>(
501 reinterpret_cast<intptr_t>(positions) + vertSize - sizeof(SkPoint));
bsalomonafbf2d62014-09-30 12:18:44 -0700502 textureCoords->setRectFan(SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx)),
503 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty)),
504 SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx + width)),
505 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty + height)),
bsalomon594069f2014-06-06 06:16:34 -0700506 vertSize);
507 if (useColorVerts) {
bsalomon62c447d2014-08-08 08:08:50 -0700508 if (0xFF == GrColorUnpackA(fPaint.getColor())) {
509 fDrawTarget->drawState()->setHint(GrDrawState::kVertexColorsAreOpaque_Hint, true);
510 }
bsalomon594069f2014-06-06 06:16:34 -0700511 // color comes after position.
512 GrColor* colors = reinterpret_cast<GrColor*>(positions + 1);
513 for (int i = 0; i < 4; ++i) {
514 *colors = fPaint.getColor();
515 colors = reinterpret_cast<GrColor*>(reinterpret_cast<intptr_t>(colors) + vertSize);
516 }
517 }
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000518 fCurrVertex += 4;
519}
jvanverth0fedb192014-10-08 09:07:27 -0700520
521static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
522 unsigned r = SkColorGetR(c);
523 unsigned g = SkColorGetG(c);
524 unsigned b = SkColorGetB(c);
525 return GrColorPackRGBA(r, g, b, 0xff);
526}
527
528void GrBitmapTextContext::flush() {
529 if (NULL == fDrawTarget) {
530 return;
531 }
532
533 GrDrawState* drawState = fDrawTarget->drawState();
534 GrDrawState::AutoRestoreEffects are(drawState);
535 drawState->setFromPaint(fPaint, SkMatrix::I(), fContext->getRenderTarget());
536
537 if (fCurrVertex > 0) {
538 // setup our sampler state for our text texture/atlas
539 SkASSERT(SkIsAlign4(fCurrVertex));
540 SkASSERT(fCurrTexture);
541 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kNone_FilterMode);
542
543 uint32_t textureUniqueID = fCurrTexture->getUniqueID();
544
545 if (textureUniqueID != fEffectTextureUniqueID) {
546 fCachedGeometryProcessor.reset(GrCustomCoordsTextureEffect::Create(fCurrTexture,
547 params));
548 fEffectTextureUniqueID = textureUniqueID;
549 }
550
551 // This effect could be stored with one of the cache objects (atlas?)
552 drawState->setGeometryProcessor(fCachedGeometryProcessor.get());
553 SkASSERT(fStrike);
jvanverth294c3262014-10-10 11:36:12 -0700554 switch (fCurrMaskFormat) {
jvanverth0fedb192014-10-08 09:07:27 -0700555 // Color bitmap text
556 case kARGB_GrMaskFormat:
557 SkASSERT(!drawState->hasColorVertexAttribute());
558 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
jvanverth294c3262014-10-10 11:36:12 -0700559 drawState->setAlpha(fSkPaint.getAlpha());
jvanverth0fedb192014-10-08 09:07:27 -0700560 break;
561 // LCD text
562 case kA888_GrMaskFormat:
563 case kA565_GrMaskFormat: {
564 if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
565 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
566 fPaint.numColorStages()) {
567 GrPrintf("LCD Text will not draw correctly.\n");
568 }
569 SkASSERT(!drawState->hasColorVertexAttribute());
570 // We don't use the GrPaint's color in this case because it's been premultiplied by
571 // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by
572 // the mask texture color. The end result is that we get
573 // mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor
574 int a = SkColorGetA(fSkPaint.getColor());
575 // paintAlpha
576 drawState->setColor(SkColorSetARGB(a, a, a, a));
577 // paintColor
578 drawState->setBlendConstant(skcolor_to_grcolor_nopremultiply(fSkPaint.getColor()));
579 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
580 break;
581 }
582 // Grayscale/BW text
583 case kA8_GrMaskFormat:
584 // set back to normal in case we took LCD path previously.
585 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
586 // We're using per-vertex color.
587 SkASSERT(drawState->hasColorVertexAttribute());
588 break;
589 default:
jvanverth294c3262014-10-10 11:36:12 -0700590 SkFAIL("Unexpected mask format.");
jvanverth0fedb192014-10-08 09:07:27 -0700591 }
592 int nGlyphs = fCurrVertex / 4;
593 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
594 fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
595 nGlyphs,
596 4, 6, &fVertexBounds);
597
598 fDrawTarget->resetVertexSource();
599 fVertices = NULL;
600 fMaxVertices = 0;
601 fCurrVertex = 0;
602 fVertexBounds.setLargestInverted();
603 SkSafeSetNull(fCurrTexture);
604 }
605}
606
607inline void GrBitmapTextContext::finish() {
608 this->flush();
609
610 GrTextContext::finish();
611}
612