blob: 2ff410e5cf278ca6b89c58e382c44af8b22f1720 [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
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000083bool GrBitmapTextContext::canDraw(const SkPaint& paint) {
84 return !SkDraw::ShouldDrawTextAsPaths(paint, fContext->getMatrix());
85}
86
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000087inline void GrBitmapTextContext::init(const GrPaint& paint, const SkPaint& skPaint) {
88 GrTextContext::init(paint, skPaint);
89
90 fStrike = NULL;
91
Mike Klein6a25bd02014-08-29 10:03:59 -040092 fCurrTexture = NULL;
jvanverth63b9dc82014-08-28 10:39:40 -070093 fCurrVertex = 0;
Mike Klein6a25bd02014-08-29 10:03:59 -040094
95 fVertices = NULL;
jvanverth73f10532014-10-23 11:57:12 -070096 fAllocVertexCount = 0;
97 fTotalVertexCount = 0;
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000098}
99
jvanverthaab626c2014-10-16 08:04:39 -0700100void GrBitmapTextContext::onDrawText(const GrPaint& paint, const SkPaint& skPaint,
joshualitt9853cce2014-11-17 14:22:48 -0800101 const char text[], size_t byteLength,
102 SkScalar x, SkScalar y) {
jvanverthaab626c2014-10-16 08:04:39 -0700103 SkASSERT(byteLength == 0 || text != NULL);
104
105 // nothing to draw
106 if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) {
107 return;
108 }
109
110 this->init(paint, skPaint);
111
112 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
113
114 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, &fContext->getMatrix());
115 SkGlyphCache* cache = autoCache.getCache();
116 GrFontScaler* fontScaler = GetGrFontScaler(cache);
117
118 // transform our starting point
119 {
120 SkPoint loc;
121 fContext->getMatrix().mapXY(x, y, &loc);
122 x = loc.fX;
123 y = loc.fY;
124 }
125
126 // need to measure first
jvanverth73f10532014-10-23 11:57:12 -0700127 int numGlyphs;
jvanverthaab626c2014-10-16 08:04:39 -0700128 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
jvanverth73f10532014-10-23 11:57:12 -0700129 SkVector stopVector;
130 numGlyphs = MeasureText(cache, glyphCacheProc, text, byteLength, &stopVector);
jvanverthaab626c2014-10-16 08:04:39 -0700131
jvanverth73f10532014-10-23 11:57:12 -0700132 SkScalar stopX = stopVector.fX;
133 SkScalar stopY = stopVector.fY;
jvanverthaab626c2014-10-16 08:04:39 -0700134
135 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
136 stopX = SkScalarHalf(stopX);
137 stopY = SkScalarHalf(stopY);
138 }
139 x -= stopX;
140 y -= stopY;
jvanverth73f10532014-10-23 11:57:12 -0700141 } else {
142 numGlyphs = fSkPaint.textToGlyphs(text, byteLength, NULL);
jvanverthaab626c2014-10-16 08:04:39 -0700143 }
jvanverth73f10532014-10-23 11:57:12 -0700144 fTotalVertexCount = kVerticesPerGlyph*numGlyphs;
jvanverthaab626c2014-10-16 08:04:39 -0700145
146 const char* stop = text + byteLength;
147
148 SkAutoKern autokern;
149
150 SkFixed fxMask = ~0;
151 SkFixed fyMask = ~0;
152 SkFixed halfSampleX, halfSampleY;
153 if (cache->isSubpixel()) {
154 halfSampleX = halfSampleY = (SK_FixedHalf >> SkGlyph::kSubBits);
155 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(fContext->getMatrix());
156 if (kX_SkAxisAlignment == baseline) {
157 fyMask = 0;
158 halfSampleY = SK_FixedHalf;
159 } else if (kY_SkAxisAlignment == baseline) {
160 fxMask = 0;
161 halfSampleX = SK_FixedHalf;
162 }
163 } else {
164 halfSampleX = halfSampleY = SK_FixedHalf;
165 }
166
167 SkFixed fx = SkScalarToFixed(x) + halfSampleX;
168 SkFixed fy = SkScalarToFixed(y) + halfSampleY;
169
170 GrContext::AutoMatrix autoMatrix;
171 autoMatrix.setIdentity(fContext, &fPaint);
172
173 while (text < stop) {
174 const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
175
176 fx += autokern.adjust(glyph);
177
178 if (glyph.fWidth) {
179 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
180 glyph.getSubXFixed(),
181 glyph.getSubYFixed()),
182 SkFixedFloorToFixed(fx),
183 SkFixedFloorToFixed(fy),
184 fontScaler);
185 }
186
187 fx += glyph.fAdvanceX;
188 fy += glyph.fAdvanceY;
189 }
190
191 this->finish();
192}
193
jvanverth8c27a182014-10-14 08:45:50 -0700194void GrBitmapTextContext::onDrawPosText(const GrPaint& paint, const SkPaint& skPaint,
joshualitt9853cce2014-11-17 14:22:48 -0800195 const char text[], size_t byteLength,
196 const SkScalar pos[], int scalarsPerPosition,
197 const SkPoint& offset) {
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000198 SkASSERT(byteLength == 0 || text != NULL);
199 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
200
201 // nothing to draw
202 if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) {
203 return;
204 }
205
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000206 this->init(paint, skPaint);
207
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000208 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
209
210 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, &fContext->getMatrix());
211 SkGlyphCache* cache = autoCache.getCache();
212 GrFontScaler* fontScaler = GetGrFontScaler(cache);
213
214 // store original matrix before we reset, so we can use it to transform positions
215 SkMatrix ctm = fContext->getMatrix();
216 GrContext::AutoMatrix autoMatrix;
217 autoMatrix.setIdentity(fContext, &fPaint);
218
jvanverth73f10532014-10-23 11:57:12 -0700219 int numGlyphs = fSkPaint.textToGlyphs(text, byteLength, NULL);
220 fTotalVertexCount = kVerticesPerGlyph*numGlyphs;
221
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000222 const char* stop = text + byteLength;
kkinnunencb9a2c82014-06-12 23:06:28 -0700223 SkTextAlignProc alignProc(fSkPaint.getTextAlign());
fmalita05c4a432014-09-29 06:29:53 -0700224 SkTextMapStateProc tmsProc(ctm, offset, scalarsPerPosition);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000225 SkFixed halfSampleX = 0, halfSampleY = 0;
226
227 if (cache->isSubpixel()) {
228 // maybe we should skip the rounding if linearText is set
229 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(ctm);
230
231 SkFixed fxMask = ~0;
232 SkFixed fyMask = ~0;
233 if (kX_SkAxisAlignment == baseline) {
234 fyMask = 0;
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000235 halfSampleY = SK_FixedHalf;
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000236 } else if (kY_SkAxisAlignment == baseline) {
237 fxMask = 0;
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000238 halfSampleX = SK_FixedHalf;
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000239 }
240
241 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
242 while (text < stop) {
kkinnunencb9a2c82014-06-12 23:06:28 -0700243 SkPoint tmsLoc;
244 tmsProc(pos, &tmsLoc);
245 SkFixed fx = SkScalarToFixed(tmsLoc.fX) + halfSampleX;
246 SkFixed fy = SkScalarToFixed(tmsLoc.fY) + halfSampleY;
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000247
248 const SkGlyph& glyph = glyphCacheProc(cache, &text,
249 fx & fxMask, fy & fyMask);
250
251 if (glyph.fWidth) {
jvanverth0fedb192014-10-08 09:07:27 -0700252 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
253 glyph.getSubXFixed(),
254 glyph.getSubYFixed()),
255 SkFixedFloorToFixed(fx),
256 SkFixedFloorToFixed(fy),
257 fontScaler);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000258 }
259 pos += scalarsPerPosition;
260 }
261 } else {
262 while (text < stop) {
263 const char* currentText = text;
264 const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
265
266 if (metricGlyph.fWidth) {
267 SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
268 SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
kkinnunencb9a2c82014-06-12 23:06:28 -0700269 SkPoint tmsLoc;
270 tmsProc(pos, &tmsLoc);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000271 SkIPoint fixedLoc;
kkinnunencb9a2c82014-06-12 23:06:28 -0700272 alignProc(tmsLoc, metricGlyph, &fixedLoc);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000273
274 SkFixed fx = fixedLoc.fX + halfSampleX;
275 SkFixed fy = fixedLoc.fY + halfSampleY;
276
277 // have to call again, now that we've been "aligned"
278 const SkGlyph& glyph = glyphCacheProc(cache, &currentText,
279 fx & fxMask, fy & fyMask);
280 // the assumption is that the metrics haven't changed
281 SkASSERT(prevAdvX == glyph.fAdvanceX);
282 SkASSERT(prevAdvY == glyph.fAdvanceY);
283 SkASSERT(glyph.fWidth);
284
jvanverth0fedb192014-10-08 09:07:27 -0700285 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
286 glyph.getSubXFixed(),
287 glyph.getSubYFixed()),
288 SkFixedFloorToFixed(fx),
289 SkFixedFloorToFixed(fy),
290 fontScaler);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000291 }
292 pos += scalarsPerPosition;
293 }
294 }
295 } else { // not subpixel
296
297 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
298 while (text < stop) {
299 // the last 2 parameters are ignored
300 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
301
302 if (glyph.fWidth) {
kkinnunencb9a2c82014-06-12 23:06:28 -0700303 SkPoint tmsLoc;
304 tmsProc(pos, &tmsLoc);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000305
kkinnunencb9a2c82014-06-12 23:06:28 -0700306 SkFixed fx = SkScalarToFixed(tmsLoc.fX) + SK_FixedHalf; //halfSampleX;
307 SkFixed fy = SkScalarToFixed(tmsLoc.fY) + SK_FixedHalf; //halfSampleY;
jvanverth0fedb192014-10-08 09:07:27 -0700308 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
309 glyph.getSubXFixed(),
310 glyph.getSubYFixed()),
311 SkFixedFloorToFixed(fx),
312 SkFixedFloorToFixed(fy),
313 fontScaler);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000314 }
315 pos += scalarsPerPosition;
316 }
317 } else {
318 while (text < stop) {
319 // the last 2 parameters are ignored
320 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
321
322 if (glyph.fWidth) {
kkinnunencb9a2c82014-06-12 23:06:28 -0700323 SkPoint tmsLoc;
324 tmsProc(pos, &tmsLoc);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000325
326 SkIPoint fixedLoc;
kkinnunencb9a2c82014-06-12 23:06:28 -0700327 alignProc(tmsLoc, glyph, &fixedLoc);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000328
329 SkFixed fx = fixedLoc.fX + SK_FixedHalf; //halfSampleX;
330 SkFixed fy = fixedLoc.fY + SK_FixedHalf; //halfSampleY;
jvanverth0fedb192014-10-08 09:07:27 -0700331 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
332 glyph.getSubXFixed(),
333 glyph.getSubYFixed()),
334 SkFixedFloorToFixed(fx),
335 SkFixedFloorToFixed(fy),
336 fontScaler);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000337 }
338 pos += scalarsPerPosition;
339 }
340 }
341 }
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000342
343 this->finish();
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000344}
345
joshualitt9853cce2014-11-17 14:22:48 -0800346static size_t get_vertex_stride(GrMaskFormat maskFormat) {
347 switch (maskFormat) {
348 case kA8_GrMaskFormat:
349 return kGrayTextVASize;
350 case kARGB_GrMaskFormat:
351 return kColorTextVASize;
352 default:
353 return kLCDTextVASize;
354 }
355}
356
357static void set_vertex_attributes(GrDrawState* drawState, GrMaskFormat maskFormat) {
358 if (kA8_GrMaskFormat == maskFormat) {
359 drawState->setVertexAttribs<gGrayVertexAttribs>(
360 SK_ARRAY_COUNT(gGrayVertexAttribs), kGrayTextVASize);
361 } else if (kARGB_GrMaskFormat == maskFormat) {
362 GrDefaultGeoProcFactory::SetAttribs(drawState,
363 GrDefaultGeoProcFactory::kLocalCoord_GPType);
364 } else {
365 drawState->setVertexAttribs<gLCDVertexAttribs>(
366 SK_ARRAY_COUNT(gLCDVertexAttribs), kLCDTextVASize);
367 }
368}
369
370static void* alloc_vertices(GrDrawTarget* drawTarget,
371 int numVertices,
372 GrMaskFormat maskFormat) {
jvanverth73f10532014-10-23 11:57:12 -0700373 if (numVertices <= 0) {
374 return NULL;
375 }
376
377 // set up attributes
jvanverth73f10532014-10-23 11:57:12 -0700378 void* vertices = NULL;
379 bool success = drawTarget->reserveVertexAndIndexSpace(numVertices,
joshualitt9853cce2014-11-17 14:22:48 -0800380 get_vertex_stride(maskFormat),
jvanverth73f10532014-10-23 11:57:12 -0700381 0,
382 &vertices,
383 NULL);
384 GrAlwaysAssert(success);
385 return vertices;
386}
387
jvanverth0fedb192014-10-08 09:07:27 -0700388void GrBitmapTextContext::appendGlyph(GrGlyph::PackedID packed,
389 SkFixed vx, SkFixed vy,
390 GrFontScaler* scaler) {
Mike Klein6a25bd02014-08-29 10:03:59 -0400391 if (NULL == fDrawTarget) {
392 return;
393 }
394
395 if (NULL == fStrike) {
396 fStrike = fContext->getFontCache()->getStrike(scaler, false);
397 }
398
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000399 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
400 if (NULL == glyph || glyph->fBounds.isEmpty()) {
401 return;
402 }
403
404 vx += SkIntToFixed(glyph->fBounds.fLeft);
405 vy += SkIntToFixed(glyph->fBounds.fTop);
406
407 // keep them as ints until we've done the clip-test
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000408 SkFixed width = glyph->fBounds.width();
409 SkFixed height = glyph->fBounds.height();
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000410
411 // check if we clipped out
412 if (true || NULL == glyph->fPlot) {
413 int x = vx >> 16;
414 int y = vy >> 16;
415 if (fClipRect.quickReject(x, y, x + width, y + height)) {
416// SkCLZ(3); // so we can set a break-point in the debugger
417 return;
418 }
419 }
420
421 if (NULL == glyph->fPlot) {
jvanverth681e65b2014-09-19 13:07:38 -0700422 if (!fStrike->glyphTooLargeForAtlas(glyph)) {
423 if (fStrike->addGlyphToAtlas(glyph, scaler)) {
424 goto HAS_ATLAS;
425 }
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000426
jvanverth681e65b2014-09-19 13:07:38 -0700427 // try to clear out an unused plot before we flush
jvanverth294c3262014-10-10 11:36:12 -0700428 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
jvanverth681e65b2014-09-19 13:07:38 -0700429 fStrike->addGlyphToAtlas(glyph, scaler)) {
430 goto HAS_ATLAS;
431 }
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000432
jvanverth681e65b2014-09-19 13:07:38 -0700433 if (c_DumpFontCache) {
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000434#ifdef SK_DEVELOPER
jvanverth681e65b2014-09-19 13:07:38 -0700435 fContext->getFontCache()->dump();
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000436#endif
jvanverth681e65b2014-09-19 13:07:38 -0700437 }
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000438
jvanverth681e65b2014-09-19 13:07:38 -0700439 // flush any accumulated draws to allow us to free up a plot
jvanverth0fedb192014-10-08 09:07:27 -0700440 this->flush();
jvanverth681e65b2014-09-19 13:07:38 -0700441 fContext->flush();
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000442
jvanverth681e65b2014-09-19 13:07:38 -0700443 // we should have an unused plot now
jvanverth294c3262014-10-10 11:36:12 -0700444 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
jvanverth681e65b2014-09-19 13:07:38 -0700445 fStrike->addGlyphToAtlas(glyph, scaler)) {
446 goto HAS_ATLAS;
447 }
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000448 }
449
450 if (NULL == glyph->fPath) {
451 SkPath* path = SkNEW(SkPath);
452 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
453 // flag the glyph as being dead?
454 delete path;
455 return;
456 }
457 glyph->fPath = path;
458 }
459
bsalomonec87dc62014-10-14 10:52:00 -0700460 // flush any accumulated draws before drawing this glyph as a path.
461 this->flush();
462
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000463 GrContext::AutoMatrix am;
464 SkMatrix translate;
465 translate.setTranslate(SkFixedToScalar(vx - SkIntToFixed(glyph->fBounds.fLeft)),
466 SkFixedToScalar(vy - SkIntToFixed(glyph->fBounds.fTop)));
467 GrPaint tmpPaint(fPaint);
468 am.setPreConcat(fContext, translate, &tmpPaint);
egdanield58a0ba2014-06-11 10:30:05 -0700469 GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle);
470 fContext->drawPath(tmpPaint, *glyph->fPath, strokeInfo);
jvanverth73f10532014-10-23 11:57:12 -0700471
472 // remove this glyph from the vertices we need to allocate
473 fTotalVertexCount -= kVerticesPerGlyph;
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000474 return;
475 }
476
477HAS_ATLAS:
478 SkASSERT(glyph->fPlot);
479 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
480 glyph->fPlot->setDrawToken(drawToken);
481
482 // now promote them to fixed (TODO: Rethink using fixed pt).
483 width = SkIntToFixed(width);
484 height = SkIntToFixed(height);
485
jvanverth294c3262014-10-10 11:36:12 -0700486 // the current texture/maskformat must match what the glyph needs
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000487 GrTexture* texture = glyph->fPlot->texture();
488 SkASSERT(texture);
489
jvanverth73f10532014-10-23 11:57:12 -0700490 if (fCurrTexture != texture || fCurrVertex + kVerticesPerGlyph > fAllocVertexCount) {
jvanverth0fedb192014-10-08 09:07:27 -0700491 this->flush();
Mike Klein6a25bd02014-08-29 10:03:59 -0400492 fCurrTexture = texture;
493 fCurrTexture->ref();
jvanverth294c3262014-10-10 11:36:12 -0700494 fCurrMaskFormat = glyph->fMaskFormat;
Mike Klein6a25bd02014-08-29 10:03:59 -0400495 }
496
Mike Klein6a25bd02014-08-29 10:03:59 -0400497 if (NULL == fVertices) {
jvanverth73f10532014-10-23 11:57:12 -0700498 int maxQuadVertices = kVerticesPerGlyph * fContext->getQuadIndexBuffer()->maxQuads();
499 fAllocVertexCount = SkMin32(fTotalVertexCount, maxQuadVertices);
egdanieled3af662014-10-31 06:55:45 -0700500 fVertices = alloc_vertices(fDrawTarget, fAllocVertexCount, fCurrMaskFormat);
Mike Klein6a25bd02014-08-29 10:03:59 -0400501 }
502
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000503 SkFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX);
504 SkFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY);
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000505
commit-bot@chromium.org3ae0e6c2014-02-11 18:24:25 +0000506 SkRect r;
507 r.fLeft = SkFixedToFloat(vx);
508 r.fTop = SkFixedToFloat(vy);
509 r.fRight = SkFixedToFloat(vx + width);
510 r.fBottom = SkFixedToFloat(vy + height);
511
reed10d03272014-10-01 09:24:12 -0700512 fVertexBounds.joinNonEmptyArg(r);
commit-bot@chromium.org3ae0e6c2014-02-11 18:24:25 +0000513
joshualitt9853cce2014-11-17 14:22:48 -0800514 size_t vertSize = get_vertex_stride(fCurrMaskFormat);
bsalomon594069f2014-06-06 06:16:34 -0700515
516 SkPoint* positions = reinterpret_cast<SkPoint*>(
517 reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVertex);
518 positions->setRectFan(r.fLeft, r.fTop, r.fRight, r.fBottom, vertSize);
519
520 // The texture coords are last in both the with and without color vertex layouts.
521 SkPoint* textureCoords = reinterpret_cast<SkPoint*>(
522 reinterpret_cast<intptr_t>(positions) + vertSize - sizeof(SkPoint));
bsalomonafbf2d62014-09-30 12:18:44 -0700523 textureCoords->setRectFan(SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx)),
524 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty)),
525 SkFixedToFloat(texture->texturePriv().normalizeFixedX(tx + width)),
526 SkFixedToFloat(texture->texturePriv().normalizeFixedY(ty + height)),
bsalomon594069f2014-06-06 06:16:34 -0700527 vertSize);
egdanieled3af662014-10-31 06:55:45 -0700528 if (kA8_GrMaskFormat == fCurrMaskFormat) {
bsalomon594069f2014-06-06 06:16:34 -0700529 // color comes after position.
530 GrColor* colors = reinterpret_cast<GrColor*>(positions + 1);
531 for (int i = 0; i < 4; ++i) {
532 *colors = fPaint.getColor();
533 colors = reinterpret_cast<GrColor*>(reinterpret_cast<intptr_t>(colors) + vertSize);
534 }
535 }
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000536 fCurrVertex += 4;
537}
jvanverth0fedb192014-10-08 09:07:27 -0700538
539static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
540 unsigned r = SkColorGetR(c);
541 unsigned g = SkColorGetG(c);
542 unsigned b = SkColorGetB(c);
543 return GrColorPackRGBA(r, g, b, 0xff);
544}
545
546void GrBitmapTextContext::flush() {
547 if (NULL == fDrawTarget) {
548 return;
549 }
550
jvanverth0fedb192014-10-08 09:07:27 -0700551 if (fCurrVertex > 0) {
joshualitt780b11e2014-11-18 09:40:40 -0800552 GrDrawState drawState;
553 drawState.setFromPaint(fPaint, SkMatrix::I(), fContext->getRenderTarget());
554
555 set_vertex_attributes(&drawState, fCurrMaskFormat);
556
jvanverth0fedb192014-10-08 09:07:27 -0700557 // 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) {
joshualitt9853cce2014-11-17 14:22:48 -0800564 drawState.setGeometryProcessor(GrDefaultGeoProcFactory::Create(true))->unref();
egdanieled3af662014-10-31 06:55:45 -0700565 GrFragmentProcessor* fragProcessor = GrSimpleTextureEffect::Create(fCurrTexture,
566 SkMatrix::I(),
567 params);
joshualitt9853cce2014-11-17 14:22:48 -0800568 drawState.addColorProcessor(fragProcessor)->unref();
egdanieled3af662014-10-31 06:55:45 -0700569 } 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
joshualitt9853cce2014-11-17 14:22:48 -0800577 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:
joshualitt9853cce2014-11-17 14:22:48 -0800584 SkASSERT(!drawState.hasColorVertexAttribute());
585 drawState.setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
586 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 }
joshualitt9853cce2014-11-17 14:22:48 -0800595 SkASSERT(!drawState.hasColorVertexAttribute());
jvanverth0fedb192014-10-08 09:07:27 -0700596 // 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
joshualitt9853cce2014-11-17 14:22:48 -0800602 drawState.setColor(SkColorSetARGB(a, a, a, a));
jvanverth0fedb192014-10-08 09:07:27 -0700603 // paintColor
joshualitt9853cce2014-11-17 14:22:48 -0800604 drawState.setBlendConstant(skcolor_to_grcolor_nopremultiply(fSkPaint.getColor()));
605 drawState.setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
jvanverth0fedb192014-10-08 09:07:27 -0700606 break;
607 }
608 // Grayscale/BW text
609 case kA8_GrMaskFormat:
joshualitt9853cce2014-11-17 14:22:48 -0800610 drawState.setHint(GrDrawState::kVertexColorsAreOpaque_Hint,
611 0xFF == GrColorUnpackA(fPaint.getColor()));
jvanverth0fedb192014-10-08 09:07:27 -0700612 // set back to normal in case we took LCD path previously.
joshualitt9853cce2014-11-17 14:22:48 -0800613 drawState.setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
jvanverth0fedb192014-10-08 09:07:27 -0700614 // We're using per-vertex color.
joshualitt9853cce2014-11-17 14:22:48 -0800615 SkASSERT(drawState.hasColorVertexAttribute());
jvanverth0fedb192014-10-08 09:07:27 -0700616 break;
617 default:
jvanverth294c3262014-10-10 11:36:12 -0700618 SkFAIL("Unexpected mask format.");
jvanverth0fedb192014-10-08 09:07:27 -0700619 }
jvanverth73f10532014-10-23 11:57:12 -0700620 int nGlyphs = fCurrVertex / kVerticesPerGlyph;
jvanverth0fedb192014-10-08 09:07:27 -0700621 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
joshualitt9853cce2014-11-17 14:22:48 -0800622 fDrawTarget->drawIndexedInstances(&drawState,
623 kTriangles_GrPrimitiveType,
jvanverth0fedb192014-10-08 09:07:27 -0700624 nGlyphs,
joshualitt9853cce2014-11-17 14:22:48 -0800625 kVerticesPerGlyph,
626 kIndicesPerGlyph,
627 &fVertexBounds);
jvanverth0fedb192014-10-08 09:07:27 -0700628
629 fDrawTarget->resetVertexSource();
630 fVertices = NULL;
jvanverth73f10532014-10-23 11:57:12 -0700631 fAllocVertexCount = 0;
632 // reset to be those that are left
633 fTotalVertexCount -= fCurrVertex;
jvanverth0fedb192014-10-08 09:07:27 -0700634 fCurrVertex = 0;
635 fVertexBounds.setLargestInverted();
636 SkSafeSetNull(fCurrTexture);
637 }
638}
639
640inline void GrBitmapTextContext::finish() {
641 this->flush();
jvanverth73f10532014-10-23 11:57:12 -0700642 fTotalVertexCount = 0;
jvanverth0fedb192014-10-08 09:07:27 -0700643
644 GrTextContext::finish();
645}
646