blob: 50fe9ca13301beb7af7d6a5609160d5b2613249a [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 {
38// position + texture coord
egdanieled3af662014-10-31 06:55:45 -070039extern const GrVertexAttrib gLCDVertexAttribs[] = {
bsalomon594069f2014-06-06 06:16:34 -070040 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
joshualittb0a8a372014-09-23 09:50:21 -070041 {kVec2f_GrVertexAttribType, sizeof(SkPoint), kGeometryProcessor_GrVertexAttribBinding}
bsalomon594069f2014-06-06 06:16:34 -070042};
43
egdanieled3af662014-10-31 06:55:45 -070044static const size_t kLCDTextVASize = 2 * sizeof(SkPoint);
45
46// position + local coord
egdanieled3af662014-10-31 06:55:45 -070047static const size_t kColorTextVASize = 2 * sizeof(SkPoint);
egdaniel7b3d5ee2014-08-28 05:41:14 -070048
bsalomon594069f2014-06-06 06:16:34 -070049// position + color + texture coord
egdanieled3af662014-10-31 06:55:45 -070050extern const GrVertexAttrib gGrayVertexAttribs[] = {
bsalomon594069f2014-06-06 06:16:34 -070051 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
52 {kVec4ub_GrVertexAttribType, sizeof(SkPoint), kColor_GrVertexAttribBinding},
joshualittb0a8a372014-09-23 09:50:21 -070053 {kVec2f_GrVertexAttribType, sizeof(SkPoint) + sizeof(GrColor), kGeometryProcessor_GrVertexAttribBinding}
bsalomon594069f2014-06-06 06:16:34 -070054};
55
egdanieled3af662014-10-31 06:55:45 -070056static const size_t kGrayTextVASize = 2 * sizeof(SkPoint) + sizeof(GrColor);
egdaniel7b3d5ee2014-08-28 05:41:14 -070057
jvanverth73f10532014-10-23 11:57:12 -070058static const int kVerticesPerGlyph = 4;
59static const int kIndicesPerGlyph = 6;
bsalomon594069f2014-06-06 06:16:34 -070060};
61
skia.committer@gmail.come5d70152014-01-29 07:01:48 +000062GrBitmapTextContext::GrBitmapTextContext(GrContext* context,
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +000063 const SkDeviceProperties& properties)
Mike Klein6a25bd02014-08-29 10:03:59 -040064 : GrTextContext(context, properties) {
65 fStrike = NULL;
66
67 fCurrTexture = NULL;
Mike Klein6a25bd02014-08-29 10:03:59 -040068 fEffectTextureUniqueID = SK_InvalidUniqueID;
69
70 fVertices = NULL;
jvanverth73f10532014-10-23 11:57:12 -070071 fCurrVertex = 0;
72 fAllocVertexCount = 0;
73 fTotalVertexCount = 0;
Mike Klein6a25bd02014-08-29 10:03:59 -040074
commit-bot@chromium.org3ae0e6c2014-02-11 18:24:25 +000075 fVertexBounds.setLargestInverted();
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000076}
77
jvanverth8c27a182014-10-14 08:45:50 -070078GrBitmapTextContext* GrBitmapTextContext::Create(GrContext* context,
79 const SkDeviceProperties& props) {
80 return SkNEW_ARGS(GrBitmapTextContext, (context, props));
81}
82
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000083GrBitmapTextContext::~GrBitmapTextContext() {
jvanverth73f10532014-10-23 11:57:12 -070084 this->finish();
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000085}
86
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000087bool GrBitmapTextContext::canDraw(const SkPaint& paint) {
88 return !SkDraw::ShouldDrawTextAsPaths(paint, fContext->getMatrix());
89}
90
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000091inline void GrBitmapTextContext::init(const GrPaint& paint, const SkPaint& skPaint) {
92 GrTextContext::init(paint, skPaint);
93
94 fStrike = NULL;
95
Mike Klein6a25bd02014-08-29 10:03:59 -040096 fCurrTexture = NULL;
jvanverth63b9dc82014-08-28 10:39:40 -070097 fCurrVertex = 0;
Mike Klein6a25bd02014-08-29 10:03:59 -040098
99 fVertices = NULL;
jvanverth73f10532014-10-23 11:57:12 -0700100 fAllocVertexCount = 0;
101 fTotalVertexCount = 0;
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000102}
103
jvanverthaab626c2014-10-16 08:04:39 -0700104void GrBitmapTextContext::onDrawText(const GrPaint& paint, const SkPaint& skPaint,
105 const char text[], size_t byteLength,
106 SkScalar x, SkScalar y) {
107 SkASSERT(byteLength == 0 || text != NULL);
108
109 // nothing to draw
110 if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) {
111 return;
112 }
113
114 this->init(paint, skPaint);
115
116 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
117
118 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, &fContext->getMatrix());
119 SkGlyphCache* cache = autoCache.getCache();
120 GrFontScaler* fontScaler = GetGrFontScaler(cache);
121
122 // transform our starting point
123 {
124 SkPoint loc;
125 fContext->getMatrix().mapXY(x, y, &loc);
126 x = loc.fX;
127 y = loc.fY;
128 }
129
130 // need to measure first
jvanverth73f10532014-10-23 11:57:12 -0700131 int numGlyphs;
jvanverthaab626c2014-10-16 08:04:39 -0700132 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
jvanverth73f10532014-10-23 11:57:12 -0700133 SkVector stopVector;
134 numGlyphs = MeasureText(cache, glyphCacheProc, text, byteLength, &stopVector);
jvanverthaab626c2014-10-16 08:04:39 -0700135
jvanverth73f10532014-10-23 11:57:12 -0700136 SkScalar stopX = stopVector.fX;
137 SkScalar stopY = stopVector.fY;
jvanverthaab626c2014-10-16 08:04:39 -0700138
139 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
140 stopX = SkScalarHalf(stopX);
141 stopY = SkScalarHalf(stopY);
142 }
143 x -= stopX;
144 y -= stopY;
jvanverth73f10532014-10-23 11:57:12 -0700145 } else {
146 numGlyphs = fSkPaint.textToGlyphs(text, byteLength, NULL);
jvanverthaab626c2014-10-16 08:04:39 -0700147 }
jvanverth73f10532014-10-23 11:57:12 -0700148 fTotalVertexCount = kVerticesPerGlyph*numGlyphs;
jvanverthaab626c2014-10-16 08:04:39 -0700149
150 const char* stop = text + byteLength;
151
152 SkAutoKern autokern;
153
154 SkFixed fxMask = ~0;
155 SkFixed fyMask = ~0;
156 SkFixed halfSampleX, halfSampleY;
157 if (cache->isSubpixel()) {
158 halfSampleX = halfSampleY = (SK_FixedHalf >> SkGlyph::kSubBits);
159 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(fContext->getMatrix());
160 if (kX_SkAxisAlignment == baseline) {
161 fyMask = 0;
162 halfSampleY = SK_FixedHalf;
163 } else if (kY_SkAxisAlignment == baseline) {
164 fxMask = 0;
165 halfSampleX = SK_FixedHalf;
166 }
167 } else {
168 halfSampleX = halfSampleY = SK_FixedHalf;
169 }
170
171 SkFixed fx = SkScalarToFixed(x) + halfSampleX;
172 SkFixed fy = SkScalarToFixed(y) + halfSampleY;
173
174 GrContext::AutoMatrix autoMatrix;
175 autoMatrix.setIdentity(fContext, &fPaint);
176
177 while (text < stop) {
178 const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
179
180 fx += autokern.adjust(glyph);
181
182 if (glyph.fWidth) {
183 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
184 glyph.getSubXFixed(),
185 glyph.getSubYFixed()),
186 SkFixedFloorToFixed(fx),
187 SkFixedFloorToFixed(fy),
188 fontScaler);
189 }
190
191 fx += glyph.fAdvanceX;
192 fy += glyph.fAdvanceY;
193 }
194
195 this->finish();
196}
197
jvanverth8c27a182014-10-14 08:45:50 -0700198void GrBitmapTextContext::onDrawPosText(const GrPaint& paint, const SkPaint& skPaint,
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000199 const char text[], size_t byteLength,
fmalita05c4a432014-09-29 06:29:53 -0700200 const SkScalar pos[], int scalarsPerPosition,
201 const SkPoint& offset) {
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000202 SkASSERT(byteLength == 0 || text != NULL);
203 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
204
205 // nothing to draw
206 if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) {
207 return;
208 }
209
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000210 this->init(paint, skPaint);
211
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000212 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
213
214 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, &fContext->getMatrix());
215 SkGlyphCache* cache = autoCache.getCache();
216 GrFontScaler* fontScaler = GetGrFontScaler(cache);
217
218 // store original matrix before we reset, so we can use it to transform positions
219 SkMatrix ctm = fContext->getMatrix();
220 GrContext::AutoMatrix autoMatrix;
221 autoMatrix.setIdentity(fContext, &fPaint);
222
jvanverth73f10532014-10-23 11:57:12 -0700223 int numGlyphs = fSkPaint.textToGlyphs(text, byteLength, NULL);
224 fTotalVertexCount = kVerticesPerGlyph*numGlyphs;
225
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000226 const char* stop = text + byteLength;
kkinnunencb9a2c82014-06-12 23:06:28 -0700227 SkTextAlignProc alignProc(fSkPaint.getTextAlign());
fmalita05c4a432014-09-29 06:29:53 -0700228 SkTextMapStateProc tmsProc(ctm, offset, scalarsPerPosition);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000229 SkFixed halfSampleX = 0, halfSampleY = 0;
230
231 if (cache->isSubpixel()) {
232 // maybe we should skip the rounding if linearText is set
233 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(ctm);
234
235 SkFixed fxMask = ~0;
236 SkFixed fyMask = ~0;
237 if (kX_SkAxisAlignment == baseline) {
238 fyMask = 0;
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000239 halfSampleY = SK_FixedHalf;
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000240 } else if (kY_SkAxisAlignment == baseline) {
241 fxMask = 0;
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000242 halfSampleX = SK_FixedHalf;
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000243 }
244
245 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
246 while (text < stop) {
kkinnunencb9a2c82014-06-12 23:06:28 -0700247 SkPoint tmsLoc;
248 tmsProc(pos, &tmsLoc);
249 SkFixed fx = SkScalarToFixed(tmsLoc.fX) + halfSampleX;
250 SkFixed fy = SkScalarToFixed(tmsLoc.fY) + halfSampleY;
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000251
252 const SkGlyph& glyph = glyphCacheProc(cache, &text,
253 fx & fxMask, fy & fyMask);
254
255 if (glyph.fWidth) {
jvanverth0fedb192014-10-08 09:07:27 -0700256 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
257 glyph.getSubXFixed(),
258 glyph.getSubYFixed()),
259 SkFixedFloorToFixed(fx),
260 SkFixedFloorToFixed(fy),
261 fontScaler);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000262 }
263 pos += scalarsPerPosition;
264 }
265 } else {
266 while (text < stop) {
267 const char* currentText = text;
268 const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
269
270 if (metricGlyph.fWidth) {
271 SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
272 SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
kkinnunencb9a2c82014-06-12 23:06:28 -0700273 SkPoint tmsLoc;
274 tmsProc(pos, &tmsLoc);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000275 SkIPoint fixedLoc;
kkinnunencb9a2c82014-06-12 23:06:28 -0700276 alignProc(tmsLoc, metricGlyph, &fixedLoc);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000277
278 SkFixed fx = fixedLoc.fX + halfSampleX;
279 SkFixed fy = fixedLoc.fY + halfSampleY;
280
281 // have to call again, now that we've been "aligned"
282 const SkGlyph& glyph = glyphCacheProc(cache, &currentText,
283 fx & fxMask, fy & fyMask);
284 // the assumption is that the metrics haven't changed
285 SkASSERT(prevAdvX == glyph.fAdvanceX);
286 SkASSERT(prevAdvY == glyph.fAdvanceY);
287 SkASSERT(glyph.fWidth);
288
jvanverth0fedb192014-10-08 09:07:27 -0700289 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
290 glyph.getSubXFixed(),
291 glyph.getSubYFixed()),
292 SkFixedFloorToFixed(fx),
293 SkFixedFloorToFixed(fy),
294 fontScaler);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000295 }
296 pos += scalarsPerPosition;
297 }
298 }
299 } else { // not subpixel
300
301 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
302 while (text < stop) {
303 // the last 2 parameters are ignored
304 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
305
306 if (glyph.fWidth) {
kkinnunencb9a2c82014-06-12 23:06:28 -0700307 SkPoint tmsLoc;
308 tmsProc(pos, &tmsLoc);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000309
kkinnunencb9a2c82014-06-12 23:06:28 -0700310 SkFixed fx = SkScalarToFixed(tmsLoc.fX) + SK_FixedHalf; //halfSampleX;
311 SkFixed fy = SkScalarToFixed(tmsLoc.fY) + SK_FixedHalf; //halfSampleY;
jvanverth0fedb192014-10-08 09:07:27 -0700312 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
313 glyph.getSubXFixed(),
314 glyph.getSubYFixed()),
315 SkFixedFloorToFixed(fx),
316 SkFixedFloorToFixed(fy),
317 fontScaler);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000318 }
319 pos += scalarsPerPosition;
320 }
321 } else {
322 while (text < stop) {
323 // the last 2 parameters are ignored
324 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
325
326 if (glyph.fWidth) {
kkinnunencb9a2c82014-06-12 23:06:28 -0700327 SkPoint tmsLoc;
328 tmsProc(pos, &tmsLoc);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000329
330 SkIPoint fixedLoc;
kkinnunencb9a2c82014-06-12 23:06:28 -0700331 alignProc(tmsLoc, glyph, &fixedLoc);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000332
333 SkFixed fx = fixedLoc.fX + SK_FixedHalf; //halfSampleX;
334 SkFixed fy = fixedLoc.fY + SK_FixedHalf; //halfSampleY;
jvanverth0fedb192014-10-08 09:07:27 -0700335 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
336 glyph.getSubXFixed(),
337 glyph.getSubYFixed()),
338 SkFixedFloorToFixed(fx),
339 SkFixedFloorToFixed(fy),
340 fontScaler);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000341 }
342 pos += scalarsPerPosition;
343 }
344 }
345 }
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000346
347 this->finish();
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000348}
349
egdanieled3af662014-10-31 06:55:45 -0700350static void* alloc_vertices(GrDrawTarget* drawTarget, int numVertices, GrMaskFormat maskFormat) {
jvanverth73f10532014-10-23 11:57:12 -0700351 if (numVertices <= 0) {
352 return NULL;
353 }
354
355 // set up attributes
egdanieled3af662014-10-31 06:55:45 -0700356 if (kA8_GrMaskFormat == maskFormat) {
357 drawTarget->drawState()->setVertexAttribs<gGrayVertexAttribs>(
358 SK_ARRAY_COUNT(gGrayVertexAttribs), kGrayTextVASize);
359 } else if (kARGB_GrMaskFormat == maskFormat) {
joshualitt5478d422014-11-14 16:00:38 -0800360 GrDefaultGeoProcFactory::SetAttribs(drawTarget->drawState(),
361 GrDefaultGeoProcFactory::kLocalCoord_GPType);
jvanverth73f10532014-10-23 11:57:12 -0700362 } else {
egdanieled3af662014-10-31 06:55:45 -0700363 drawTarget->drawState()->setVertexAttribs<gLCDVertexAttribs>(
364 SK_ARRAY_COUNT(gLCDVertexAttribs), kLCDTextVASize);
jvanverth73f10532014-10-23 11:57:12 -0700365 }
366 void* vertices = NULL;
367 bool success = drawTarget->reserveVertexAndIndexSpace(numVertices,
368 0,
369 &vertices,
370 NULL);
371 GrAlwaysAssert(success);
372 return vertices;
373}
374
jvanverth0fedb192014-10-08 09:07:27 -0700375void GrBitmapTextContext::appendGlyph(GrGlyph::PackedID packed,
376 SkFixed vx, SkFixed vy,
377 GrFontScaler* scaler) {
Mike Klein6a25bd02014-08-29 10:03:59 -0400378 if (NULL == fDrawTarget) {
379 return;
380 }
381
382 if (NULL == fStrike) {
383 fStrike = fContext->getFontCache()->getStrike(scaler, false);
384 }
385
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000386 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
387 if (NULL == glyph || glyph->fBounds.isEmpty()) {
388 return;
389 }
390
391 vx += SkIntToFixed(glyph->fBounds.fLeft);
392 vy += SkIntToFixed(glyph->fBounds.fTop);
393
394 // keep them as ints until we've done the clip-test
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000395 SkFixed width = glyph->fBounds.width();
396 SkFixed height = glyph->fBounds.height();
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000397
398 // check if we clipped out
399 if (true || NULL == glyph->fPlot) {
400 int x = vx >> 16;
401 int y = vy >> 16;
402 if (fClipRect.quickReject(x, y, x + width, y + height)) {
403// SkCLZ(3); // so we can set a break-point in the debugger
404 return;
405 }
406 }
407
408 if (NULL == glyph->fPlot) {
jvanverth681e65b2014-09-19 13:07:38 -0700409 if (!fStrike->glyphTooLargeForAtlas(glyph)) {
410 if (fStrike->addGlyphToAtlas(glyph, scaler)) {
411 goto HAS_ATLAS;
412 }
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000413
jvanverth681e65b2014-09-19 13:07:38 -0700414 // try to clear out an unused plot before we flush
jvanverth294c3262014-10-10 11:36:12 -0700415 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
jvanverth681e65b2014-09-19 13:07:38 -0700416 fStrike->addGlyphToAtlas(glyph, scaler)) {
417 goto HAS_ATLAS;
418 }
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000419
jvanverth681e65b2014-09-19 13:07:38 -0700420 if (c_DumpFontCache) {
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000421#ifdef SK_DEVELOPER
jvanverth681e65b2014-09-19 13:07:38 -0700422 fContext->getFontCache()->dump();
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000423#endif
jvanverth681e65b2014-09-19 13:07:38 -0700424 }
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000425
jvanverth681e65b2014-09-19 13:07:38 -0700426 // flush any accumulated draws to allow us to free up a plot
jvanverth0fedb192014-10-08 09:07:27 -0700427 this->flush();
jvanverth681e65b2014-09-19 13:07:38 -0700428 fContext->flush();
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000429
jvanverth681e65b2014-09-19 13:07:38 -0700430 // we should have an unused plot now
jvanverth294c3262014-10-10 11:36:12 -0700431 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
jvanverth681e65b2014-09-19 13:07:38 -0700432 fStrike->addGlyphToAtlas(glyph, scaler)) {
433 goto HAS_ATLAS;
434 }
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000435 }
436
437 if (NULL == glyph->fPath) {
438 SkPath* path = SkNEW(SkPath);
439 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
440 // flag the glyph as being dead?
441 delete path;
442 return;
443 }
444 glyph->fPath = path;
445 }
446
bsalomonec87dc62014-10-14 10:52:00 -0700447 // flush any accumulated draws before drawing this glyph as a path.
448 this->flush();
449
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000450 GrContext::AutoMatrix am;
451 SkMatrix translate;
452 translate.setTranslate(SkFixedToScalar(vx - SkIntToFixed(glyph->fBounds.fLeft)),
453 SkFixedToScalar(vy - SkIntToFixed(glyph->fBounds.fTop)));
454 GrPaint tmpPaint(fPaint);
455 am.setPreConcat(fContext, translate, &tmpPaint);
egdanield58a0ba2014-06-11 10:30:05 -0700456 GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle);
457 fContext->drawPath(tmpPaint, *glyph->fPath, strokeInfo);
jvanverth73f10532014-10-23 11:57:12 -0700458
459 // remove this glyph from the vertices we need to allocate
460 fTotalVertexCount -= kVerticesPerGlyph;
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000461 return;
462 }
463
464HAS_ATLAS:
465 SkASSERT(glyph->fPlot);
466 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
467 glyph->fPlot->setDrawToken(drawToken);
468
469 // now promote them to fixed (TODO: Rethink using fixed pt).
470 width = SkIntToFixed(width);
471 height = SkIntToFixed(height);
472
jvanverth294c3262014-10-10 11:36:12 -0700473 // the current texture/maskformat must match what the glyph needs
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000474 GrTexture* texture = glyph->fPlot->texture();
475 SkASSERT(texture);
476
jvanverth73f10532014-10-23 11:57:12 -0700477 if (fCurrTexture != texture || fCurrVertex + kVerticesPerGlyph > fAllocVertexCount) {
jvanverth0fedb192014-10-08 09:07:27 -0700478 this->flush();
Mike Klein6a25bd02014-08-29 10:03:59 -0400479 fCurrTexture = texture;
480 fCurrTexture->ref();
jvanverth294c3262014-10-10 11:36:12 -0700481 fCurrMaskFormat = glyph->fMaskFormat;
Mike Klein6a25bd02014-08-29 10:03:59 -0400482 }
483
Mike Klein6a25bd02014-08-29 10:03:59 -0400484 if (NULL == fVertices) {
jvanverth73f10532014-10-23 11:57:12 -0700485 int maxQuadVertices = kVerticesPerGlyph * fContext->getQuadIndexBuffer()->maxQuads();
486 fAllocVertexCount = SkMin32(fTotalVertexCount, maxQuadVertices);
egdanieled3af662014-10-31 06:55:45 -0700487 fVertices = alloc_vertices(fDrawTarget, fAllocVertexCount, fCurrMaskFormat);
Mike Klein6a25bd02014-08-29 10:03:59 -0400488 }
489
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000490 SkFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX);
491 SkFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY);
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000492
commit-bot@chromium.org3ae0e6c2014-02-11 18:24:25 +0000493 SkRect r;
494 r.fLeft = SkFixedToFloat(vx);
495 r.fTop = SkFixedToFloat(vy);
496 r.fRight = SkFixedToFloat(vx + width);
497 r.fBottom = SkFixedToFloat(vy + height);
498
reed10d03272014-10-01 09:24:12 -0700499 fVertexBounds.joinNonEmptyArg(r);
commit-bot@chromium.org3ae0e6c2014-02-11 18:24:25 +0000500
egdanieled3af662014-10-31 06:55:45 -0700501 size_t vertSize;
502 switch (fCurrMaskFormat) {
503 case kA8_GrMaskFormat:
504 vertSize = kGrayTextVASize;
505 break;
506 case kARGB_GrMaskFormat:
507 vertSize = kColorTextVASize;
508 default:
509 vertSize = kLCDTextVASize;
510 }
bsalomon594069f2014-06-06 06:16:34 -0700511
egdaniel7b3d5ee2014-08-28 05:41:14 -0700512 SkASSERT(vertSize == fDrawTarget->getDrawState().getVertexStride());
bsalomon594069f2014-06-06 06:16:34 -0700513
514 SkPoint* positions = reinterpret_cast<SkPoint*>(
515 reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVertex);
516 positions->setRectFan(r.fLeft, r.fTop, r.fRight, r.fBottom, vertSize);
517
518 // The texture coords are last in both the with and without color vertex layouts.
519 SkPoint* textureCoords = reinterpret_cast<SkPoint*>(
520 reinterpret_cast<intptr_t>(positions) + vertSize - sizeof(SkPoint));
bsalomonafbf2d62014-09-30 12:18:44 -0700521 textureCoords->setRectFan(SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx)),
522 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty)),
523 SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx + width)),
524 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty + height)),
bsalomon594069f2014-06-06 06:16:34 -0700525 vertSize);
egdanieled3af662014-10-31 06:55:45 -0700526 if (kA8_GrMaskFormat == fCurrMaskFormat) {
bsalomon62c447d2014-08-08 08:08:50 -0700527 if (0xFF == GrColorUnpackA(fPaint.getColor())) {
528 fDrawTarget->drawState()->setHint(GrDrawState::kVertexColorsAreOpaque_Hint, true);
529 }
bsalomon594069f2014-06-06 06:16:34 -0700530 // color comes after position.
531 GrColor* colors = reinterpret_cast<GrColor*>(positions + 1);
532 for (int i = 0; i < 4; ++i) {
533 *colors = fPaint.getColor();
534 colors = reinterpret_cast<GrColor*>(reinterpret_cast<intptr_t>(colors) + vertSize);
535 }
536 }
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000537 fCurrVertex += 4;
538}
jvanverth0fedb192014-10-08 09:07:27 -0700539
540static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
541 unsigned r = SkColorGetR(c);
542 unsigned g = SkColorGetG(c);
543 unsigned b = SkColorGetB(c);
544 return GrColorPackRGBA(r, g, b, 0xff);
545}
546
547void GrBitmapTextContext::flush() {
548 if (NULL == fDrawTarget) {
549 return;
550 }
551
552 GrDrawState* drawState = fDrawTarget->drawState();
553 GrDrawState::AutoRestoreEffects are(drawState);
554 drawState->setFromPaint(fPaint, SkMatrix::I(), fContext->getRenderTarget());
555
556 if (fCurrVertex > 0) {
557 // setup our sampler state for our text texture/atlas
558 SkASSERT(SkIsAlign4(fCurrVertex));
559 SkASSERT(fCurrTexture);
560 GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kNone_FilterMode);
561
egdanieled3af662014-10-31 06:55:45 -0700562 // This effect could be stored with one of the cache objects (atlas?)
563 if (kARGB_GrMaskFormat == fCurrMaskFormat) {
joshualitt5478d422014-11-14 16:00:38 -0800564 drawState->setGeometryProcessor(GrDefaultGeoProcFactory::Create(true))->unref();
egdanieled3af662014-10-31 06:55:45 -0700565 GrFragmentProcessor* fragProcessor = GrSimpleTextureEffect::Create(fCurrTexture,
566 SkMatrix::I(),
567 params);
568 drawState->addColorProcessor(fragProcessor)->unref();
569 } else {
570 uint32_t textureUniqueID = fCurrTexture->getUniqueID();
571 if (textureUniqueID != fEffectTextureUniqueID) {
572 fCachedGeometryProcessor.reset(GrCustomCoordsTextureEffect::Create(fCurrTexture,
573 params));
574 fEffectTextureUniqueID = textureUniqueID;
575 }
jvanverth0fedb192014-10-08 09:07:27 -0700576
egdanieled3af662014-10-31 06:55:45 -0700577 drawState->setGeometryProcessor(fCachedGeometryProcessor.get());
jvanverth0fedb192014-10-08 09:07:27 -0700578 }
579
jvanverth0fedb192014-10-08 09:07:27 -0700580 SkASSERT(fStrike);
jvanverth294c3262014-10-10 11:36:12 -0700581 switch (fCurrMaskFormat) {
jvanverth0fedb192014-10-08 09:07:27 -0700582 // Color bitmap text
583 case kARGB_GrMaskFormat:
584 SkASSERT(!drawState->hasColorVertexAttribute());
585 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
jvanverth294c3262014-10-10 11:36:12 -0700586 drawState->setAlpha(fSkPaint.getAlpha());
jvanverth0fedb192014-10-08 09:07:27 -0700587 break;
588 // LCD text
jvanverth0fedb192014-10-08 09:07:27 -0700589 case kA565_GrMaskFormat: {
590 if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
591 kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
592 fPaint.numColorStages()) {
tfarina38406c82014-10-31 07:11:12 -0700593 SkDebugf("LCD Text will not draw correctly.\n");
jvanverth0fedb192014-10-08 09:07:27 -0700594 }
595 SkASSERT(!drawState->hasColorVertexAttribute());
596 // We don't use the GrPaint's color in this case because it's been premultiplied by
597 // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by
598 // the mask texture color. The end result is that we get
599 // mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor
600 int a = SkColorGetA(fSkPaint.getColor());
601 // paintAlpha
602 drawState->setColor(SkColorSetARGB(a, a, a, a));
603 // paintColor
604 drawState->setBlendConstant(skcolor_to_grcolor_nopremultiply(fSkPaint.getColor()));
605 drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
606 break;
607 }
608 // Grayscale/BW text
609 case kA8_GrMaskFormat:
610 // set back to normal in case we took LCD path previously.
611 drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
612 // We're using per-vertex color.
613 SkASSERT(drawState->hasColorVertexAttribute());
614 break;
615 default:
jvanverth294c3262014-10-10 11:36:12 -0700616 SkFAIL("Unexpected mask format.");
jvanverth0fedb192014-10-08 09:07:27 -0700617 }
jvanverth73f10532014-10-23 11:57:12 -0700618 int nGlyphs = fCurrVertex / kVerticesPerGlyph;
jvanverth0fedb192014-10-08 09:07:27 -0700619 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
620 fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
621 nGlyphs,
jvanverth73f10532014-10-23 11:57:12 -0700622 kVerticesPerGlyph, kIndicesPerGlyph, &fVertexBounds);
jvanverth0fedb192014-10-08 09:07:27 -0700623
624 fDrawTarget->resetVertexSource();
625 fVertices = NULL;
jvanverth73f10532014-10-23 11:57:12 -0700626 fAllocVertexCount = 0;
627 // reset to be those that are left
628 fTotalVertexCount -= fCurrVertex;
jvanverth0fedb192014-10-08 09:07:27 -0700629 fCurrVertex = 0;
630 fVertexBounds.setLargestInverted();
631 SkSafeSetNull(fCurrTexture);
632 }
633}
634
635inline void GrBitmapTextContext::finish() {
636 this->flush();
jvanverth73f10532014-10-23 11:57:12 -0700637 fTotalVertexCount = 0;
jvanverth0fedb192014-10-08 09:07:27 -0700638
639 GrTextContext::finish();
640}
641