blob: eb6abb39a814ddded730fec3b59f1609dec0dc6c [file] [log] [blame]
kkinnunenc6cb56f2014-06-24 00:12:27 -07001/*
2 * Copyright 2014 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 "GrStencilAndCoverTextContext.h"
joshualitt1d89e8d2015-04-01 12:40:54 -07009#include "GrAtlasTextContext.h"
robertphillips7715e062016-04-22 10:57:16 -070010#include "GrContext.h"
robertphillipsea461502015-05-26 11:38:03 -070011#include "GrDrawContext.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070012#include "GrPath.h"
cdaltonb85a0aa2014-07-21 15:32:44 -070013#include "GrPathRange.h"
bsalomond309e7a2015-04-30 14:18:54 -070014#include "GrResourceProvider.h"
joshualitte55750e2016-02-10 12:52:21 -080015#include "GrTextUtils.h"
jvanverthaab626c2014-10-16 08:04:39 -070016#include "SkAutoKern.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070017#include "SkDraw.h"
18#include "SkDrawProcs.h"
19#include "SkGlyphCache.h"
cdaltoncdd79072015-10-05 15:37:35 -070020#include "SkGrPriv.h"
joshualitte55750e2016-02-10 12:52:21 -080021#include "SkDrawFilter.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070022#include "SkPath.h"
halcanary33779752015-10-27 14:01:05 -070023#include "SkTextBlobRunIterator.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070024#include "SkTextMapStateProc.h"
cdalton855d83f2014-09-18 13:51:53 -070025#include "SkTextFormatParams.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070026
bsalomon1fcc01c2015-09-09 09:48:06 -070027#include "batches/GrDrawPathBatch.h"
28
cdaltoncdd79072015-10-05 15:37:35 -070029template<typename Key, typename Val> static void delete_hash_map_entry(const Key&, Val* val) {
30 SkASSERT(*val);
31 delete *val;
32}
33
34template<typename T> static void delete_hash_table_entry(T* val) {
35 SkASSERT(*val);
36 delete *val;
37}
38
joshualitt27004b72016-02-11 12:00:33 -080039GrStencilAndCoverTextContext::GrStencilAndCoverTextContext()
40 : fFallbackTextContext(nullptr)
joshualitte55750e2016-02-10 12:52:21 -080041 , fCacheSize(0) {
kkinnunenc6cb56f2014-06-24 00:12:27 -070042}
43
joshualitt6e8cd962015-03-20 10:30:14 -070044GrStencilAndCoverTextContext*
joshualitt27004b72016-02-11 12:00:33 -080045GrStencilAndCoverTextContext::Create() {
46 GrStencilAndCoverTextContext* textContext = new GrStencilAndCoverTextContext();
47 textContext->fFallbackTextContext = GrAtlasTextContext::Create();
jvanverth8c27a182014-10-14 08:45:50 -070048
49 return textContext;
50}
51
kkinnunenc6cb56f2014-06-24 00:12:27 -070052GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() {
joshualitte55750e2016-02-10 12:52:21 -080053 delete fFallbackTextContext;
cdaltoncdd79072015-10-05 15:37:35 -070054 fBlobIdCache.foreach(delete_hash_map_entry<uint32_t, TextBlob*>);
55 fBlobKeyCache.foreach(delete_hash_table_entry<TextBlob*>);
kkinnunenc6cb56f2014-06-24 00:12:27 -070056}
57
cdaltoncdd79072015-10-05 15:37:35 -070058bool GrStencilAndCoverTextContext::internalCanDraw(const SkPaint& skPaint) {
cdaltone68f7362015-03-25 14:02:37 -070059 if (skPaint.getRasterizer()) {
jvanverth0fedb192014-10-08 09:07:27 -070060 return false;
61 }
cdaltone68f7362015-03-25 14:02:37 -070062 if (skPaint.getMaskFilter()) {
jvanverth0fedb192014-10-08 09:07:27 -070063 return false;
64 }
kkinnunen50b58e62015-05-18 23:02:07 -070065 if (SkPathEffect* pe = skPaint.getPathEffect()) {
halcanary96fcdcc2015-08-27 07:41:13 -070066 if (pe->asADash(nullptr) != SkPathEffect::kDash_DashType) {
kkinnunen50b58e62015-05-18 23:02:07 -070067 return false;
68 }
jvanverth0fedb192014-10-08 09:07:27 -070069 }
cdalton7d5c9502015-10-03 13:28:35 -070070 // No hairlines. They would require new paths with customized strokes for every new draw matrix.
71 return SkPaint::kStroke_Style != skPaint.getStyle() || 0 != skPaint.getStrokeWidth();
jvanverth0fedb192014-10-08 09:07:27 -070072}
73
joshualitt27004b72016-02-11 12:00:33 -080074void GrStencilAndCoverTextContext::drawText(GrContext* context, GrDrawContext* dc,
joshualitte55750e2016-02-10 12:52:21 -080075 const GrClip& clip, const GrPaint& paint,
76 const SkPaint& skPaint, const SkMatrix& viewMatrix,
joshualitt2c89bc12016-02-11 05:42:30 -080077 const SkSurfaceProps& props,
joshualitte55750e2016-02-10 12:52:21 -080078 const char text[], size_t byteLength,
79 SkScalar x, SkScalar y, const SkIRect& clipBounds) {
joshualitt27004b72016-02-11 12:00:33 -080080 if (context->abandoned()) {
joshualitte55750e2016-02-10 12:52:21 -080081 return;
82 } else if (this->canDraw(skPaint, viewMatrix)) {
kkinnunen68c63b32016-03-04 00:12:33 -080083 if (skPaint.getTextSize() > 0) {
84 TextRun run(skPaint);
cdalton862cff32016-05-12 15:09:48 -070085 GrPipelineBuilder pipelineBuilder(paint, dc->accessRenderTarget());
kkinnunen68c63b32016-03-04 00:12:33 -080086 run.setText(text, byteLength, x, y);
cdalton862cff32016-05-12 15:09:48 -070087 run.draw(context, dc, &pipelineBuilder, clip, paint.getColor(), viewMatrix, props, 0, 0,
kkinnunen68c63b32016-03-04 00:12:33 -080088 clipBounds, fFallbackTextContext, skPaint);
89 }
joshualitte55750e2016-02-10 12:52:21 -080090 return;
joshualitt27004b72016-02-11 12:00:33 -080091 } else if (fFallbackTextContext->canDraw(skPaint, viewMatrix, props,
92 *context->caps()->shaderCaps())) {
93 fFallbackTextContext->drawText(context, dc, clip, paint, skPaint, viewMatrix, props, text,
joshualitte55750e2016-02-10 12:52:21 -080094 byteLength, x, y, clipBounds);
95 return;
96 }
97
98 // fall back to drawing as a path
joshualitt27004b72016-02-11 12:00:33 -080099 GrTextUtils::DrawTextAsPath(context, dc, clip, skPaint, viewMatrix, text, byteLength, x, y,
joshualitte55750e2016-02-10 12:52:21 -0800100 clipBounds);
cdalton3bd909a2015-10-05 14:57:20 -0700101}
jvanverthaab626c2014-10-16 08:04:39 -0700102
joshualitt27004b72016-02-11 12:00:33 -0800103void GrStencilAndCoverTextContext::drawPosText(GrContext* context, GrDrawContext* dc,
joshualitte55750e2016-02-10 12:52:21 -0800104 const GrClip& clip,
105 const GrPaint& paint,
106 const SkPaint& skPaint,
107 const SkMatrix& viewMatrix,
joshualitt2c89bc12016-02-11 05:42:30 -0800108 const SkSurfaceProps& props,
joshualitte55750e2016-02-10 12:52:21 -0800109 const char text[],
110 size_t byteLength,
111 const SkScalar pos[],
112 int scalarsPerPosition,
113 const SkPoint& offset,
114 const SkIRect& clipBounds) {
joshualitt27004b72016-02-11 12:00:33 -0800115 if (context->abandoned()) {
joshualitte55750e2016-02-10 12:52:21 -0800116 return;
117 } else if (this->canDraw(skPaint, viewMatrix)) {
kkinnunen68c63b32016-03-04 00:12:33 -0800118 if (skPaint.getTextSize() > 0) {
119 TextRun run(skPaint);
cdalton862cff32016-05-12 15:09:48 -0700120 GrPipelineBuilder pipelineBuilder(paint, dc->accessRenderTarget());
kkinnunen68c63b32016-03-04 00:12:33 -0800121 run.setPosText(text, byteLength, pos, scalarsPerPosition, offset);
cdalton862cff32016-05-12 15:09:48 -0700122 run.draw(context, dc, &pipelineBuilder, clip, paint.getColor(), viewMatrix, props, 0, 0,
kkinnunen68c63b32016-03-04 00:12:33 -0800123 clipBounds, fFallbackTextContext, skPaint);
124 }
joshualitte55750e2016-02-10 12:52:21 -0800125 return;
joshualitt27004b72016-02-11 12:00:33 -0800126 } else if (fFallbackTextContext->canDraw(skPaint, viewMatrix, props,
127 *context->caps()->shaderCaps())) {
128 fFallbackTextContext->drawPosText(context, dc, clip, paint, skPaint, viewMatrix, props,
joshualitte55750e2016-02-10 12:52:21 -0800129 text, byteLength, pos,
130 scalarsPerPosition, offset, clipBounds);
131 return;
132 }
133
134 // fall back to drawing as a path
joshualitt27004b72016-02-11 12:00:33 -0800135 GrTextUtils::DrawPosTextAsPath(context, dc, props, clip, skPaint, viewMatrix, text,
joshualitte55750e2016-02-10 12:52:21 -0800136 byteLength, pos, scalarsPerPosition, offset, clipBounds);
137}
138
joshualitt27004b72016-02-11 12:00:33 -0800139void GrStencilAndCoverTextContext::uncachedDrawTextBlob(GrContext* context,
140 GrDrawContext* dc,
joshualitte55750e2016-02-10 12:52:21 -0800141 const GrClip& clip, const SkPaint& skPaint,
142 const SkMatrix& viewMatrix,
joshualitt2c89bc12016-02-11 05:42:30 -0800143 const SkSurfaceProps& props,
joshualitte55750e2016-02-10 12:52:21 -0800144 const SkTextBlob* blob,
145 SkScalar x, SkScalar y,
146 SkDrawFilter* drawFilter,
147 const SkIRect& clipBounds) {
148 SkPaint runPaint = skPaint;
149
150 SkTextBlobRunIterator it(blob);
151 for (;!it.done(); it.next()) {
152 size_t textLen = it.glyphCount() * sizeof(uint16_t);
153 const SkPoint& offset = it.offset();
154
155 // applyFontToPaint() always overwrites the exact same attributes,
156 // so it is safe to not re-seed the paint for this reason.
157 it.applyFontToPaint(&runPaint);
158
159 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
160 // A false return from filter() means we should abort the current draw.
161 runPaint = skPaint;
162 continue;
163 }
164
joshualitt8e84a1e2016-02-16 11:09:25 -0800165 runPaint.setFlags(GrTextUtils::FilterTextFlags(props, runPaint));
joshualitte55750e2016-02-10 12:52:21 -0800166
167 GrPaint grPaint;
brianosmanb461d342016-04-13 13:10:14 -0700168 if (!SkPaintToGrPaint(context, runPaint, viewMatrix, dc->isGammaCorrect(), &grPaint)) {
joshualitte55750e2016-02-10 12:52:21 -0800169 return;
170 }
171
172 switch (it.positioning()) {
173 case SkTextBlob::kDefault_Positioning:
joshualitt27004b72016-02-11 12:00:33 -0800174 this->drawText(context, dc, clip, grPaint, runPaint, viewMatrix, props,
joshualitt2c89bc12016-02-11 05:42:30 -0800175 (const char *)it.glyphs(),
joshualitte55750e2016-02-10 12:52:21 -0800176 textLen, x + offset.x(), y + offset.y(), clipBounds);
177 break;
178 case SkTextBlob::kHorizontal_Positioning:
joshualitt27004b72016-02-11 12:00:33 -0800179 this->drawPosText(context, dc, clip, grPaint, runPaint, viewMatrix, props,
joshualitt2c89bc12016-02-11 05:42:30 -0800180 (const char*)it.glyphs(),
joshualitte55750e2016-02-10 12:52:21 -0800181 textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()),
182 clipBounds);
183 break;
184 case SkTextBlob::kFull_Positioning:
joshualitt27004b72016-02-11 12:00:33 -0800185 this->drawPosText(context, dc, clip, grPaint, runPaint, viewMatrix, props,
joshualitt2c89bc12016-02-11 05:42:30 -0800186 (const char*)it.glyphs(),
joshualitte55750e2016-02-10 12:52:21 -0800187 textLen, it.pos(), 2, SkPoint::Make(x, y), clipBounds);
188 break;
189 }
190
191 if (drawFilter) {
192 // A draw filter may change the paint arbitrarily, so we must re-seed in this case.
193 runPaint = skPaint;
194 }
195 }
cdaltoncdd79072015-10-05 15:37:35 -0700196}
197
joshualitt27004b72016-02-11 12:00:33 -0800198void GrStencilAndCoverTextContext::drawTextBlob(GrContext* context, GrDrawContext* dc,
cdaltoncdd79072015-10-05 15:37:35 -0700199 const GrClip& clip, const SkPaint& skPaint,
200 const SkMatrix& viewMatrix,
joshualitt2c89bc12016-02-11 05:42:30 -0800201 const SkSurfaceProps& props,
cdaltoncdd79072015-10-05 15:37:35 -0700202 const SkTextBlob* skBlob, SkScalar x, SkScalar y,
203 SkDrawFilter* drawFilter,
204 const SkIRect& clipBounds) {
joshualitt27004b72016-02-11 12:00:33 -0800205 if (context->abandoned()) {
joshualitte55750e2016-02-10 12:52:21 -0800206 return;
207 }
208
cdaltoncdd79072015-10-05 15:37:35 -0700209 if (!this->internalCanDraw(skPaint)) {
joshualitt27004b72016-02-11 12:00:33 -0800210 fFallbackTextContext->drawTextBlob(context, dc, clip, skPaint, viewMatrix, props, skBlob,
211 x, y, drawFilter, clipBounds);
cdaltoncdd79072015-10-05 15:37:35 -0700212 return;
213 }
214
215 if (drawFilter || skPaint.getPathEffect()) {
216 // This draw can't be cached.
joshualitt27004b72016-02-11 12:00:33 -0800217 this->uncachedDrawTextBlob(context, dc, clip, skPaint, viewMatrix, props, skBlob, x, y,
218 drawFilter, clipBounds);
cdaltoncdd79072015-10-05 15:37:35 -0700219 return;
220 }
221
222 GrPaint paint;
brianosmanb461d342016-04-13 13:10:14 -0700223 if (!SkPaintToGrPaint(context, skPaint, viewMatrix, dc->isGammaCorrect(), &paint)) {
cdaltoncdd79072015-10-05 15:37:35 -0700224 return;
225 }
226
227 const TextBlob& blob = this->findOrCreateTextBlob(skBlob, skPaint);
cdalton862cff32016-05-12 15:09:48 -0700228 GrPipelineBuilder pipelineBuilder(paint, dc->accessRenderTarget());
cdaltoncdd79072015-10-05 15:37:35 -0700229
230 TextBlob::Iter iter(blob);
231 for (TextRun* run = iter.get(); run; run = iter.next()) {
cdalton862cff32016-05-12 15:09:48 -0700232 run->draw(context, dc, &pipelineBuilder, clip, paint.getColor(), viewMatrix, props, x, y,
joshualitt2c89bc12016-02-11 05:42:30 -0800233 clipBounds, fFallbackTextContext, skPaint);
cdalton8585dd22015-10-08 08:04:09 -0700234 run->releaseGlyphCache();
cdaltoncdd79072015-10-05 15:37:35 -0700235 }
236}
237
bsalomon6663acf2016-05-10 09:14:17 -0700238static inline int style_key_cnt(const GrStyle& style) {
239 int cnt = GrStyle::KeySize(style, GrStyle::Apply::kPathEffectAndStrokeRec);
240 // We should be able to make a key because we filtered out arbitrary path effects.
241 SkASSERT(cnt > 0);
242 return cnt;
243}
244
245static inline void write_style_key(uint32_t* dst, const GrStyle& style) {
246 // Pass 1 for the scale since the GPU will apply the style not GrStyle::applyToPath().
247 GrStyle::WriteKey(dst, style, GrStyle::Apply::kPathEffectAndStrokeRec, SK_Scalar1);
248}
249
cdaltoncdd79072015-10-05 15:37:35 -0700250const GrStencilAndCoverTextContext::TextBlob&
251GrStencilAndCoverTextContext::findOrCreateTextBlob(const SkTextBlob* skBlob,
252 const SkPaint& skPaint) {
253 // The font-related parameters are baked into the text blob and will override this skPaint, so
254 // the only remaining properties that can affect a TextBlob are the ones related to stroke.
255 if (SkPaint::kFill_Style == skPaint.getStyle()) { // Fast path.
256 if (TextBlob** found = fBlobIdCache.find(skBlob->uniqueID())) {
257 fLRUList.remove(*found);
258 fLRUList.addToTail(*found);
259 return **found;
260 }
cdalton8585dd22015-10-08 08:04:09 -0700261 TextBlob* blob = new TextBlob(skBlob->uniqueID(), skBlob, skPaint);
cdaltoncdd79072015-10-05 15:37:35 -0700262 this->purgeToFit(*blob);
263 fBlobIdCache.set(skBlob->uniqueID(), blob);
264 fLRUList.addToTail(blob);
265 fCacheSize += blob->cpuMemorySize();
266 return *blob;
267 } else {
bsalomon6663acf2016-05-10 09:14:17 -0700268 GrStyle style(skPaint);
cdaltoncdd79072015-10-05 15:37:35 -0700269 SkSTArray<4, uint32_t, true> key;
bsalomon6663acf2016-05-10 09:14:17 -0700270 key.reset(1 + style_key_cnt(style));
cdaltoncdd79072015-10-05 15:37:35 -0700271 key[0] = skBlob->uniqueID();
bsalomon6663acf2016-05-10 09:14:17 -0700272 write_style_key(&key[1], style);
cdaltoncdd79072015-10-05 15:37:35 -0700273 if (TextBlob** found = fBlobKeyCache.find(key)) {
274 fLRUList.remove(*found);
275 fLRUList.addToTail(*found);
276 return **found;
277 }
cdalton8585dd22015-10-08 08:04:09 -0700278 TextBlob* blob = new TextBlob(key, skBlob, skPaint);
cdaltoncdd79072015-10-05 15:37:35 -0700279 this->purgeToFit(*blob);
280 fBlobKeyCache.set(blob);
281 fLRUList.addToTail(blob);
282 fCacheSize += blob->cpuMemorySize();
283 return *blob;
284 }
285}
286
287void GrStencilAndCoverTextContext::purgeToFit(const TextBlob& blob) {
cdalton8585dd22015-10-08 08:04:09 -0700288 static const size_t maxCacheSize = 4 * 1024 * 1024; // Allow up to 4 MB for caching text blobs.
cdaltoncdd79072015-10-05 15:37:35 -0700289
cdalton8585dd22015-10-08 08:04:09 -0700290 size_t maxSizeForNewBlob = maxCacheSize - blob.cpuMemorySize();
cdaltoncdd79072015-10-05 15:37:35 -0700291 while (fCacheSize && fCacheSize > maxSizeForNewBlob) {
292 TextBlob* lru = fLRUList.head();
293 if (1 == lru->key().count()) {
294 // 1-length keys are unterstood to be the blob id.
295 fBlobIdCache.remove(lru->key()[0]);
296 } else {
297 fBlobKeyCache.remove(lru->key());
298 }
299 fLRUList.remove(lru);
300 fCacheSize -= lru->cpuMemorySize();
301 delete lru;
302 }
303}
304
305////////////////////////////////////////////////////////////////////////////////////////////////////
306
cdalton8585dd22015-10-08 08:04:09 -0700307void GrStencilAndCoverTextContext::TextBlob::init(const SkTextBlob* skBlob,
308 const SkPaint& skPaint) {
cdaltoncdd79072015-10-05 15:37:35 -0700309 fCpuMemorySize = sizeof(TextBlob);
310 SkPaint runPaint(skPaint);
halcanary33779752015-10-27 14:01:05 -0700311 for (SkTextBlobRunIterator iter(skBlob); !iter.done(); iter.next()) {
cdaltoncdd79072015-10-05 15:37:35 -0700312 iter.applyFontToPaint(&runPaint); // No need to re-seed the paint.
kkinnunen68c63b32016-03-04 00:12:33 -0800313 if (runPaint.getTextSize() <= 0) {
314 continue;
315 }
bsalomon5aaef1f2015-11-18 14:11:08 -0800316 TextRun* run = this->addToTail(runPaint);
cdaltoncdd79072015-10-05 15:37:35 -0700317
318 const char* text = reinterpret_cast<const char*>(iter.glyphs());
319 size_t byteLength = sizeof(uint16_t) * iter.glyphCount();
320 const SkPoint& runOffset = iter.offset();
321
322 switch (iter.positioning()) {
323 case SkTextBlob::kDefault_Positioning:
cdalton8585dd22015-10-08 08:04:09 -0700324 run->setText(text, byteLength, runOffset.fX, runOffset.fY);
cdaltoncdd79072015-10-05 15:37:35 -0700325 break;
326 case SkTextBlob::kHorizontal_Positioning:
cdalton8585dd22015-10-08 08:04:09 -0700327 run->setPosText(text, byteLength, iter.pos(), 1, SkPoint::Make(0, runOffset.fY));
cdaltoncdd79072015-10-05 15:37:35 -0700328 break;
329 case SkTextBlob::kFull_Positioning:
cdalton8585dd22015-10-08 08:04:09 -0700330 run->setPosText(text, byteLength, iter.pos(), 2, SkPoint::Make(0, 0));
cdaltoncdd79072015-10-05 15:37:35 -0700331 break;
332 }
333
cdalton8585dd22015-10-08 08:04:09 -0700334 fCpuMemorySize += run->computeSizeInCache();
cdaltoncdd79072015-10-05 15:37:35 -0700335 }
cdalton3bd909a2015-10-05 14:57:20 -0700336}
337
338////////////////////////////////////////////////////////////////////////////////////////////////////
339
cdalton02015e52015-10-05 15:28:20 -0700340class GrStencilAndCoverTextContext::FallbackBlobBuilder {
341public:
cdaltoncdd46822015-12-08 10:48:31 -0800342 FallbackBlobBuilder() : fBuffIdx(0), fCount(0) {}
cdalton02015e52015-10-05 15:28:20 -0700343
mtklein5f939ab2016-03-16 10:28:35 -0700344 bool isInitialized() const { return fBuilder != nullptr; }
cdalton02015e52015-10-05 15:28:20 -0700345
346 void init(const SkPaint& font, SkScalar textRatio);
347
348 void appendGlyph(uint16_t glyphId, const SkPoint& pos);
349
cdaltoncdd46822015-12-08 10:48:31 -0800350 const SkTextBlob* buildIfNeeded(int* count);
cdalton02015e52015-10-05 15:28:20 -0700351
352private:
353 enum { kWriteBufferSize = 1024 };
354
355 void flush();
356
357 SkAutoTDelete<SkTextBlobBuilder> fBuilder;
358 SkPaint fFont;
359 int fBuffIdx;
cdaltoncdd46822015-12-08 10:48:31 -0800360 int fCount;
cdalton02015e52015-10-05 15:28:20 -0700361 uint16_t fGlyphIds[kWriteBufferSize];
362 SkPoint fPositions[kWriteBufferSize];
363};
364
365////////////////////////////////////////////////////////////////////////////////////////////////////
366
cdalton3bd909a2015-10-05 14:57:20 -0700367GrStencilAndCoverTextContext::TextRun::TextRun(const SkPaint& fontAndStroke)
bsalomon6663acf2016-05-10 09:14:17 -0700368 : fStyle(fontAndStroke),
cdaltoncdd79072015-10-05 15:37:35 -0700369 fFont(fontAndStroke),
cdalton8585dd22015-10-08 08:04:09 -0700370 fTotalGlyphCount(0),
cdaltoncdd46822015-12-08 10:48:31 -0800371 fFallbackGlyphCount(0),
cdalton8585dd22015-10-08 08:04:09 -0700372 fDetachedGlyphCache(nullptr),
373 fLastDrawnGlyphsID(SK_InvalidUniqueID) {
kkinnunen68c63b32016-03-04 00:12:33 -0800374 SkASSERT(fFont.getTextSize() > 0);
bsalomon6663acf2016-05-10 09:14:17 -0700375 SkASSERT(!fStyle.hasNonDashPathEffect()); // Arbitrary path effects not supported.
376 SkASSERT(!fStyle.isSimpleHairline()); // Hairlines are not supported.
cdalton3bd909a2015-10-05 14:57:20 -0700377
378 // Setting to "fill" ensures that no strokes get baked into font outlines. (We use the GPU path
379 // rendering API for stroking).
380 fFont.setStyle(SkPaint::kFill_Style);
381
bsalomon6663acf2016-05-10 09:14:17 -0700382 if (fFont.isFakeBoldText() && fStyle.isSimpleFill()) {
383 const SkStrokeRec& stroke = fStyle.strokeRec();
cdalton3bd909a2015-10-05 14:57:20 -0700384 // Instead of letting fake bold get baked into the glyph outlines, do it with GPU stroke.
385 SkScalar fakeBoldScale = SkScalarInterpFunc(fFont.getTextSize(),
386 kStdFakeBoldInterpKeys,
387 kStdFakeBoldInterpValues,
388 kStdFakeBoldInterpLength);
389 SkScalar extra = SkScalarMul(fFont.getTextSize(), fakeBoldScale);
cdalton3bd909a2015-10-05 14:57:20 -0700390
bsalomon6663acf2016-05-10 09:14:17 -0700391 SkStrokeRec strokeRec(SkStrokeRec::kFill_InitStyle);
392 strokeRec.setStrokeStyle(stroke.needToApply() ? stroke.getWidth() + extra : extra,
393 true /*strokeAndFill*/);
394 fStyle = GrStyle(strokeRec, fStyle.pathEffect());
cdalton3bd909a2015-10-05 14:57:20 -0700395 fFont.setFakeBoldText(false);
jvanverthaab626c2014-10-16 08:04:39 -0700396 }
397
bsalomon6663acf2016-05-10 09:14:17 -0700398 if (!fFont.getPathEffect() && !fStyle.isDashed()) {
399 const SkStrokeRec& stroke = fStyle.strokeRec();
cdalton3bd909a2015-10-05 14:57:20 -0700400 // We can draw the glyphs from canonically sized paths.
401 fTextRatio = fFont.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
402 fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fFont.getTextSize();
jvanverthaab626c2014-10-16 08:04:39 -0700403
cdalton3bd909a2015-10-05 14:57:20 -0700404 // Compensate for the glyphs being scaled by fTextRatio.
bsalomon6663acf2016-05-10 09:14:17 -0700405 if (!fStyle.isSimpleFill()) {
406 SkStrokeRec strokeRec(SkStrokeRec::kFill_InitStyle);
407 strokeRec.setStrokeStyle(stroke.getWidth() / fTextRatio,
408 SkStrokeRec::kStrokeAndFill_Style == stroke.getStyle());
409 fStyle = GrStyle(strokeRec, fStyle.pathEffect());
cdalton3bd909a2015-10-05 14:57:20 -0700410 }
411
412 fFont.setLinearText(true);
413 fFont.setLCDRenderText(false);
414 fFont.setAutohinted(false);
415 fFont.setHinting(SkPaint::kNo_Hinting);
416 fFont.setSubpixelText(true);
417 fFont.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
418
419 fUsingRawGlyphPaths = SK_Scalar1 == fFont.getTextScaleX() &&
420 0 == fFont.getTextSkewX() &&
421 !fFont.isFakeBoldText() &&
422 !fFont.isVerticalText();
423 } else {
424 fTextRatio = fTextInverseRatio = 1.0f;
425 fUsingRawGlyphPaths = false;
426 }
427
cdalton8585dd22015-10-08 08:04:09 -0700428 // Generate the key that will be used to cache the GPU glyph path objects.
bsalomon6663acf2016-05-10 09:14:17 -0700429 if (fUsingRawGlyphPaths && fStyle.isSimpleFill()) {
cdalton8585dd22015-10-08 08:04:09 -0700430 static const GrUniqueKey::Domain kRawFillPathGlyphDomain = GrUniqueKey::GenerateDomain();
431
432 const SkTypeface* typeface = fFont.getTypeface();
433 GrUniqueKey::Builder builder(&fGlyphPathsKey, kRawFillPathGlyphDomain, 1);
434 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
435 } else {
436 static const GrUniqueKey::Domain kPathGlyphDomain = GrUniqueKey::GenerateDomain();
437
bsalomon6663acf2016-05-10 09:14:17 -0700438 int styleDataCount = GrStyle::KeySize(fStyle, GrStyle::Apply::kPathEffectAndStrokeRec);
439 // Key should be valid since we opted out of drawing arbitrary path effects.
440 SkASSERT(styleDataCount >= 0);
cdalton8585dd22015-10-08 08:04:09 -0700441 if (fUsingRawGlyphPaths) {
442 const SkTypeface* typeface = fFont.getTypeface();
bsalomon6663acf2016-05-10 09:14:17 -0700443 GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain, 2 + styleDataCount);
cdalton8585dd22015-10-08 08:04:09 -0700444 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
bsalomon6663acf2016-05-10 09:14:17 -0700445 reinterpret_cast<uint32_t&>(builder[1]) = styleDataCount;
446 if (styleDataCount) {
447 write_style_key(&builder[2], fStyle);
448 }
cdalton8585dd22015-10-08 08:04:09 -0700449 } else {
450 SkGlyphCache* glyphCache = this->getGlyphCache();
451 const SkTypeface* typeface = glyphCache->getScalerContext()->getTypeface();
452 const SkDescriptor* desc = &glyphCache->getDescriptor();
453 int descDataCount = (desc->getLength() + 3) / 4;
454 GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain,
bsalomon6663acf2016-05-10 09:14:17 -0700455 2 + styleDataCount + descDataCount);
cdalton8585dd22015-10-08 08:04:09 -0700456 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
bsalomon6663acf2016-05-10 09:14:17 -0700457 reinterpret_cast<uint32_t&>(builder[1]) = styleDataCount | (descDataCount << 16);
458 if (styleDataCount) {
459 write_style_key(&builder[2], fStyle);
460 }
461 memcpy(&builder[2 + styleDataCount], desc, desc->getLength());
cdalton8585dd22015-10-08 08:04:09 -0700462 }
463 }
cdalton3bd909a2015-10-05 14:57:20 -0700464}
465
466GrStencilAndCoverTextContext::TextRun::~TextRun() {
cdalton8585dd22015-10-08 08:04:09 -0700467 this->releaseGlyphCache();
cdalton3bd909a2015-10-05 14:57:20 -0700468}
469
470void GrStencilAndCoverTextContext::TextRun::setText(const char text[], size_t byteLength,
cdalton8585dd22015-10-08 08:04:09 -0700471 SkScalar x, SkScalar y) {
cdalton3bd909a2015-10-05 14:57:20 -0700472 SkASSERT(byteLength == 0 || text != nullptr);
473
cdalton8585dd22015-10-08 08:04:09 -0700474 SkGlyphCache* glyphCache = this->getGlyphCache();
benjaminwagnerd936f632016-02-23 10:44:31 -0800475 SkPaint::GlyphCacheProc glyphCacheProc = fFont.getGlyphCacheProc(true);
jvanverthaab626c2014-10-16 08:04:39 -0700476
cdaltoncdd46822015-12-08 10:48:31 -0800477 fTotalGlyphCount = fFont.countText(text, byteLength);
478 fInstanceData.reset(InstanceData::Alloc(GrPathRendering::kTranslate_PathTransformType,
479 fTotalGlyphCount));
cdalton8585dd22015-10-08 08:04:09 -0700480
jvanverthaab626c2014-10-16 08:04:39 -0700481 const char* stop = text + byteLength;
482
483 // Measure first if needed.
cdalton3bd909a2015-10-05 14:57:20 -0700484 if (fFont.getTextAlign() != SkPaint::kLeft_Align) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700485 SkScalar stopX = 0;
486 SkScalar stopY = 0;
jvanverthaab626c2014-10-16 08:04:39 -0700487
488 const char* textPtr = text;
489 while (textPtr < stop) {
490 // We don't need x, y here, since all subpixel variants will have the
491 // same advance.
benjaminwagnerd936f632016-02-23 10:44:31 -0800492 const SkGlyph& glyph = glyphCacheProc(glyphCache, &textPtr);
jvanverthaab626c2014-10-16 08:04:39 -0700493
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700494 stopX += SkFloatToScalar(glyph.fAdvanceX);
495 stopY += SkFloatToScalar(glyph.fAdvanceY);
jvanverthaab626c2014-10-16 08:04:39 -0700496 }
497 SkASSERT(textPtr == stop);
498
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700499 SkScalar alignX = stopX * fTextRatio;
500 SkScalar alignY = stopY * fTextRatio;
jvanverthaab626c2014-10-16 08:04:39 -0700501
cdalton3bd909a2015-10-05 14:57:20 -0700502 if (fFont.getTextAlign() == SkPaint::kCenter_Align) {
jvanverthaab626c2014-10-16 08:04:39 -0700503 alignX = SkScalarHalf(alignX);
504 alignY = SkScalarHalf(alignY);
505 }
506
507 x -= alignX;
508 y -= alignY;
509 }
510
511 SkAutoKern autokern;
512
cdalton02015e52015-10-05 15:28:20 -0700513 FallbackBlobBuilder fallback;
jvanverthaab626c2014-10-16 08:04:39 -0700514 while (text < stop) {
benjaminwagnerd936f632016-02-23 10:44:31 -0800515 const SkGlyph& glyph = glyphCacheProc(glyphCache, &text);
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700516 x += autokern.adjust(glyph) * fTextRatio;
jvanverthaab626c2014-10-16 08:04:39 -0700517 if (glyph.fWidth) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700518 this->appendGlyph(glyph, SkPoint::Make(x, y), &fallback);
jvanverthaab626c2014-10-16 08:04:39 -0700519 }
520
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700521 x += SkFloatToScalar(glyph.fAdvanceX) * fTextRatio;
522 y += SkFloatToScalar(glyph.fAdvanceY) * fTextRatio;
jvanverthaab626c2014-10-16 08:04:39 -0700523 }
cdalton02015e52015-10-05 15:28:20 -0700524
cdaltoncdd46822015-12-08 10:48:31 -0800525 fFallbackTextBlob.reset(fallback.buildIfNeeded(&fFallbackGlyphCount));
jvanverthaab626c2014-10-16 08:04:39 -0700526}
527
cdalton3bd909a2015-10-05 14:57:20 -0700528void GrStencilAndCoverTextContext::TextRun::setPosText(const char text[], size_t byteLength,
529 const SkScalar pos[], int scalarsPerPosition,
cdalton8585dd22015-10-08 08:04:09 -0700530 const SkPoint& offset) {
halcanary96fcdcc2015-08-27 07:41:13 -0700531 SkASSERT(byteLength == 0 || text != nullptr);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700532 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
533
cdalton8585dd22015-10-08 08:04:09 -0700534 SkGlyphCache* glyphCache = this->getGlyphCache();
benjaminwagnerd936f632016-02-23 10:44:31 -0800535 SkPaint::GlyphCacheProc glyphCacheProc = fFont.getGlyphCacheProc(true);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700536
cdaltoncdd46822015-12-08 10:48:31 -0800537 fTotalGlyphCount = fFont.countText(text, byteLength);
538 fInstanceData.reset(InstanceData::Alloc(GrPathRendering::kTranslate_PathTransformType,
539 fTotalGlyphCount));
cdalton8585dd22015-10-08 08:04:09 -0700540
kkinnunenc6cb56f2014-06-24 00:12:27 -0700541 const char* stop = text + byteLength;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700542
cdalton38e13ad2014-11-07 06:02:15 -0800543 SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
cdalton3bd909a2015-10-05 14:57:20 -0700544 SkTextAlignProc alignProc(fFont.getTextAlign());
cdalton02015e52015-10-05 15:28:20 -0700545 FallbackBlobBuilder fallback;
cdalton38e13ad2014-11-07 06:02:15 -0800546 while (text < stop) {
benjaminwagnerd936f632016-02-23 10:44:31 -0800547 const SkGlyph& glyph = glyphCacheProc(glyphCache, &text);
cdalton38e13ad2014-11-07 06:02:15 -0800548 if (glyph.fWidth) {
549 SkPoint tmsLoc;
550 tmsProc(pos, &tmsLoc);
551 SkPoint loc;
552 alignProc(tmsLoc, glyph, &loc);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700553
cdalton02015e52015-10-05 15:28:20 -0700554 this->appendGlyph(glyph, loc, &fallback);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700555 }
cdalton38e13ad2014-11-07 06:02:15 -0800556 pos += scalarsPerPosition;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700557 }
cdalton02015e52015-10-05 15:28:20 -0700558
cdaltoncdd46822015-12-08 10:48:31 -0800559 fFallbackTextBlob.reset(fallback.buildIfNeeded(&fFallbackGlyphCount));
kkinnunenc6cb56f2014-06-24 00:12:27 -0700560}
561
cdalton8585dd22015-10-08 08:04:09 -0700562GrPathRange* GrStencilAndCoverTextContext::TextRun::createGlyphs(GrContext* ctx) const {
563 GrPathRange* glyphs = static_cast<GrPathRange*>(
564 ctx->resourceProvider()->findAndRefResourceByUniqueKey(fGlyphPathsKey));
halcanary96fcdcc2015-08-27 07:41:13 -0700565 if (nullptr == glyphs) {
cdalton8585dd22015-10-08 08:04:09 -0700566 if (fUsingRawGlyphPaths) {
reeda9322c22016-04-12 06:47:05 -0700567 SkScalerContextEffects noeffects;
568 glyphs = ctx->resourceProvider()->createGlyphs(fFont.getTypeface(), noeffects,
bsalomon6663acf2016-05-10 09:14:17 -0700569 nullptr, fStyle);
cdalton8585dd22015-10-08 08:04:09 -0700570 } else {
571 SkGlyphCache* cache = this->getGlyphCache();
572 glyphs = ctx->resourceProvider()->createGlyphs(cache->getScalerContext()->getTypeface(),
reeda9322c22016-04-12 06:47:05 -0700573 cache->getScalerContext()->getEffects(),
cdalton8585dd22015-10-08 08:04:09 -0700574 &cache->getDescriptor(),
bsalomon6663acf2016-05-10 09:14:17 -0700575 fStyle);
cdalton8585dd22015-10-08 08:04:09 -0700576 }
577 ctx->resourceProvider()->assignUniqueKeyToResource(fGlyphPathsKey, glyphs);
cdalton855d83f2014-09-18 13:51:53 -0700578 }
cdalton8585dd22015-10-08 08:04:09 -0700579 return glyphs;
cdalton855d83f2014-09-18 13:51:53 -0700580}
581
cdalton3bd909a2015-10-05 14:57:20 -0700582inline void GrStencilAndCoverTextContext::TextRun::appendGlyph(const SkGlyph& glyph,
cdalton02015e52015-10-05 15:28:20 -0700583 const SkPoint& pos,
584 FallbackBlobBuilder* fallback) {
585 // Stick the glyphs we can't draw into the fallback text blob.
bsalomon1fcc01c2015-09-09 09:48:06 -0700586 if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
cdalton02015e52015-10-05 15:28:20 -0700587 if (!fallback->isInitialized()) {
588 fallback->init(fFont, fTextRatio);
589 }
590 fallback->appendGlyph(glyph.getGlyphID(), pos);
bsalomon1fcc01c2015-09-09 09:48:06 -0700591 } else {
cdaltoncdd46822015-12-08 10:48:31 -0800592 fInstanceData->append(glyph.getGlyphID(), fTextInverseRatio * pos.x(),
593 fTextInverseRatio * pos.y());
cdaltonb2808cd2014-07-25 14:13:57 -0700594 }
cdalton20b373c2014-12-01 08:38:55 -0800595}
596
cdalton8585dd22015-10-08 08:04:09 -0700597void GrStencilAndCoverTextContext::TextRun::draw(GrContext* ctx,
598 GrDrawContext* dc,
cdaltoncdd79072015-10-05 15:37:35 -0700599 GrPipelineBuilder* pipelineBuilder,
cdalton862cff32016-05-12 15:09:48 -0700600 const GrClip& clip,
cdaltoncdd79072015-10-05 15:37:35 -0700601 GrColor color,
cdalton3bd909a2015-10-05 14:57:20 -0700602 const SkMatrix& viewMatrix,
joshualitt2c89bc12016-02-11 05:42:30 -0800603 const SkSurfaceProps& props,
cdaltoncdd79072015-10-05 15:37:35 -0700604 SkScalar x, SkScalar y,
605 const SkIRect& clipBounds,
joshualitt8e84a1e2016-02-16 11:09:25 -0800606 GrAtlasTextContext* fallbackTextContext,
cdalton3bd909a2015-10-05 14:57:20 -0700607 const SkPaint& originalSkPaint) const {
cdaltoncdd46822015-12-08 10:48:31 -0800608 SkASSERT(fInstanceData);
robertphillips433625e2015-12-04 06:58:16 -0800609 SkASSERT(dc->accessRenderTarget()->isStencilBufferMultisampled() || !fFont.isAntiAlias());
robertphillipsea461502015-05-26 11:38:03 -0700610
cdaltoncdd46822015-12-08 10:48:31 -0800611 if (fInstanceData->count()) {
cdaltoncdd79072015-10-05 15:37:35 -0700612 pipelineBuilder->setState(GrPipelineBuilder::kHWAntialias_Flag, fFont.isAntiAlias());
joshualitt7b670db2015-07-09 13:25:02 -0700613
cdalton93a379b2016-05-11 13:58:08 -0700614 static constexpr GrUserStencilSettings kCoverPass(
615 GrUserStencilSettings::StaticInit<
616 0x0000,
617 GrUserStencilTest::kNotEqual, // Stencil pass accounts for clip.
618 0xffff,
619 GrUserStencilOp::kZero,
620 GrUserStencilOp::kKeep,
621 0xffff>()
622 );
joshualitt7b670db2015-07-09 13:25:02 -0700623
cdalton93a379b2016-05-11 13:58:08 -0700624 pipelineBuilder->setUserStencil(&kCoverPass);
joshualitt7b670db2015-07-09 13:25:02 -0700625
cdalton8585dd22015-10-08 08:04:09 -0700626 SkAutoTUnref<GrPathRange> glyphs(this->createGlyphs(ctx));
627 if (fLastDrawnGlyphsID != glyphs->getUniqueID()) {
628 // Either this is the first draw or the glyphs object was purged since last draw.
cdaltoncdd46822015-12-08 10:48:31 -0800629 glyphs->loadPathsIfNeeded(fInstanceData->indices(), fInstanceData->count());
cdalton8585dd22015-10-08 08:04:09 -0700630 fLastDrawnGlyphsID = glyphs->getUniqueID();
631 }
632
bsalomonbf074552015-11-23 14:25:19 -0800633 // Don't compute a bounding box. For dst copy texture, we'll opt instead for it to just copy
634 // the entire dst. Realistically this is a moot point, because any context that supports
635 // NV_path_rendering will also support NV_blend_equation_advanced.
636 // For clipping we'll just skip any optimizations based on the bounds. This does, however,
637 // hurt batching.
638 SkRect bounds = SkRect::MakeIWH(pipelineBuilder->getRenderTarget()->width(),
639 pipelineBuilder->getRenderTarget()->height());
640
cdalton193d9cf2016-05-12 11:52:02 -0700641 SkAutoTUnref<GrDrawBatch> batch(
cdaltoncdd46822015-12-08 10:48:31 -0800642 GrDrawPathRangeBatch::Create(viewMatrix, fTextRatio, fTextInverseRatio * x,
643 fTextInverseRatio * y, color,
644 GrPathRendering::kWinding_FillType, glyphs, fInstanceData,
cdalton8ff8d242015-12-08 10:20:32 -0800645 bounds));
646
cdalton862cff32016-05-12 15:09:48 -0700647 dc->drawBatch(pipelineBuilder, clip, batch);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700648 }
649
cdalton02015e52015-10-05 15:28:20 -0700650 if (fFallbackTextBlob) {
cdalton3bd909a2015-10-05 14:57:20 -0700651 SkPaint fallbackSkPaint(originalSkPaint);
bsalomon6663acf2016-05-10 09:14:17 -0700652 fStyle.strokeRec().applyToPaint(&fallbackSkPaint);
653 if (!fStyle.isSimpleFill()) {
654 fallbackSkPaint.setStrokeWidth(fStyle.strokeRec().getWidth() * fTextRatio);
cdalton20b373c2014-12-01 08:38:55 -0800655 }
cdalton20b373c2014-12-01 08:38:55 -0800656
cdalton862cff32016-05-12 15:09:48 -0700657 fallbackTextContext->drawTextBlob(ctx, dc, clip, fallbackSkPaint, viewMatrix, props,
658 fFallbackTextBlob, x, y, nullptr, clipBounds);
cdalton20b373c2014-12-01 08:38:55 -0800659 }
kkinnunenc6cb56f2014-06-24 00:12:27 -0700660}
cdalton02015e52015-10-05 15:28:20 -0700661
cdalton8585dd22015-10-08 08:04:09 -0700662SkGlyphCache* GrStencilAndCoverTextContext::TextRun::getGlyphCache() const {
663 if (!fDetachedGlyphCache) {
brianosmana1e8f8d2016-04-08 06:47:54 -0700664 fDetachedGlyphCache = fFont.detachCache(nullptr, SkPaint::kNone_ScalerContextFlags,
665 nullptr);
cdalton8585dd22015-10-08 08:04:09 -0700666 }
667 return fDetachedGlyphCache;
668}
669
670
671void GrStencilAndCoverTextContext::TextRun::releaseGlyphCache() const {
672 if (fDetachedGlyphCache) {
673 SkGlyphCache::AttachCache(fDetachedGlyphCache);
674 fDetachedGlyphCache = nullptr;
675 }
676}
677
678size_t GrStencilAndCoverTextContext::TextRun::computeSizeInCache() const {
cdaltoncdd46822015-12-08 10:48:31 -0800679 size_t size = sizeof(TextRun) + fGlyphPathsKey.size();
680 // The instance data always reserves enough space for every glyph.
681 size += (fTotalGlyphCount + fFallbackGlyphCount) * (sizeof(uint16_t) + 2 * sizeof(float));
682 if (fInstanceData) {
683 size += sizeof(InstanceData);
cdaltoncdd79072015-10-05 15:37:35 -0700684 }
685 if (fFallbackTextBlob) {
686 size += sizeof(SkTextBlob);
687 }
688 return size;
689}
690
cdalton02015e52015-10-05 15:28:20 -0700691////////////////////////////////////////////////////////////////////////////////////////////////////
692
693void GrStencilAndCoverTextContext::FallbackBlobBuilder::init(const SkPaint& font,
694 SkScalar textRatio) {
695 SkASSERT(!this->isInitialized());
696 fBuilder.reset(new SkTextBlobBuilder);
697 fFont = font;
698 fFont.setTextAlign(SkPaint::kLeft_Align); // The glyph positions will already account for align.
699 fFont.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
700 // No need for subpixel positioning with bitmap glyphs. TODO: revisit if non-bitmap color glyphs
701 // show up and https://code.google.com/p/skia/issues/detail?id=4408 gets resolved.
702 fFont.setSubpixelText(false);
703 fFont.setTextSize(fFont.getTextSize() * textRatio);
704 fBuffIdx = 0;
705}
706
707void GrStencilAndCoverTextContext::FallbackBlobBuilder::appendGlyph(uint16_t glyphId,
708 const SkPoint& pos) {
709 SkASSERT(this->isInitialized());
710 if (fBuffIdx >= kWriteBufferSize) {
711 this->flush();
712 }
713 fGlyphIds[fBuffIdx] = glyphId;
714 fPositions[fBuffIdx] = pos;
715 fBuffIdx++;
cdaltoncdd46822015-12-08 10:48:31 -0800716 fCount++;
cdalton02015e52015-10-05 15:28:20 -0700717}
718
719void GrStencilAndCoverTextContext::FallbackBlobBuilder::flush() {
720 SkASSERT(this->isInitialized());
721 SkASSERT(fBuffIdx <= kWriteBufferSize);
722 if (!fBuffIdx) {
723 return;
724 }
725 // This will automatically merge with previous runs since we use the same font.
726 const SkTextBlobBuilder::RunBuffer& buff = fBuilder->allocRunPos(fFont, fBuffIdx);
727 memcpy(buff.glyphs, fGlyphIds, fBuffIdx * sizeof(uint16_t));
728 memcpy(buff.pos, fPositions[0].asScalars(), fBuffIdx * 2 * sizeof(SkScalar));
729 fBuffIdx = 0;
730}
731
cdaltoncdd46822015-12-08 10:48:31 -0800732const SkTextBlob* GrStencilAndCoverTextContext::FallbackBlobBuilder::buildIfNeeded(int *count) {
733 *count = fCount;
734 if (fCount) {
735 this->flush();
736 return fBuilder->build();
cdalton02015e52015-10-05 15:28:20 -0700737 }
cdaltoncdd46822015-12-08 10:48:31 -0800738 return nullptr;
cdalton02015e52015-10-05 15:28:20 -0700739}