blob: 1e2347741f5f2463feb267bf7299da7b18c9707a [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"
robertphillipsda152eb2016-07-21 07:58:41 -070014#include "GrPipelineBuilder.h"
bsalomond309e7a2015-04-30 14:18:54 -070015#include "GrResourceProvider.h"
joshualitte55750e2016-02-10 12:52:21 -080016#include "GrTextUtils.h"
jvanverthaab626c2014-10-16 08:04:39 -070017#include "SkAutoKern.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070018#include "SkDraw.h"
19#include "SkDrawProcs.h"
20#include "SkGlyphCache.h"
cdaltoncdd79072015-10-05 15:37:35 -070021#include "SkGrPriv.h"
joshualitte55750e2016-02-10 12:52:21 -080022#include "SkDrawFilter.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070023#include "SkPath.h"
halcanary33779752015-10-27 14:01:05 -070024#include "SkTextBlobRunIterator.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070025#include "SkTextMapStateProc.h"
cdalton855d83f2014-09-18 13:51:53 -070026#include "SkTextFormatParams.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070027
bsalomon1fcc01c2015-09-09 09:48:06 -070028#include "batches/GrDrawPathBatch.h"
29
cdaltoncdd79072015-10-05 15:37:35 -070030template<typename Key, typename Val> static void delete_hash_map_entry(const Key&, Val* val) {
31 SkASSERT(*val);
32 delete *val;
33}
34
35template<typename T> static void delete_hash_table_entry(T* val) {
36 SkASSERT(*val);
37 delete *val;
38}
39
joshualitt27004b72016-02-11 12:00:33 -080040GrStencilAndCoverTextContext::GrStencilAndCoverTextContext()
41 : fFallbackTextContext(nullptr)
joshualitte55750e2016-02-10 12:52:21 -080042 , fCacheSize(0) {
kkinnunenc6cb56f2014-06-24 00:12:27 -070043}
44
joshualitt6e8cd962015-03-20 10:30:14 -070045GrStencilAndCoverTextContext*
joshualitt27004b72016-02-11 12:00:33 -080046GrStencilAndCoverTextContext::Create() {
47 GrStencilAndCoverTextContext* textContext = new GrStencilAndCoverTextContext();
48 textContext->fFallbackTextContext = GrAtlasTextContext::Create();
jvanverth8c27a182014-10-14 08:45:50 -070049
50 return textContext;
51}
52
kkinnunenc6cb56f2014-06-24 00:12:27 -070053GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() {
joshualitte55750e2016-02-10 12:52:21 -080054 delete fFallbackTextContext;
cdaltoncdd79072015-10-05 15:37:35 -070055 fBlobIdCache.foreach(delete_hash_map_entry<uint32_t, TextBlob*>);
56 fBlobKeyCache.foreach(delete_hash_table_entry<TextBlob*>);
kkinnunenc6cb56f2014-06-24 00:12:27 -070057}
58
cdaltoncdd79072015-10-05 15:37:35 -070059bool GrStencilAndCoverTextContext::internalCanDraw(const SkPaint& skPaint) {
cdaltone68f7362015-03-25 14:02:37 -070060 if (skPaint.getRasterizer()) {
jvanverth0fedb192014-10-08 09:07:27 -070061 return false;
62 }
cdaltone68f7362015-03-25 14:02:37 -070063 if (skPaint.getMaskFilter()) {
jvanverth0fedb192014-10-08 09:07:27 -070064 return false;
65 }
kkinnunen50b58e62015-05-18 23:02:07 -070066 if (SkPathEffect* pe = skPaint.getPathEffect()) {
halcanary96fcdcc2015-08-27 07:41:13 -070067 if (pe->asADash(nullptr) != SkPathEffect::kDash_DashType) {
kkinnunen50b58e62015-05-18 23:02:07 -070068 return false;
69 }
jvanverth0fedb192014-10-08 09:07:27 -070070 }
cdalton7d5c9502015-10-03 13:28:35 -070071 // No hairlines. They would require new paths with customized strokes for every new draw matrix.
72 return SkPaint::kStroke_Style != skPaint.getStyle() || 0 != skPaint.getStrokeWidth();
jvanverth0fedb192014-10-08 09:07:27 -070073}
74
joshualitt27004b72016-02-11 12:00:33 -080075void GrStencilAndCoverTextContext::drawText(GrContext* context, GrDrawContext* dc,
joshualitte55750e2016-02-10 12:52:21 -080076 const GrClip& clip, const GrPaint& paint,
77 const SkPaint& skPaint, const SkMatrix& viewMatrix,
joshualitt2c89bc12016-02-11 05:42:30 -080078 const SkSurfaceProps& props,
joshualitte55750e2016-02-10 12:52:21 -080079 const char text[], size_t byteLength,
80 SkScalar x, SkScalar y, const SkIRect& clipBounds) {
joshualitt27004b72016-02-11 12:00:33 -080081 if (context->abandoned()) {
joshualitte55750e2016-02-10 12:52:21 -080082 return;
83 } else if (this->canDraw(skPaint, viewMatrix)) {
kkinnunen68c63b32016-03-04 00:12:33 -080084 if (skPaint.getTextSize() > 0) {
85 TextRun run(skPaint);
kkinnunen68c63b32016-03-04 00:12:33 -080086 run.setText(text, byteLength, x, y);
robertphillipsecbc15d2016-07-19 14:04:39 -070087 run.draw(context, dc, paint, clip, 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);
kkinnunen68c63b32016-03-04 00:12:33 -0800120 run.setPosText(text, byteLength, pos, scalarsPerPosition, offset);
robertphillipsecbc15d2016-07-19 14:04:39 -0700121 run.draw(context, dc, paint, clip, viewMatrix, props, 0, 0,
kkinnunen68c63b32016-03-04 00:12:33 -0800122 clipBounds, fFallbackTextContext, skPaint);
123 }
joshualitte55750e2016-02-10 12:52:21 -0800124 return;
joshualitt27004b72016-02-11 12:00:33 -0800125 } else if (fFallbackTextContext->canDraw(skPaint, viewMatrix, props,
126 *context->caps()->shaderCaps())) {
127 fFallbackTextContext->drawPosText(context, dc, clip, paint, skPaint, viewMatrix, props,
joshualitte55750e2016-02-10 12:52:21 -0800128 text, byteLength, pos,
129 scalarsPerPosition, offset, clipBounds);
130 return;
131 }
132
133 // fall back to drawing as a path
joshualitt27004b72016-02-11 12:00:33 -0800134 GrTextUtils::DrawPosTextAsPath(context, dc, props, clip, skPaint, viewMatrix, text,
joshualitte55750e2016-02-10 12:52:21 -0800135 byteLength, pos, scalarsPerPosition, offset, clipBounds);
136}
137
joshualitt27004b72016-02-11 12:00:33 -0800138void GrStencilAndCoverTextContext::uncachedDrawTextBlob(GrContext* context,
139 GrDrawContext* dc,
joshualitte55750e2016-02-10 12:52:21 -0800140 const GrClip& clip, const SkPaint& skPaint,
141 const SkMatrix& viewMatrix,
joshualitt2c89bc12016-02-11 05:42:30 -0800142 const SkSurfaceProps& props,
joshualitte55750e2016-02-10 12:52:21 -0800143 const SkTextBlob* blob,
144 SkScalar x, SkScalar y,
145 SkDrawFilter* drawFilter,
146 const SkIRect& clipBounds) {
147 SkPaint runPaint = skPaint;
148
149 SkTextBlobRunIterator it(blob);
150 for (;!it.done(); it.next()) {
151 size_t textLen = it.glyphCount() * sizeof(uint16_t);
152 const SkPoint& offset = it.offset();
153
154 // applyFontToPaint() always overwrites the exact same attributes,
155 // so it is safe to not re-seed the paint for this reason.
156 it.applyFontToPaint(&runPaint);
157
158 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
159 // A false return from filter() means we should abort the current draw.
160 runPaint = skPaint;
161 continue;
162 }
163
joshualitt8e84a1e2016-02-16 11:09:25 -0800164 runPaint.setFlags(GrTextUtils::FilterTextFlags(props, runPaint));
joshualitte55750e2016-02-10 12:52:21 -0800165
166 GrPaint grPaint;
brianosmanb461d342016-04-13 13:10:14 -0700167 if (!SkPaintToGrPaint(context, runPaint, viewMatrix, dc->isGammaCorrect(), &grPaint)) {
joshualitte55750e2016-02-10 12:52:21 -0800168 return;
169 }
170
171 switch (it.positioning()) {
172 case SkTextBlob::kDefault_Positioning:
joshualitt27004b72016-02-11 12:00:33 -0800173 this->drawText(context, dc, clip, grPaint, runPaint, viewMatrix, props,
joshualitt2c89bc12016-02-11 05:42:30 -0800174 (const char *)it.glyphs(),
joshualitte55750e2016-02-10 12:52:21 -0800175 textLen, x + offset.x(), y + offset.y(), clipBounds);
176 break;
177 case SkTextBlob::kHorizontal_Positioning:
joshualitt27004b72016-02-11 12:00:33 -0800178 this->drawPosText(context, dc, clip, grPaint, runPaint, viewMatrix, props,
joshualitt2c89bc12016-02-11 05:42:30 -0800179 (const char*)it.glyphs(),
joshualitte55750e2016-02-10 12:52:21 -0800180 textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()),
181 clipBounds);
182 break;
183 case SkTextBlob::kFull_Positioning:
joshualitt27004b72016-02-11 12:00:33 -0800184 this->drawPosText(context, dc, clip, grPaint, runPaint, viewMatrix, props,
joshualitt2c89bc12016-02-11 05:42:30 -0800185 (const char*)it.glyphs(),
joshualitte55750e2016-02-10 12:52:21 -0800186 textLen, it.pos(), 2, SkPoint::Make(x, y), clipBounds);
187 break;
188 }
189
190 if (drawFilter) {
191 // A draw filter may change the paint arbitrarily, so we must re-seed in this case.
192 runPaint = skPaint;
193 }
194 }
cdaltoncdd79072015-10-05 15:37:35 -0700195}
196
joshualitt27004b72016-02-11 12:00:33 -0800197void GrStencilAndCoverTextContext::drawTextBlob(GrContext* context, GrDrawContext* dc,
cdaltoncdd79072015-10-05 15:37:35 -0700198 const GrClip& clip, const SkPaint& skPaint,
199 const SkMatrix& viewMatrix,
joshualitt2c89bc12016-02-11 05:42:30 -0800200 const SkSurfaceProps& props,
cdaltoncdd79072015-10-05 15:37:35 -0700201 const SkTextBlob* skBlob, SkScalar x, SkScalar y,
202 SkDrawFilter* drawFilter,
203 const SkIRect& clipBounds) {
joshualitt27004b72016-02-11 12:00:33 -0800204 if (context->abandoned()) {
joshualitte55750e2016-02-10 12:52:21 -0800205 return;
206 }
207
cdaltoncdd79072015-10-05 15:37:35 -0700208 if (!this->internalCanDraw(skPaint)) {
joshualitt27004b72016-02-11 12:00:33 -0800209 fFallbackTextContext->drawTextBlob(context, dc, clip, skPaint, viewMatrix, props, skBlob,
210 x, y, drawFilter, clipBounds);
cdaltoncdd79072015-10-05 15:37:35 -0700211 return;
212 }
213
214 if (drawFilter || skPaint.getPathEffect()) {
215 // This draw can't be cached.
joshualitt27004b72016-02-11 12:00:33 -0800216 this->uncachedDrawTextBlob(context, dc, clip, skPaint, viewMatrix, props, skBlob, x, y,
217 drawFilter, clipBounds);
cdaltoncdd79072015-10-05 15:37:35 -0700218 return;
219 }
220
221 GrPaint paint;
brianosmanb461d342016-04-13 13:10:14 -0700222 if (!SkPaintToGrPaint(context, skPaint, viewMatrix, dc->isGammaCorrect(), &paint)) {
cdaltoncdd79072015-10-05 15:37:35 -0700223 return;
224 }
225
226 const TextBlob& blob = this->findOrCreateTextBlob(skBlob, skPaint);
cdaltoncdd79072015-10-05 15:37:35 -0700227
228 TextBlob::Iter iter(blob);
229 for (TextRun* run = iter.get(); run; run = iter.next()) {
robertphillipsecbc15d2016-07-19 14:04:39 -0700230 // The run's "font" overrides the anti-aliasing of the passed in paint!
231 paint.setAntiAlias(run->isAntiAlias());
232 run->draw(context, dc, paint, clip, 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)
robertphillipsecbc15d2016-07-19 14:04:39 -0700368 : fStyle(fontAndStroke)
369 , fFont(fontAndStroke)
370 , fTotalGlyphCount(0)
371 , fFallbackGlyphCount(0)
372 , 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();
robertphillipse34f17d2016-07-19 07:59:22 -0700475 SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(fFont.getTextEncoding(),
476 fFont.isDevKernText(),
477 true);
jvanverthaab626c2014-10-16 08:04:39 -0700478
cdaltoncdd46822015-12-08 10:48:31 -0800479 fTotalGlyphCount = fFont.countText(text, byteLength);
480 fInstanceData.reset(InstanceData::Alloc(GrPathRendering::kTranslate_PathTransformType,
481 fTotalGlyphCount));
cdalton8585dd22015-10-08 08:04:09 -0700482
jvanverthaab626c2014-10-16 08:04:39 -0700483 const char* stop = text + byteLength;
484
485 // Measure first if needed.
cdalton3bd909a2015-10-05 14:57:20 -0700486 if (fFont.getTextAlign() != SkPaint::kLeft_Align) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700487 SkScalar stopX = 0;
488 SkScalar stopY = 0;
jvanverthaab626c2014-10-16 08:04:39 -0700489
490 const char* textPtr = text;
491 while (textPtr < stop) {
492 // We don't need x, y here, since all subpixel variants will have the
493 // same advance.
benjaminwagnerd936f632016-02-23 10:44:31 -0800494 const SkGlyph& glyph = glyphCacheProc(glyphCache, &textPtr);
jvanverthaab626c2014-10-16 08:04:39 -0700495
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700496 stopX += SkFloatToScalar(glyph.fAdvanceX);
497 stopY += SkFloatToScalar(glyph.fAdvanceY);
jvanverthaab626c2014-10-16 08:04:39 -0700498 }
499 SkASSERT(textPtr == stop);
500
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700501 SkScalar alignX = stopX * fTextRatio;
502 SkScalar alignY = stopY * fTextRatio;
jvanverthaab626c2014-10-16 08:04:39 -0700503
cdalton3bd909a2015-10-05 14:57:20 -0700504 if (fFont.getTextAlign() == SkPaint::kCenter_Align) {
jvanverthaab626c2014-10-16 08:04:39 -0700505 alignX = SkScalarHalf(alignX);
506 alignY = SkScalarHalf(alignY);
507 }
508
509 x -= alignX;
510 y -= alignY;
511 }
512
513 SkAutoKern autokern;
514
cdalton02015e52015-10-05 15:28:20 -0700515 FallbackBlobBuilder fallback;
jvanverthaab626c2014-10-16 08:04:39 -0700516 while (text < stop) {
benjaminwagnerd936f632016-02-23 10:44:31 -0800517 const SkGlyph& glyph = glyphCacheProc(glyphCache, &text);
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700518 x += autokern.adjust(glyph) * fTextRatio;
jvanverthaab626c2014-10-16 08:04:39 -0700519 if (glyph.fWidth) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700520 this->appendGlyph(glyph, SkPoint::Make(x, y), &fallback);
jvanverthaab626c2014-10-16 08:04:39 -0700521 }
522
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700523 x += SkFloatToScalar(glyph.fAdvanceX) * fTextRatio;
524 y += SkFloatToScalar(glyph.fAdvanceY) * fTextRatio;
jvanverthaab626c2014-10-16 08:04:39 -0700525 }
cdalton02015e52015-10-05 15:28:20 -0700526
cdaltoncdd46822015-12-08 10:48:31 -0800527 fFallbackTextBlob.reset(fallback.buildIfNeeded(&fFallbackGlyphCount));
jvanverthaab626c2014-10-16 08:04:39 -0700528}
529
cdalton3bd909a2015-10-05 14:57:20 -0700530void GrStencilAndCoverTextContext::TextRun::setPosText(const char text[], size_t byteLength,
531 const SkScalar pos[], int scalarsPerPosition,
cdalton8585dd22015-10-08 08:04:09 -0700532 const SkPoint& offset) {
halcanary96fcdcc2015-08-27 07:41:13 -0700533 SkASSERT(byteLength == 0 || text != nullptr);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700534 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
535
cdalton8585dd22015-10-08 08:04:09 -0700536 SkGlyphCache* glyphCache = this->getGlyphCache();
robertphillipse34f17d2016-07-19 07:59:22 -0700537 SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(fFont.getTextEncoding(),
538 fFont.isDevKernText(),
539 true);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700540
cdaltoncdd46822015-12-08 10:48:31 -0800541 fTotalGlyphCount = fFont.countText(text, byteLength);
542 fInstanceData.reset(InstanceData::Alloc(GrPathRendering::kTranslate_PathTransformType,
543 fTotalGlyphCount));
cdalton8585dd22015-10-08 08:04:09 -0700544
kkinnunenc6cb56f2014-06-24 00:12:27 -0700545 const char* stop = text + byteLength;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700546
cdalton38e13ad2014-11-07 06:02:15 -0800547 SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
cdalton3bd909a2015-10-05 14:57:20 -0700548 SkTextAlignProc alignProc(fFont.getTextAlign());
cdalton02015e52015-10-05 15:28:20 -0700549 FallbackBlobBuilder fallback;
cdalton38e13ad2014-11-07 06:02:15 -0800550 while (text < stop) {
benjaminwagnerd936f632016-02-23 10:44:31 -0800551 const SkGlyph& glyph = glyphCacheProc(glyphCache, &text);
cdalton38e13ad2014-11-07 06:02:15 -0800552 if (glyph.fWidth) {
553 SkPoint tmsLoc;
554 tmsProc(pos, &tmsLoc);
555 SkPoint loc;
556 alignProc(tmsLoc, glyph, &loc);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700557
cdalton02015e52015-10-05 15:28:20 -0700558 this->appendGlyph(glyph, loc, &fallback);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700559 }
cdalton38e13ad2014-11-07 06:02:15 -0800560 pos += scalarsPerPosition;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700561 }
cdalton02015e52015-10-05 15:28:20 -0700562
cdaltoncdd46822015-12-08 10:48:31 -0800563 fFallbackTextBlob.reset(fallback.buildIfNeeded(&fFallbackGlyphCount));
kkinnunenc6cb56f2014-06-24 00:12:27 -0700564}
565
cdalton8585dd22015-10-08 08:04:09 -0700566GrPathRange* GrStencilAndCoverTextContext::TextRun::createGlyphs(GrContext* ctx) const {
567 GrPathRange* glyphs = static_cast<GrPathRange*>(
568 ctx->resourceProvider()->findAndRefResourceByUniqueKey(fGlyphPathsKey));
halcanary96fcdcc2015-08-27 07:41:13 -0700569 if (nullptr == glyphs) {
cdalton8585dd22015-10-08 08:04:09 -0700570 if (fUsingRawGlyphPaths) {
reeda9322c22016-04-12 06:47:05 -0700571 SkScalerContextEffects noeffects;
572 glyphs = ctx->resourceProvider()->createGlyphs(fFont.getTypeface(), noeffects,
bsalomon6663acf2016-05-10 09:14:17 -0700573 nullptr, fStyle);
cdalton8585dd22015-10-08 08:04:09 -0700574 } else {
575 SkGlyphCache* cache = this->getGlyphCache();
576 glyphs = ctx->resourceProvider()->createGlyphs(cache->getScalerContext()->getTypeface(),
reeda9322c22016-04-12 06:47:05 -0700577 cache->getScalerContext()->getEffects(),
cdalton8585dd22015-10-08 08:04:09 -0700578 &cache->getDescriptor(),
bsalomon6663acf2016-05-10 09:14:17 -0700579 fStyle);
cdalton8585dd22015-10-08 08:04:09 -0700580 }
581 ctx->resourceProvider()->assignUniqueKeyToResource(fGlyphPathsKey, glyphs);
cdalton855d83f2014-09-18 13:51:53 -0700582 }
cdalton8585dd22015-10-08 08:04:09 -0700583 return glyphs;
cdalton855d83f2014-09-18 13:51:53 -0700584}
585
cdalton3bd909a2015-10-05 14:57:20 -0700586inline void GrStencilAndCoverTextContext::TextRun::appendGlyph(const SkGlyph& glyph,
cdalton02015e52015-10-05 15:28:20 -0700587 const SkPoint& pos,
588 FallbackBlobBuilder* fallback) {
589 // Stick the glyphs we can't draw into the fallback text blob.
bsalomon1fcc01c2015-09-09 09:48:06 -0700590 if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
cdalton02015e52015-10-05 15:28:20 -0700591 if (!fallback->isInitialized()) {
592 fallback->init(fFont, fTextRatio);
593 }
594 fallback->appendGlyph(glyph.getGlyphID(), pos);
bsalomon1fcc01c2015-09-09 09:48:06 -0700595 } else {
cdaltoncdd46822015-12-08 10:48:31 -0800596 fInstanceData->append(glyph.getGlyphID(), fTextInverseRatio * pos.x(),
597 fTextInverseRatio * pos.y());
cdaltonb2808cd2014-07-25 14:13:57 -0700598 }
cdalton20b373c2014-12-01 08:38:55 -0800599}
600
cdalton8585dd22015-10-08 08:04:09 -0700601void GrStencilAndCoverTextContext::TextRun::draw(GrContext* ctx,
robertphillips55fdccc2016-06-06 06:16:20 -0700602 GrDrawContext* drawContext,
robertphillips640789d2016-07-18 14:56:06 -0700603 const GrPaint& grPaint,
cdalton862cff32016-05-12 15:09:48 -0700604 const GrClip& clip,
cdalton3bd909a2015-10-05 14:57:20 -0700605 const SkMatrix& viewMatrix,
joshualitt2c89bc12016-02-11 05:42:30 -0800606 const SkSurfaceProps& props,
cdaltoncdd79072015-10-05 15:37:35 -0700607 SkScalar x, SkScalar y,
608 const SkIRect& clipBounds,
joshualitt8e84a1e2016-02-16 11:09:25 -0800609 GrAtlasTextContext* fallbackTextContext,
cdalton3bd909a2015-10-05 14:57:20 -0700610 const SkPaint& originalSkPaint) const {
cdaltoncdd46822015-12-08 10:48:31 -0800611 SkASSERT(fInstanceData);
robertphillipsecbc15d2016-07-19 14:04:39 -0700612 SkASSERT(drawContext->isStencilBufferMultisampled() || !grPaint.isAntiAlias());
robertphillipsea461502015-05-26 11:38:03 -0700613
cdaltoncdd46822015-12-08 10:48:31 -0800614 if (fInstanceData->count()) {
cdalton93a379b2016-05-11 13:58:08 -0700615 static constexpr GrUserStencilSettings kCoverPass(
616 GrUserStencilSettings::StaticInit<
617 0x0000,
618 GrUserStencilTest::kNotEqual, // Stencil pass accounts for clip.
619 0xffff,
620 GrUserStencilOp::kZero,
621 GrUserStencilOp::kKeep,
622 0xffff>()
623 );
joshualitt7b670db2015-07-09 13:25:02 -0700624
cdalton8585dd22015-10-08 08:04:09 -0700625 SkAutoTUnref<GrPathRange> glyphs(this->createGlyphs(ctx));
626 if (fLastDrawnGlyphsID != glyphs->getUniqueID()) {
627 // Either this is the first draw or the glyphs object was purged since last draw.
cdaltoncdd46822015-12-08 10:48:31 -0800628 glyphs->loadPathsIfNeeded(fInstanceData->indices(), fInstanceData->count());
cdalton8585dd22015-10-08 08:04:09 -0700629 fLastDrawnGlyphsID = glyphs->getUniqueID();
630 }
631
bsalomonbf074552015-11-23 14:25:19 -0800632 // Don't compute a bounding box. For dst copy texture, we'll opt instead for it to just copy
633 // the entire dst. Realistically this is a moot point, because any context that supports
634 // NV_path_rendering will also support NV_blend_equation_advanced.
635 // For clipping we'll just skip any optimizations based on the bounds. This does, however,
636 // hurt batching.
robertphillips55fdccc2016-06-06 06:16:20 -0700637 const SkRect bounds = SkRect::MakeIWH(drawContext->width(), drawContext->height());
bsalomonbf074552015-11-23 14:25:19 -0800638
cdalton193d9cf2016-05-12 11:52:02 -0700639 SkAutoTUnref<GrDrawBatch> batch(
cdaltoncdd46822015-12-08 10:48:31 -0800640 GrDrawPathRangeBatch::Create(viewMatrix, fTextRatio, fTextInverseRatio * x,
robertphillipsecbc15d2016-07-19 14:04:39 -0700641 fTextInverseRatio * y, grPaint.getColor(),
cdaltoncdd46822015-12-08 10:48:31 -0800642 GrPathRendering::kWinding_FillType, glyphs, fInstanceData,
cdalton8ff8d242015-12-08 10:20:32 -0800643 bounds));
644
robertphillips640789d2016-07-18 14:56:06 -0700645 GrPipelineBuilder pipelineBuilder(grPaint);
robertphillipsecbc15d2016-07-19 14:04:39 -0700646 pipelineBuilder.setState(GrPipelineBuilder::kHWAntialias_Flag, grPaint.isAntiAlias());
robertphillips640789d2016-07-18 14:56:06 -0700647 pipelineBuilder.setUserStencil(&kCoverPass);
648
649 drawContext->drawBatch(pipelineBuilder, clip, batch);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700650 }
651
cdalton02015e52015-10-05 15:28:20 -0700652 if (fFallbackTextBlob) {
cdalton3bd909a2015-10-05 14:57:20 -0700653 SkPaint fallbackSkPaint(originalSkPaint);
bsalomon6663acf2016-05-10 09:14:17 -0700654 fStyle.strokeRec().applyToPaint(&fallbackSkPaint);
655 if (!fStyle.isSimpleFill()) {
656 fallbackSkPaint.setStrokeWidth(fStyle.strokeRec().getWidth() * fTextRatio);
cdalton20b373c2014-12-01 08:38:55 -0800657 }
cdalton20b373c2014-12-01 08:38:55 -0800658
robertphillips55fdccc2016-06-06 06:16:20 -0700659 fallbackTextContext->drawTextBlob(ctx, drawContext, clip, fallbackSkPaint, viewMatrix,
660 props, fFallbackTextBlob, x, y, nullptr, clipBounds);
cdalton20b373c2014-12-01 08:38:55 -0800661 }
kkinnunenc6cb56f2014-06-24 00:12:27 -0700662}
cdalton02015e52015-10-05 15:28:20 -0700663
cdalton8585dd22015-10-08 08:04:09 -0700664SkGlyphCache* GrStencilAndCoverTextContext::TextRun::getGlyphCache() const {
665 if (!fDetachedGlyphCache) {
brianosmana1e8f8d2016-04-08 06:47:54 -0700666 fDetachedGlyphCache = fFont.detachCache(nullptr, SkPaint::kNone_ScalerContextFlags,
667 nullptr);
cdalton8585dd22015-10-08 08:04:09 -0700668 }
669 return fDetachedGlyphCache;
670}
671
672
673void GrStencilAndCoverTextContext::TextRun::releaseGlyphCache() const {
674 if (fDetachedGlyphCache) {
675 SkGlyphCache::AttachCache(fDetachedGlyphCache);
676 fDetachedGlyphCache = nullptr;
677 }
678}
679
680size_t GrStencilAndCoverTextContext::TextRun::computeSizeInCache() const {
cdaltoncdd46822015-12-08 10:48:31 -0800681 size_t size = sizeof(TextRun) + fGlyphPathsKey.size();
682 // The instance data always reserves enough space for every glyph.
683 size += (fTotalGlyphCount + fFallbackGlyphCount) * (sizeof(uint16_t) + 2 * sizeof(float));
684 if (fInstanceData) {
685 size += sizeof(InstanceData);
cdaltoncdd79072015-10-05 15:37:35 -0700686 }
687 if (fFallbackTextBlob) {
688 size += sizeof(SkTextBlob);
689 }
690 return size;
691}
692
cdalton02015e52015-10-05 15:28:20 -0700693////////////////////////////////////////////////////////////////////////////////////////////////////
694
695void GrStencilAndCoverTextContext::FallbackBlobBuilder::init(const SkPaint& font,
696 SkScalar textRatio) {
697 SkASSERT(!this->isInitialized());
698 fBuilder.reset(new SkTextBlobBuilder);
699 fFont = font;
700 fFont.setTextAlign(SkPaint::kLeft_Align); // The glyph positions will already account for align.
701 fFont.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
702 // No need for subpixel positioning with bitmap glyphs. TODO: revisit if non-bitmap color glyphs
703 // show up and https://code.google.com/p/skia/issues/detail?id=4408 gets resolved.
704 fFont.setSubpixelText(false);
705 fFont.setTextSize(fFont.getTextSize() * textRatio);
706 fBuffIdx = 0;
707}
708
709void GrStencilAndCoverTextContext::FallbackBlobBuilder::appendGlyph(uint16_t glyphId,
710 const SkPoint& pos) {
711 SkASSERT(this->isInitialized());
712 if (fBuffIdx >= kWriteBufferSize) {
713 this->flush();
714 }
715 fGlyphIds[fBuffIdx] = glyphId;
716 fPositions[fBuffIdx] = pos;
717 fBuffIdx++;
cdaltoncdd46822015-12-08 10:48:31 -0800718 fCount++;
cdalton02015e52015-10-05 15:28:20 -0700719}
720
721void GrStencilAndCoverTextContext::FallbackBlobBuilder::flush() {
722 SkASSERT(this->isInitialized());
723 SkASSERT(fBuffIdx <= kWriteBufferSize);
724 if (!fBuffIdx) {
725 return;
726 }
727 // This will automatically merge with previous runs since we use the same font.
728 const SkTextBlobBuilder::RunBuffer& buff = fBuilder->allocRunPos(fFont, fBuffIdx);
729 memcpy(buff.glyphs, fGlyphIds, fBuffIdx * sizeof(uint16_t));
730 memcpy(buff.pos, fPositions[0].asScalars(), fBuffIdx * 2 * sizeof(SkScalar));
731 fBuffIdx = 0;
732}
733
cdaltoncdd46822015-12-08 10:48:31 -0800734const SkTextBlob* GrStencilAndCoverTextContext::FallbackBlobBuilder::buildIfNeeded(int *count) {
735 *count = fCount;
736 if (fCount) {
737 this->flush();
738 return fBuilder->build();
cdalton02015e52015-10-05 15:28:20 -0700739 }
cdaltoncdd46822015-12-08 10:48:31 -0800740 return nullptr;
cdalton02015e52015-10-05 15:28:20 -0700741}