blob: 4061c4882137e9888ff697a20c171284512501bc [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"
jvanverth787cdf92014-12-04 10:46:50 -080012#include "GrFontCache.h"
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000013#include "GrFontScaler.h"
14#include "GrIndexBuffer.h"
egdanield58a0ba2014-06-11 10:30:05 -070015#include "GrStrokeInfo.h"
bsalomonafbf2d62014-09-30 12:18:44 -070016#include "GrTexturePriv.h"
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000017
jvanverthaab626c2014-10-16 08:04:39 -070018#include "SkAutoKern.h"
bsalomonafbf2d62014-09-30 12:18:44 -070019#include "SkColorPriv.h"
commit-bot@chromium.org9f94b912014-01-30 15:22:54 +000020#include "SkDraw.h"
kkinnunencb9a2c82014-06-12 23:06:28 -070021#include "SkDrawProcs.h"
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +000022#include "SkGlyphCache.h"
23#include "SkGpuDevice.h"
24#include "SkGr.h"
bsalomonafbf2d62014-09-30 12:18:44 -070025#include "SkPath.h"
26#include "SkRTConf.h"
27#include "SkStrokeRec.h"
kkinnunencb9a2c82014-06-12 23:06:28 -070028#include "SkTextMapStateProc.h"
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +000029
egdaniel309e3462014-12-09 10:35:58 -080030#include "effects/GrBitmapTextGeoProc.h"
joshualitt5478d422014-11-14 16:00:38 -080031#include "effects/GrSimpleTextureEffect.h"
32
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000033SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false,
34 "Dump the contents of the font cache before every purge.");
35
bsalomon594069f2014-06-06 06:16:34 -070036namespace {
jvanverth5a105ff2015-02-18 11:36:35 -080037static const size_t kLCDTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
egdanieled3af662014-10-31 06:55:45 -070038
39// position + local coord
jvanverth5a105ff2015-02-18 11:36:35 -080040static const size_t kColorTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
egdaniel7b3d5ee2014-08-28 05:41:14 -070041
jvanverth5a105ff2015-02-18 11:36:35 -080042static const size_t kGrayTextVASize = sizeof(SkPoint) + sizeof(GrColor) + sizeof(SkIPoint16);
egdaniel7b3d5ee2014-08-28 05:41:14 -070043
jvanverth73f10532014-10-23 11:57:12 -070044static const int kVerticesPerGlyph = 4;
45static const int kIndicesPerGlyph = 6;
bsalomon594069f2014-06-06 06:16:34 -070046};
47
skia.committer@gmail.come5d70152014-01-29 07:01:48 +000048GrBitmapTextContext::GrBitmapTextContext(GrContext* context,
joshualitt6e8cd962015-03-20 10:30:14 -070049 SkGpuDevice* gpuDevice,
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +000050 const SkDeviceProperties& properties)
joshualitt6e8cd962015-03-20 10:30:14 -070051 : GrTextContext(context, gpuDevice, properties) {
Mike Klein6a25bd02014-08-29 10:03:59 -040052 fStrike = NULL;
53
54 fCurrTexture = NULL;
Mike Klein6a25bd02014-08-29 10:03:59 -040055 fEffectTextureUniqueID = SK_InvalidUniqueID;
56
57 fVertices = NULL;
jvanverth73f10532014-10-23 11:57:12 -070058 fCurrVertex = 0;
59 fAllocVertexCount = 0;
60 fTotalVertexCount = 0;
Mike Klein6a25bd02014-08-29 10:03:59 -040061
commit-bot@chromium.org3ae0e6c2014-02-11 18:24:25 +000062 fVertexBounds.setLargestInverted();
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +000063}
64
jvanverth8c27a182014-10-14 08:45:50 -070065GrBitmapTextContext* GrBitmapTextContext::Create(GrContext* context,
joshualitt6e8cd962015-03-20 10:30:14 -070066 SkGpuDevice* gpuDevice,
jvanverth8c27a182014-10-14 08:45:50 -070067 const SkDeviceProperties& props) {
joshualitt6e8cd962015-03-20 10:30:14 -070068 return SkNEW_ARGS(GrBitmapTextContext, (context, gpuDevice, props));
jvanverth8c27a182014-10-14 08:45:50 -070069}
70
joshualitt5531d512014-12-17 15:50:11 -080071bool GrBitmapTextContext::canDraw(const SkPaint& paint, const SkMatrix& viewMatrix) {
72 return !SkDraw::ShouldDrawTextAsPaths(paint, viewMatrix);
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000073}
74
joshualitt570d2f82015-02-25 13:19:48 -080075inline void GrBitmapTextContext::init(GrRenderTarget* rt, const GrClip& clip,
joshualitt6e8cd962015-03-20 10:30:14 -070076 const GrPaint& paint, const SkPaint& skPaint,
77 const SkIRect& regionClipBounds) {
78 GrTextContext::init(rt, clip, paint, skPaint, regionClipBounds);
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000079
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;
jvanverth73f10532014-10-23 11:57:12 -070086 fAllocVertexCount = 0;
87 fTotalVertexCount = 0;
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +000088}
89
joshualitt570d2f82015-02-25 13:19:48 -080090void GrBitmapTextContext::onDrawText(GrRenderTarget* rt, const GrClip& clip,
91 const GrPaint& paint, const SkPaint& skPaint,
joshualitt5531d512014-12-17 15:50:11 -080092 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -080093 const char text[], size_t byteLength,
joshualitt6e8cd962015-03-20 10:30:14 -070094 SkScalar x, SkScalar y, const SkIRect& regionClipBounds) {
jvanverthaab626c2014-10-16 08:04:39 -070095 SkASSERT(byteLength == 0 || text != NULL);
96
97 // nothing to draw
98 if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) {
99 return;
100 }
101
joshualitt6e8cd962015-03-20 10:30:14 -0700102 this->init(rt, clip, paint, skPaint, regionClipBounds);
jvanverthaab626c2014-10-16 08:04:39 -0700103
104 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
105
joshualitt5531d512014-12-17 15:50:11 -0800106 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, &viewMatrix);
jvanverthaab626c2014-10-16 08:04:39 -0700107 SkGlyphCache* cache = autoCache.getCache();
108 GrFontScaler* fontScaler = GetGrFontScaler(cache);
109
110 // transform our starting point
111 {
112 SkPoint loc;
joshualitt5531d512014-12-17 15:50:11 -0800113 viewMatrix.mapXY(x, y, &loc);
jvanverthaab626c2014-10-16 08:04:39 -0700114 x = loc.fX;
115 y = loc.fY;
116 }
117
118 // need to measure first
jvanverth73f10532014-10-23 11:57:12 -0700119 int numGlyphs;
jvanverthaab626c2014-10-16 08:04:39 -0700120 if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
jvanverth73f10532014-10-23 11:57:12 -0700121 SkVector stopVector;
122 numGlyphs = MeasureText(cache, glyphCacheProc, text, byteLength, &stopVector);
jvanverthaab626c2014-10-16 08:04:39 -0700123
jvanverth73f10532014-10-23 11:57:12 -0700124 SkScalar stopX = stopVector.fX;
125 SkScalar stopY = stopVector.fY;
jvanverthaab626c2014-10-16 08:04:39 -0700126
127 if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
128 stopX = SkScalarHalf(stopX);
129 stopY = SkScalarHalf(stopY);
130 }
131 x -= stopX;
132 y -= stopY;
jvanverth73f10532014-10-23 11:57:12 -0700133 } else {
134 numGlyphs = fSkPaint.textToGlyphs(text, byteLength, NULL);
jvanverthaab626c2014-10-16 08:04:39 -0700135 }
jvanverth73f10532014-10-23 11:57:12 -0700136 fTotalVertexCount = kVerticesPerGlyph*numGlyphs;
jvanverthaab626c2014-10-16 08:04:39 -0700137
138 const char* stop = text + byteLength;
139
140 SkAutoKern autokern;
141
142 SkFixed fxMask = ~0;
143 SkFixed fyMask = ~0;
bungeman79738cc2015-03-11 14:05:29 -0700144 SkScalar halfSampleX, halfSampleY;
jvanverthaab626c2014-10-16 08:04:39 -0700145 if (cache->isSubpixel()) {
bungeman79738cc2015-03-11 14:05:29 -0700146 halfSampleX = halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound);
joshualitt5531d512014-12-17 15:50:11 -0800147 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
jvanverthaab626c2014-10-16 08:04:39 -0700148 if (kX_SkAxisAlignment == baseline) {
149 fyMask = 0;
bungeman79738cc2015-03-11 14:05:29 -0700150 halfSampleY = SK_ScalarHalf;
jvanverthaab626c2014-10-16 08:04:39 -0700151 } else if (kY_SkAxisAlignment == baseline) {
152 fxMask = 0;
bungeman79738cc2015-03-11 14:05:29 -0700153 halfSampleX = SK_ScalarHalf;
jvanverthaab626c2014-10-16 08:04:39 -0700154 }
155 } else {
bungeman79738cc2015-03-11 14:05:29 -0700156 halfSampleX = halfSampleY = SK_ScalarHalf;
jvanverthaab626c2014-10-16 08:04:39 -0700157 }
158
bungeman79738cc2015-03-11 14:05:29 -0700159 Sk48Dot16 fx = SkScalarTo48Dot16(x + halfSampleX);
160 Sk48Dot16 fy = SkScalarTo48Dot16(y + halfSampleY);
jvanverthaab626c2014-10-16 08:04:39 -0700161
joshualitt73483d92014-12-23 07:58:02 -0800162 // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix, but for
163 // performance reasons we just invert here instead
164 if (!viewMatrix.invert(&fLocalMatrix)) {
joshualitt787bb482015-01-02 10:54:26 -0800165 SkDebugf("Cannot invert viewmatrix\n");
166 return;
joshualitt5531d512014-12-17 15:50:11 -0800167 }
jvanverthaab626c2014-10-16 08:04:39 -0700168
169 while (text < stop) {
170 const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
171
172 fx += autokern.adjust(glyph);
173
174 if (glyph.fWidth) {
175 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
176 glyph.getSubXFixed(),
jvanvertha7634612015-03-19 06:08:31 -0700177 glyph.getSubYFixed(),
178 GrGlyph::kCoverage_MaskStyle),
bungeman79738cc2015-03-11 14:05:29 -0700179 Sk48Dot16FloorToInt(fx),
180 Sk48Dot16FloorToInt(fy),
jvanverthaab626c2014-10-16 08:04:39 -0700181 fontScaler);
182 }
183
184 fx += glyph.fAdvanceX;
185 fy += glyph.fAdvanceY;
186 }
187
188 this->finish();
189}
190
joshualitt570d2f82015-02-25 13:19:48 -0800191void GrBitmapTextContext::onDrawPosText(GrRenderTarget* rt, const GrClip& clip,
192 const GrPaint& paint, const SkPaint& skPaint,
joshualitt5531d512014-12-17 15:50:11 -0800193 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800194 const char text[], size_t byteLength,
195 const SkScalar pos[], int scalarsPerPosition,
joshualitt6e8cd962015-03-20 10:30:14 -0700196 const SkPoint& offset, const SkIRect& regionClipBounds) {
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000197 SkASSERT(byteLength == 0 || text != NULL);
198 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
199
200 // nothing to draw
201 if (text == NULL || byteLength == 0/* || fRC->isEmpty()*/) {
202 return;
203 }
204
joshualitt6e8cd962015-03-20 10:30:14 -0700205 this->init(rt, clip, paint, skPaint, regionClipBounds);
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000206
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000207 SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
208
joshualitt5531d512014-12-17 15:50:11 -0800209 SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, &viewMatrix);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000210 SkGlyphCache* cache = autoCache.getCache();
211 GrFontScaler* fontScaler = GetGrFontScaler(cache);
212
joshualitt73483d92014-12-23 07:58:02 -0800213 // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix, but for
214 // performance reasons we just invert here instead
215 if (!viewMatrix.invert(&fLocalMatrix)) {
joshualitt787bb482015-01-02 10:54:26 -0800216 SkDebugf("Cannot invert viewmatrix\n");
217 return;
joshualitt5531d512014-12-17 15:50:11 -0800218 }
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000219
jvanverth73f10532014-10-23 11:57:12 -0700220 int numGlyphs = fSkPaint.textToGlyphs(text, byteLength, NULL);
221 fTotalVertexCount = kVerticesPerGlyph*numGlyphs;
222
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000223 const char* stop = text + byteLength;
kkinnunencb9a2c82014-06-12 23:06:28 -0700224 SkTextAlignProc alignProc(fSkPaint.getTextAlign());
joshualitt73483d92014-12-23 07:58:02 -0800225 SkTextMapStateProc tmsProc(viewMatrix, offset, scalarsPerPosition);
bungeman79738cc2015-03-11 14:05:29 -0700226 SkScalar halfSampleX = 0, halfSampleY = 0;
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000227
228 if (cache->isSubpixel()) {
229 // maybe we should skip the rounding if linearText is set
joshualitt73483d92014-12-23 07:58:02 -0800230 SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000231
232 SkFixed fxMask = ~0;
233 SkFixed fyMask = ~0;
234 if (kX_SkAxisAlignment == baseline) {
235 fyMask = 0;
bungeman79738cc2015-03-11 14:05:29 -0700236 halfSampleY = SK_ScalarHalf;
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000237 } else if (kY_SkAxisAlignment == baseline) {
238 fxMask = 0;
bungeman79738cc2015-03-11 14:05:29 -0700239 halfSampleX = SK_ScalarHalf;
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000240 }
241
242 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
243 while (text < stop) {
kkinnunencb9a2c82014-06-12 23:06:28 -0700244 SkPoint tmsLoc;
245 tmsProc(pos, &tmsLoc);
bungeman79738cc2015-03-11 14:05:29 -0700246 Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + halfSampleX);
247 Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + halfSampleY);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000248
249 const SkGlyph& glyph = glyphCacheProc(cache, &text,
250 fx & fxMask, fy & fyMask);
251
252 if (glyph.fWidth) {
jvanverth0fedb192014-10-08 09:07:27 -0700253 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
254 glyph.getSubXFixed(),
jvanvertha7634612015-03-19 06:08:31 -0700255 glyph.getSubYFixed(),
256 GrGlyph::kCoverage_MaskStyle),
bungeman79738cc2015-03-11 14:05:29 -0700257 Sk48Dot16FloorToInt(fx),
258 Sk48Dot16FloorToInt(fy),
jvanverth0fedb192014-10-08 09:07:27 -0700259 fontScaler);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000260 }
261 pos += scalarsPerPosition;
262 }
263 } else {
264 while (text < stop) {
265 const char* currentText = text;
266 const SkGlyph& metricGlyph = glyphCacheProc(cache, &text, 0, 0);
267
268 if (metricGlyph.fWidth) {
269 SkDEBUGCODE(SkFixed prevAdvX = metricGlyph.fAdvanceX;)
270 SkDEBUGCODE(SkFixed prevAdvY = metricGlyph.fAdvanceY;)
kkinnunencb9a2c82014-06-12 23:06:28 -0700271 SkPoint tmsLoc;
272 tmsProc(pos, &tmsLoc);
bungeman79738cc2015-03-11 14:05:29 -0700273 SkPoint alignLoc;
274 alignProc(tmsLoc, metricGlyph, &alignLoc);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000275
bungeman79738cc2015-03-11 14:05:29 -0700276 Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + halfSampleX);
277 Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + halfSampleY);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000278
279 // have to call again, now that we've been "aligned"
280 const SkGlyph& glyph = glyphCacheProc(cache, &currentText,
281 fx & fxMask, fy & fyMask);
282 // the assumption is that the metrics haven't changed
283 SkASSERT(prevAdvX == glyph.fAdvanceX);
284 SkASSERT(prevAdvY == glyph.fAdvanceY);
285 SkASSERT(glyph.fWidth);
286
jvanverth0fedb192014-10-08 09:07:27 -0700287 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
288 glyph.getSubXFixed(),
jvanvertha7634612015-03-19 06:08:31 -0700289 glyph.getSubYFixed(),
290 GrGlyph::kCoverage_MaskStyle),
bungeman79738cc2015-03-11 14:05:29 -0700291 Sk48Dot16FloorToInt(fx),
292 Sk48Dot16FloorToInt(fy),
jvanverth0fedb192014-10-08 09:07:27 -0700293 fontScaler);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000294 }
295 pos += scalarsPerPosition;
296 }
297 }
298 } else { // not subpixel
299
300 if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) {
301 while (text < stop) {
302 // the last 2 parameters are ignored
303 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
304
305 if (glyph.fWidth) {
kkinnunencb9a2c82014-06-12 23:06:28 -0700306 SkPoint tmsLoc;
307 tmsProc(pos, &tmsLoc);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000308
bungeman79738cc2015-03-11 14:05:29 -0700309 Sk48Dot16 fx = SkScalarTo48Dot16(tmsLoc.fX + SK_ScalarHalf); //halfSampleX;
310 Sk48Dot16 fy = SkScalarTo48Dot16(tmsLoc.fY + SK_ScalarHalf); //halfSampleY;
jvanverth0fedb192014-10-08 09:07:27 -0700311 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
312 glyph.getSubXFixed(),
jvanvertha7634612015-03-19 06:08:31 -0700313 glyph.getSubYFixed(),
314 GrGlyph::kCoverage_MaskStyle),
bungeman79738cc2015-03-11 14:05:29 -0700315 Sk48Dot16FloorToInt(fx),
316 Sk48Dot16FloorToInt(fy),
jvanverth0fedb192014-10-08 09:07:27 -0700317 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
bungeman79738cc2015-03-11 14:05:29 -0700330 SkPoint alignLoc;
331 alignProc(tmsLoc, glyph, &alignLoc);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000332
bungeman79738cc2015-03-11 14:05:29 -0700333 Sk48Dot16 fx = SkScalarTo48Dot16(alignLoc.fX + SK_ScalarHalf); //halfSampleX;
334 Sk48Dot16 fy = SkScalarTo48Dot16(alignLoc.fY + SK_ScalarHalf); //halfSampleY;
jvanverth0fedb192014-10-08 09:07:27 -0700335 this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(),
336 glyph.getSubXFixed(),
jvanvertha7634612015-03-19 06:08:31 -0700337 glyph.getSubYFixed(),
338 GrGlyph::kCoverage_MaskStyle),
bungeman79738cc2015-03-11 14:05:29 -0700339 Sk48Dot16FloorToInt(fx),
340 Sk48Dot16FloorToInt(fy),
jvanverth0fedb192014-10-08 09:07:27 -0700341 fontScaler);
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000342 }
343 pos += scalarsPerPosition;
344 }
345 }
346 }
commit-bot@chromium.orgcbbc4812014-01-30 22:05:47 +0000347
348 this->finish();
commit-bot@chromium.orge8612d92014-01-28 22:02:07 +0000349}
350
joshualitt9853cce2014-11-17 14:22:48 -0800351static size_t get_vertex_stride(GrMaskFormat maskFormat) {
352 switch (maskFormat) {
353 case kA8_GrMaskFormat:
354 return kGrayTextVASize;
355 case kARGB_GrMaskFormat:
356 return kColorTextVASize;
357 default:
358 return kLCDTextVASize;
359 }
360}
361
joshualitt9853cce2014-11-17 14:22:48 -0800362static void* alloc_vertices(GrDrawTarget* drawTarget,
363 int numVertices,
364 GrMaskFormat maskFormat) {
jvanverth73f10532014-10-23 11:57:12 -0700365 if (numVertices <= 0) {
366 return NULL;
367 }
368
369 // set up attributes
jvanverth73f10532014-10-23 11:57:12 -0700370 void* vertices = NULL;
371 bool success = drawTarget->reserveVertexAndIndexSpace(numVertices,
joshualitt9853cce2014-11-17 14:22:48 -0800372 get_vertex_stride(maskFormat),
jvanverth73f10532014-10-23 11:57:12 -0700373 0,
374 &vertices,
375 NULL);
376 GrAlwaysAssert(success);
377 return vertices;
378}
379
jvanverth787cdf92014-12-04 10:46:50 -0800380inline bool GrBitmapTextContext::uploadGlyph(GrGlyph* glyph, GrFontScaler* scaler) {
381 if (!fStrike->glyphTooLargeForAtlas(glyph)) {
382 if (fStrike->addGlyphToAtlas(glyph, scaler)) {
383 return true;
384 }
385
386 // try to clear out an unused plot before we flush
387 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
388 fStrike->addGlyphToAtlas(glyph, scaler)) {
389 return true;
390 }
391
392 if (c_DumpFontCache) {
393#ifdef SK_DEVELOPER
394 fContext->getFontCache()->dump();
395#endif
396 }
397
398 // before we purge the cache, we must flush any accumulated draws
399 this->flush();
400 fContext->flush();
401
402 // we should have an unused plot now
403 if (fContext->getFontCache()->freeUnusedPlot(fStrike, glyph) &&
404 fStrike->addGlyphToAtlas(glyph, scaler)) {
405 return true;
406 }
407
408 // we should never get here
409 SkASSERT(false);
410 }
411
412 return false;
413}
414
jvanverth0fedb192014-10-08 09:07:27 -0700415void GrBitmapTextContext::appendGlyph(GrGlyph::PackedID packed,
bungeman79738cc2015-03-11 14:05:29 -0700416 int vx, int vy,
jvanverth0fedb192014-10-08 09:07:27 -0700417 GrFontScaler* scaler) {
Mike Klein6a25bd02014-08-29 10:03:59 -0400418 if (NULL == fDrawTarget) {
419 return;
420 }
421
422 if (NULL == fStrike) {
jvanvertha7634612015-03-19 06:08:31 -0700423 fStrike = fContext->getFontCache()->getStrike(scaler);
Mike Klein6a25bd02014-08-29 10:03:59 -0400424 }
425
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000426 GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
427 if (NULL == glyph || glyph->fBounds.isEmpty()) {
428 return;
429 }
430
bungeman79738cc2015-03-11 14:05:29 -0700431 int x = vx + glyph->fBounds.fLeft;
432 int y = vy + glyph->fBounds.fTop;
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000433
434 // keep them as ints until we've done the clip-test
jvanverth5a105ff2015-02-18 11:36:35 -0800435 int width = glyph->fBounds.width();
436 int height = glyph->fBounds.height();
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000437
438 // check if we clipped out
qiankun.miao06fb35f2015-01-29 18:36:52 -0800439 if (fClipRect.quickReject(x, y, x + width, y + height)) {
440 return;
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000441 }
442
joshualittc2625822014-12-18 16:40:54 -0800443 // If the glyph is too large we fall back to paths
jvanverth787cdf92014-12-04 10:46:50 -0800444 if (NULL == glyph->fPlot && !uploadGlyph(glyph, scaler)) {
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000445 if (NULL == glyph->fPath) {
446 SkPath* path = SkNEW(SkPath);
447 if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
448 // flag the glyph as being dead?
449 delete path;
450 return;
451 }
452 glyph->fPath = path;
453 }
454
bsalomonec87dc62014-10-14 10:52:00 -0700455 // flush any accumulated draws before drawing this glyph as a path.
456 this->flush();
457
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000458 SkMatrix translate;
bungeman79738cc2015-03-11 14:05:29 -0700459 translate.setTranslate(SkIntToScalar(vx), SkIntToScalar(vy));
joshualittc2625822014-12-18 16:40:54 -0800460 SkPath tmpPath(*glyph->fPath);
461 tmpPath.transform(translate);
egdanield58a0ba2014-06-11 10:30:05 -0700462 GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle);
joshualitt570d2f82015-02-25 13:19:48 -0800463 fContext->drawPath(fRenderTarget, fClip, fPaint, SkMatrix::I(), tmpPath, strokeInfo);
jvanverth73f10532014-10-23 11:57:12 -0700464
465 // remove this glyph from the vertices we need to allocate
466 fTotalVertexCount -= kVerticesPerGlyph;
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000467 return;
468 }
469
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000470 SkASSERT(glyph->fPlot);
471 GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken();
472 glyph->fPlot->setDrawToken(drawToken);
473
jvanverth294c3262014-10-10 11:36:12 -0700474 // the current texture/maskformat must match what the glyph needs
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000475 GrTexture* texture = glyph->fPlot->texture();
476 SkASSERT(texture);
477
jvanverth73f10532014-10-23 11:57:12 -0700478 if (fCurrTexture != texture || fCurrVertex + kVerticesPerGlyph > fAllocVertexCount) {
jvanverth0fedb192014-10-08 09:07:27 -0700479 this->flush();
Mike Klein6a25bd02014-08-29 10:03:59 -0400480 fCurrTexture = texture;
481 fCurrTexture->ref();
jvanverth294c3262014-10-10 11:36:12 -0700482 fCurrMaskFormat = glyph->fMaskFormat;
Mike Klein6a25bd02014-08-29 10:03:59 -0400483 }
484
Mike Klein6a25bd02014-08-29 10:03:59 -0400485 if (NULL == fVertices) {
jvanverth73f10532014-10-23 11:57:12 -0700486 int maxQuadVertices = kVerticesPerGlyph * fContext->getQuadIndexBuffer()->maxQuads();
487 fAllocVertexCount = SkMin32(fTotalVertexCount, maxQuadVertices);
egdanieled3af662014-10-31 06:55:45 -0700488 fVertices = alloc_vertices(fDrawTarget, fAllocVertexCount, fCurrMaskFormat);
Mike Klein6a25bd02014-08-29 10:03:59 -0400489 }
490
commit-bot@chromium.org3ae0e6c2014-02-11 18:24:25 +0000491 SkRect r;
bungeman79738cc2015-03-11 14:05:29 -0700492 r.fLeft = SkIntToScalar(x);
493 r.fTop = SkIntToScalar(y);
494 r.fRight = r.fLeft + SkIntToScalar(width);
495 r.fBottom = r.fTop + SkIntToScalar(height);
commit-bot@chromium.org3ae0e6c2014-02-11 18:24:25 +0000496
reed10d03272014-10-01 09:24:12 -0700497 fVertexBounds.joinNonEmptyArg(r);
bungeman79738cc2015-03-11 14:05:29 -0700498
jvanverth5a105ff2015-02-18 11:36:35 -0800499 int u0 = glyph->fAtlasLocation.fX;
500 int v0 = glyph->fAtlasLocation.fY;
501 int u1 = u0 + width;
502 int v1 = v0 + height;
commit-bot@chromium.org3ae0e6c2014-02-11 18:24:25 +0000503
joshualitt9853cce2014-11-17 14:22:48 -0800504 size_t vertSize = get_vertex_stride(fCurrMaskFormat);
jvanverth5a105ff2015-02-18 11:36:35 -0800505 intptr_t vertex = reinterpret_cast<intptr_t>(fVertices) + vertSize * fCurrVertex;
bsalomon594069f2014-06-06 06:16:34 -0700506
jvanverth5a105ff2015-02-18 11:36:35 -0800507 // V0
508 SkPoint* position = reinterpret_cast<SkPoint*>(vertex);
509 position->set(r.fLeft, r.fTop);
reede4ef1ca2015-02-17 18:38:38 -0800510 if (kA8_GrMaskFormat == fCurrMaskFormat) {
jvanverth5a105ff2015-02-18 11:36:35 -0800511 SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
512 *color = fPaint.getColor();
reede4ef1ca2015-02-17 18:38:38 -0800513 }
jvanverth5a105ff2015-02-18 11:36:35 -0800514 SkIPoint16* textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize -
515 sizeof(SkIPoint16));
516 textureCoords->set(u0, v0);
517 vertex += vertSize;
bungeman79738cc2015-03-11 14:05:29 -0700518
jvanverth5a105ff2015-02-18 11:36:35 -0800519 // V1
520 position = reinterpret_cast<SkPoint*>(vertex);
521 position->set(r.fLeft, r.fBottom);
522 if (kA8_GrMaskFormat == fCurrMaskFormat) {
523 SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
524 *color = fPaint.getColor();
525 }
526 textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize - sizeof(SkIPoint16));
527 textureCoords->set(u0, v1);
528 vertex += vertSize;
bungeman79738cc2015-03-11 14:05:29 -0700529
jvanverth5a105ff2015-02-18 11:36:35 -0800530 // V2
531 position = reinterpret_cast<SkPoint*>(vertex);
532 position->set(r.fRight, r.fBottom);
533 if (kA8_GrMaskFormat == fCurrMaskFormat) {
534 SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
535 *color = fPaint.getColor();
536 }
537 textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize - sizeof(SkIPoint16));
538 textureCoords->set(u1, v1);
539 vertex += vertSize;
bungeman79738cc2015-03-11 14:05:29 -0700540
jvanverth5a105ff2015-02-18 11:36:35 -0800541 // V3
542 position = reinterpret_cast<SkPoint*>(vertex);
543 position->set(r.fRight, r.fTop);
544 if (kA8_GrMaskFormat == fCurrMaskFormat) {
545 SkColor* color = reinterpret_cast<SkColor*>(vertex + sizeof(SkPoint));
546 *color = fPaint.getColor();
547 }
548 textureCoords = reinterpret_cast<SkIPoint16*>(vertex + vertSize - sizeof(SkIPoint16));
549 textureCoords->set(u1, v0);
550
jvanverth@google.comc7a40fa2013-10-16 18:15:34 +0000551 fCurrVertex += 4;
552}
jvanverth0fedb192014-10-08 09:07:27 -0700553
jvanverth0fedb192014-10-08 09:07:27 -0700554void GrBitmapTextContext::flush() {
555 if (NULL == fDrawTarget) {
556 return;
557 }
558
jvanverth0fedb192014-10-08 09:07:27 -0700559 if (fCurrVertex > 0) {
egdaniel8dd688b2015-01-22 10:16:09 -0800560 GrPipelineBuilder pipelineBuilder;
joshualitt44701df2015-02-23 14:44:57 -0800561 pipelineBuilder.setFromPaint(fPaint, fRenderTarget, fClip);
joshualitt780b11e2014-11-18 09:40:40 -0800562
jvanverth0fedb192014-10-08 09:07:27 -0700563 // setup our sampler state for our text texture/atlas
564 SkASSERT(SkIsAlign4(fCurrVertex));
565 SkASSERT(fCurrTexture);
jvanverth0fedb192014-10-08 09:07:27 -0700566
jvanverth0fedb192014-10-08 09:07:27 -0700567 SkASSERT(fStrike);
joshualitt2e3b3e32014-12-09 13:31:14 -0800568 GrColor color = fPaint.getColor();
jvanverth294c3262014-10-10 11:36:12 -0700569 switch (fCurrMaskFormat) {
jvanverth0fedb192014-10-08 09:07:27 -0700570 // Color bitmap text
joshualitt2e3b3e32014-12-09 13:31:14 -0800571 case kARGB_GrMaskFormat: {
572 int a = fSkPaint.getAlpha();
573 color = SkColorSetARGB(a, a, a, a);
jvanverth0fedb192014-10-08 09:07:27 -0700574 break;
joshualitt2e3b3e32014-12-09 13:31:14 -0800575 }
jvanverth0fedb192014-10-08 09:07:27 -0700576 // LCD text
jvanverth0fedb192014-10-08 09:07:27 -0700577 case kA565_GrMaskFormat: {
egdaniel378092f2014-12-03 10:40:13 -0800578 // TODO: move supportsRGBCoverage check to setupCoverageEffect and only add LCD
579 // processor if the xp can support it. For now we will simply assume that if
580 // fUseLCDText is true, then we have a known color output.
egdaniel8dd688b2015-01-22 10:16:09 -0800581 const GrXPFactory* xpFactory = pipelineBuilder.getXPFactory();
582 if (!xpFactory->supportsRGBCoverage(0, kRGBA_GrColorComponentFlags)) {
tfarina38406c82014-10-31 07:11:12 -0700583 SkDebugf("LCD Text will not draw correctly.\n");
jvanverth0fedb192014-10-08 09:07:27 -0700584 }
jvanverth0fedb192014-10-08 09:07:27 -0700585 break;
586 }
587 // Grayscale/BW text
588 case kA8_GrMaskFormat:
jvanverth0fedb192014-10-08 09:07:27 -0700589 break;
590 default:
jvanverth294c3262014-10-10 11:36:12 -0700591 SkFAIL("Unexpected mask format.");
jvanverth0fedb192014-10-08 09:07:27 -0700592 }
joshualitt2e3b3e32014-12-09 13:31:14 -0800593
jvanverthbe03bb62015-03-10 11:53:39 -0700594 GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
joshualitt02b05012015-02-11 06:56:30 -0800595 uint32_t textureUniqueID = fCurrTexture->getUniqueID();
596 if (textureUniqueID != fEffectTextureUniqueID ||
597 fCachedGeometryProcessor->color() != color ||
598 !fCachedGeometryProcessor->localMatrix().cheapEqualTo(fLocalMatrix)) {
599 // This will be ignored in the non A8 case
600 bool opaqueVertexColors = GrColorIsOpaque(fPaint.getColor());
601 fCachedGeometryProcessor.reset(GrBitmapTextGeoProc::Create(color,
602 fCurrTexture,
603 params,
604 fCurrMaskFormat,
605 opaqueVertexColors,
606 fLocalMatrix));
607 fEffectTextureUniqueID = textureUniqueID;
joshualitt2e3b3e32014-12-09 13:31:14 -0800608 }
609
jvanverth73f10532014-10-23 11:57:12 -0700610 int nGlyphs = fCurrVertex / kVerticesPerGlyph;
jvanverth0fedb192014-10-08 09:07:27 -0700611 fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
egdaniel8dd688b2015-01-22 10:16:09 -0800612 fDrawTarget->drawIndexedInstances(&pipelineBuilder,
joshualitt56995b52014-12-11 15:44:02 -0800613 fCachedGeometryProcessor.get(),
joshualitt9853cce2014-11-17 14:22:48 -0800614 kTriangles_GrPrimitiveType,
jvanverth0fedb192014-10-08 09:07:27 -0700615 nGlyphs,
joshualitt9853cce2014-11-17 14:22:48 -0800616 kVerticesPerGlyph,
617 kIndicesPerGlyph,
618 &fVertexBounds);
jvanverth0fedb192014-10-08 09:07:27 -0700619
620 fDrawTarget->resetVertexSource();
621 fVertices = NULL;
jvanverth73f10532014-10-23 11:57:12 -0700622 fAllocVertexCount = 0;
623 // reset to be those that are left
624 fTotalVertexCount -= fCurrVertex;
jvanverth0fedb192014-10-08 09:07:27 -0700625 fCurrVertex = 0;
626 fVertexBounds.setLargestInverted();
627 SkSafeSetNull(fCurrTexture);
628 }
629}
630
631inline void GrBitmapTextContext::finish() {
632 this->flush();
jvanverth73f10532014-10-23 11:57:12 -0700633 fTotalVertexCount = 0;
jvanverth0fedb192014-10-08 09:07:27 -0700634
635 GrTextContext::finish();
636}
637