blob: 0ae4023b1cce6ebe2f7d88dfe1fbb53ee00712cb [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"
kkinnunenc6cb56f2014-06-24 00:12:27 -070011#include "GrPath.h"
cdaltonb85a0aa2014-07-21 15:32:44 -070012#include "GrPathRange.h"
bsalomonbb243832016-07-22 07:10:19 -070013#include "GrPipelineBuilder.h"
Brian Salomon6f1d36c2017-01-13 12:02:17 -050014#include "GrRenderTargetContext.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"
Brian Salomon6f1d36c2017-01-13 12:02:17 -050019#include "SkDrawFilter.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070020#include "SkDrawProcs.h"
21#include "SkGlyphCache.h"
Brian Osman3b655982017-03-07 16:58:08 -050022#include "SkGr.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070023#include "SkPath.h"
halcanary33779752015-10-27 14:01:05 -070024#include "SkTextBlobRunIterator.h"
cdalton855d83f2014-09-18 13:51:53 -070025#include "SkTextFormatParams.h"
Brian Salomon6f1d36c2017-01-13 12:02:17 -050026#include "SkTextMapStateProc.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070027
Brian Salomon89527432016-12-16 09:52:16 -050028#include "ops/GrDrawPathOp.h"
bsalomon1fcc01c2015-09-09 09:48:06 -070029
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
brianosman86e76262016-08-11 12:17:31 -070040GrStencilAndCoverTextContext::GrStencilAndCoverTextContext(GrAtlasTextContext* fallbackTextContext)
41 : fFallbackTextContext(fallbackTextContext)
joshualitte55750e2016-02-10 12:52:21 -080042 , fCacheSize(0) {
kkinnunenc6cb56f2014-06-24 00:12:27 -070043}
44
joshualitt6e8cd962015-03-20 10:30:14 -070045GrStencilAndCoverTextContext*
brianosman86e76262016-08-11 12:17:31 -070046GrStencilAndCoverTextContext::Create(GrAtlasTextContext* fallbackTextContext) {
47 return new GrStencilAndCoverTextContext(fallbackTextContext);;
jvanverth8c27a182014-10-14 08:45:50 -070048}
49
kkinnunenc6cb56f2014-06-24 00:12:27 -070050GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() {
cdaltoncdd79072015-10-05 15:37:35 -070051 fBlobIdCache.foreach(delete_hash_map_entry<uint32_t, TextBlob*>);
52 fBlobKeyCache.foreach(delete_hash_table_entry<TextBlob*>);
kkinnunenc6cb56f2014-06-24 00:12:27 -070053}
54
cdaltoncdd79072015-10-05 15:37:35 -070055bool GrStencilAndCoverTextContext::internalCanDraw(const SkPaint& skPaint) {
cdaltone68f7362015-03-25 14:02:37 -070056 if (skPaint.getRasterizer()) {
jvanverth0fedb192014-10-08 09:07:27 -070057 return false;
58 }
cdaltone68f7362015-03-25 14:02:37 -070059 if (skPaint.getMaskFilter()) {
jvanverth0fedb192014-10-08 09:07:27 -070060 return false;
61 }
kkinnunen50b58e62015-05-18 23:02:07 -070062 if (SkPathEffect* pe = skPaint.getPathEffect()) {
halcanary96fcdcc2015-08-27 07:41:13 -070063 if (pe->asADash(nullptr) != SkPathEffect::kDash_DashType) {
kkinnunen50b58e62015-05-18 23:02:07 -070064 return false;
65 }
jvanverth0fedb192014-10-08 09:07:27 -070066 }
cdalton7d5c9502015-10-03 13:28:35 -070067 // No hairlines. They would require new paths with customized strokes for every new draw matrix.
68 return SkPaint::kStroke_Style != skPaint.getStyle() || 0 != skPaint.getStrokeWidth();
jvanverth0fedb192014-10-08 09:07:27 -070069}
70
Brian Osman11052242016-10-27 14:47:55 -040071void GrStencilAndCoverTextContext::drawText(GrContext* context, GrRenderTargetContext* rtc,
Brian Salomon6f1d36c2017-01-13 12:02:17 -050072 const GrClip& clip, const SkPaint& skPaint,
73 const SkMatrix& viewMatrix, const SkSurfaceProps& props,
74 const char text[], size_t byteLength, SkScalar x,
75 SkScalar y, const SkIRect& clipBounds) {
joshualitt27004b72016-02-11 12:00:33 -080076 if (context->abandoned()) {
joshualitte55750e2016-02-10 12:52:21 -080077 return;
78 } else if (this->canDraw(skPaint, viewMatrix)) {
kkinnunen68c63b32016-03-04 00:12:33 -080079 if (skPaint.getTextSize() > 0) {
80 TextRun run(skPaint);
kkinnunen68c63b32016-03-04 00:12:33 -080081 run.setText(text, byteLength, x, y);
Brian Salomon6f1d36c2017-01-13 12:02:17 -050082 run.draw(context, rtc, clip, viewMatrix, props, 0, 0, clipBounds, fFallbackTextContext,
83 skPaint);
kkinnunen68c63b32016-03-04 00:12:33 -080084 }
joshualitte55750e2016-02-10 12:52:21 -080085 return;
joshualitt27004b72016-02-11 12:00:33 -080086 } else if (fFallbackTextContext->canDraw(skPaint, viewMatrix, props,
87 *context->caps()->shaderCaps())) {
Brian Salomon6f1d36c2017-01-13 12:02:17 -050088 fFallbackTextContext->drawText(context, rtc, clip, skPaint, viewMatrix, props, text,
89 byteLength, x, y, clipBounds);
joshualitte55750e2016-02-10 12:52:21 -080090 return;
91 }
92
93 // fall back to drawing as a path
Brian Osman11052242016-10-27 14:47:55 -040094 GrTextUtils::DrawTextAsPath(context, rtc, clip, skPaint, viewMatrix, text, byteLength, x, y,
joshualitte55750e2016-02-10 12:52:21 -080095 clipBounds);
cdalton3bd909a2015-10-05 14:57:20 -070096}
jvanverthaab626c2014-10-16 08:04:39 -070097
Brian Osman11052242016-10-27 14:47:55 -040098void GrStencilAndCoverTextContext::drawPosText(GrContext* context, GrRenderTargetContext* rtc,
Brian Salomon6f1d36c2017-01-13 12:02:17 -050099 const GrClip& clip, const SkPaint& skPaint,
100 const SkMatrix& viewMatrix,
Brian Salomon82f44312017-01-11 13:42:54 -0500101 const SkSurfaceProps& props, const char text[],
102 size_t byteLength, const SkScalar pos[],
103 int scalarsPerPosition, const SkPoint& offset,
joshualitte55750e2016-02-10 12:52:21 -0800104 const SkIRect& clipBounds) {
joshualitt27004b72016-02-11 12:00:33 -0800105 if (context->abandoned()) {
joshualitte55750e2016-02-10 12:52:21 -0800106 return;
107 } else if (this->canDraw(skPaint, viewMatrix)) {
kkinnunen68c63b32016-03-04 00:12:33 -0800108 if (skPaint.getTextSize() > 0) {
109 TextRun run(skPaint);
kkinnunen68c63b32016-03-04 00:12:33 -0800110 run.setPosText(text, byteLength, pos, scalarsPerPosition, offset);
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500111 run.draw(context, rtc, clip, viewMatrix, props, 0, 0, clipBounds, fFallbackTextContext,
112 skPaint);
kkinnunen68c63b32016-03-04 00:12:33 -0800113 }
joshualitte55750e2016-02-10 12:52:21 -0800114 return;
joshualitt27004b72016-02-11 12:00:33 -0800115 } else if (fFallbackTextContext->canDraw(skPaint, viewMatrix, props,
116 *context->caps()->shaderCaps())) {
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500117 fFallbackTextContext->drawPosText(context, rtc, clip, skPaint, viewMatrix, props, text,
118 byteLength, pos, scalarsPerPosition, offset, clipBounds);
joshualitte55750e2016-02-10 12:52:21 -0800119 return;
120 }
121
122 // fall back to drawing as a path
Brian Osman11052242016-10-27 14:47:55 -0400123 GrTextUtils::DrawPosTextAsPath(context, rtc, props, clip, skPaint, viewMatrix, text,
joshualitte55750e2016-02-10 12:52:21 -0800124 byteLength, pos, scalarsPerPosition, offset, clipBounds);
125}
126
joshualitt27004b72016-02-11 12:00:33 -0800127void GrStencilAndCoverTextContext::uncachedDrawTextBlob(GrContext* context,
Brian Osman11052242016-10-27 14:47:55 -0400128 GrRenderTargetContext* rtc,
robertphillipsd2b6d642016-07-21 08:55:08 -0700129 const GrClip& clip,
130 const SkPaint& skPaint,
joshualitte55750e2016-02-10 12:52:21 -0800131 const SkMatrix& viewMatrix,
joshualitt2c89bc12016-02-11 05:42:30 -0800132 const SkSurfaceProps& props,
joshualitte55750e2016-02-10 12:52:21 -0800133 const SkTextBlob* blob,
134 SkScalar x, SkScalar y,
135 SkDrawFilter* drawFilter,
136 const SkIRect& clipBounds) {
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500137 GrTextUtils::Paint paint(&skPaint);
138 GrTextUtils::RunPaint runPaint(&paint, drawFilter, props);
joshualitte55750e2016-02-10 12:52:21 -0800139 SkTextBlobRunIterator it(blob);
140 for (;!it.done(); it.next()) {
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500141 if (!runPaint.modifyForRun(it)) {
142 continue;
143 }
joshualitte55750e2016-02-10 12:52:21 -0800144 size_t textLen = it.glyphCount() * sizeof(uint16_t);
145 const SkPoint& offset = it.offset();
146
joshualitte55750e2016-02-10 12:52:21 -0800147 switch (it.positioning()) {
148 case SkTextBlob::kDefault_Positioning:
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500149 this->drawText(context, rtc, clip, runPaint, viewMatrix, props,
Brian Salomon82f44312017-01-11 13:42:54 -0500150 (const char*)it.glyphs(), textLen, x + offset.x(), y + offset.y(),
151 clipBounds);
joshualitte55750e2016-02-10 12:52:21 -0800152 break;
153 case SkTextBlob::kHorizontal_Positioning:
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500154 this->drawPosText(context, rtc, clip, runPaint, viewMatrix, props,
155 (const char*)it.glyphs(), textLen, it.pos(), 1,
Brian Salomon82f44312017-01-11 13:42:54 -0500156 SkPoint::Make(x, y + offset.y()), clipBounds);
joshualitte55750e2016-02-10 12:52:21 -0800157 break;
158 case SkTextBlob::kFull_Positioning:
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500159 this->drawPosText(context, rtc, clip, runPaint, viewMatrix, props,
160 (const char*)it.glyphs(), textLen, it.pos(), 2,
Brian Salomon82f44312017-01-11 13:42:54 -0500161 SkPoint::Make(x, y), clipBounds);
joshualitte55750e2016-02-10 12:52:21 -0800162 break;
163 }
joshualitte55750e2016-02-10 12:52:21 -0800164 }
cdaltoncdd79072015-10-05 15:37:35 -0700165}
166
Brian Osman11052242016-10-27 14:47:55 -0400167void GrStencilAndCoverTextContext::drawTextBlob(GrContext* context, GrRenderTargetContext* rtc,
cdaltoncdd79072015-10-05 15:37:35 -0700168 const GrClip& clip, const SkPaint& skPaint,
169 const SkMatrix& viewMatrix,
joshualitt2c89bc12016-02-11 05:42:30 -0800170 const SkSurfaceProps& props,
cdaltoncdd79072015-10-05 15:37:35 -0700171 const SkTextBlob* skBlob, SkScalar x, SkScalar y,
172 SkDrawFilter* drawFilter,
173 const SkIRect& clipBounds) {
joshualitt27004b72016-02-11 12:00:33 -0800174 if (context->abandoned()) {
joshualitte55750e2016-02-10 12:52:21 -0800175 return;
176 }
177
cdaltoncdd79072015-10-05 15:37:35 -0700178 if (!this->internalCanDraw(skPaint)) {
Brian Osman11052242016-10-27 14:47:55 -0400179 fFallbackTextContext->drawTextBlob(context, rtc, clip, skPaint, viewMatrix, props, skBlob,
joshualitt27004b72016-02-11 12:00:33 -0800180 x, y, drawFilter, clipBounds);
cdaltoncdd79072015-10-05 15:37:35 -0700181 return;
182 }
183
184 if (drawFilter || skPaint.getPathEffect()) {
185 // This draw can't be cached.
Brian Osman11052242016-10-27 14:47:55 -0400186 this->uncachedDrawTextBlob(context, rtc, clip, skPaint, viewMatrix, props, skBlob, x, y,
joshualitt27004b72016-02-11 12:00:33 -0800187 drawFilter, clipBounds);
cdaltoncdd79072015-10-05 15:37:35 -0700188 return;
189 }
190
cdaltoncdd79072015-10-05 15:37:35 -0700191 const TextBlob& blob = this->findOrCreateTextBlob(skBlob, skPaint);
cdaltoncdd79072015-10-05 15:37:35 -0700192
193 TextBlob::Iter iter(blob);
Brian Salomon82f44312017-01-11 13:42:54 -0500194 for (TextRun *run = iter.get(), *nextRun; run; run = nextRun) {
195 nextRun = iter.next();
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500196 run->draw(context, rtc, clip, viewMatrix, props, x, y, clipBounds, fFallbackTextContext,
197 skPaint);
cdalton8585dd22015-10-08 08:04:09 -0700198 run->releaseGlyphCache();
cdaltoncdd79072015-10-05 15:37:35 -0700199 }
200}
201
bsalomon6663acf2016-05-10 09:14:17 -0700202static inline int style_key_cnt(const GrStyle& style) {
203 int cnt = GrStyle::KeySize(style, GrStyle::Apply::kPathEffectAndStrokeRec);
204 // We should be able to make a key because we filtered out arbitrary path effects.
205 SkASSERT(cnt > 0);
206 return cnt;
207}
208
209static inline void write_style_key(uint32_t* dst, const GrStyle& style) {
210 // Pass 1 for the scale since the GPU will apply the style not GrStyle::applyToPath().
211 GrStyle::WriteKey(dst, style, GrStyle::Apply::kPathEffectAndStrokeRec, SK_Scalar1);
212}
213
cdaltoncdd79072015-10-05 15:37:35 -0700214const GrStencilAndCoverTextContext::TextBlob&
215GrStencilAndCoverTextContext::findOrCreateTextBlob(const SkTextBlob* skBlob,
216 const SkPaint& skPaint) {
217 // The font-related parameters are baked into the text blob and will override this skPaint, so
218 // the only remaining properties that can affect a TextBlob are the ones related to stroke.
219 if (SkPaint::kFill_Style == skPaint.getStyle()) { // Fast path.
220 if (TextBlob** found = fBlobIdCache.find(skBlob->uniqueID())) {
221 fLRUList.remove(*found);
222 fLRUList.addToTail(*found);
223 return **found;
224 }
cdalton8585dd22015-10-08 08:04:09 -0700225 TextBlob* blob = new TextBlob(skBlob->uniqueID(), skBlob, skPaint);
cdaltoncdd79072015-10-05 15:37:35 -0700226 this->purgeToFit(*blob);
227 fBlobIdCache.set(skBlob->uniqueID(), blob);
228 fLRUList.addToTail(blob);
229 fCacheSize += blob->cpuMemorySize();
230 return *blob;
231 } else {
bsalomon6663acf2016-05-10 09:14:17 -0700232 GrStyle style(skPaint);
cdaltoncdd79072015-10-05 15:37:35 -0700233 SkSTArray<4, uint32_t, true> key;
bsalomon6663acf2016-05-10 09:14:17 -0700234 key.reset(1 + style_key_cnt(style));
cdaltoncdd79072015-10-05 15:37:35 -0700235 key[0] = skBlob->uniqueID();
bsalomon6663acf2016-05-10 09:14:17 -0700236 write_style_key(&key[1], style);
cdaltoncdd79072015-10-05 15:37:35 -0700237 if (TextBlob** found = fBlobKeyCache.find(key)) {
238 fLRUList.remove(*found);
239 fLRUList.addToTail(*found);
240 return **found;
241 }
cdalton8585dd22015-10-08 08:04:09 -0700242 TextBlob* blob = new TextBlob(key, skBlob, skPaint);
cdaltoncdd79072015-10-05 15:37:35 -0700243 this->purgeToFit(*blob);
244 fBlobKeyCache.set(blob);
245 fLRUList.addToTail(blob);
246 fCacheSize += blob->cpuMemorySize();
247 return *blob;
248 }
249}
250
251void GrStencilAndCoverTextContext::purgeToFit(const TextBlob& blob) {
cdalton8585dd22015-10-08 08:04:09 -0700252 static const size_t maxCacheSize = 4 * 1024 * 1024; // Allow up to 4 MB for caching text blobs.
cdaltoncdd79072015-10-05 15:37:35 -0700253
cdalton8585dd22015-10-08 08:04:09 -0700254 size_t maxSizeForNewBlob = maxCacheSize - blob.cpuMemorySize();
cdaltoncdd79072015-10-05 15:37:35 -0700255 while (fCacheSize && fCacheSize > maxSizeForNewBlob) {
256 TextBlob* lru = fLRUList.head();
257 if (1 == lru->key().count()) {
258 // 1-length keys are unterstood to be the blob id.
259 fBlobIdCache.remove(lru->key()[0]);
260 } else {
261 fBlobKeyCache.remove(lru->key());
262 }
263 fLRUList.remove(lru);
264 fCacheSize -= lru->cpuMemorySize();
265 delete lru;
266 }
267}
268
269////////////////////////////////////////////////////////////////////////////////////////////////////
270
cdalton8585dd22015-10-08 08:04:09 -0700271void GrStencilAndCoverTextContext::TextBlob::init(const SkTextBlob* skBlob,
272 const SkPaint& skPaint) {
cdaltoncdd79072015-10-05 15:37:35 -0700273 fCpuMemorySize = sizeof(TextBlob);
274 SkPaint runPaint(skPaint);
halcanary33779752015-10-27 14:01:05 -0700275 for (SkTextBlobRunIterator iter(skBlob); !iter.done(); iter.next()) {
cdaltoncdd79072015-10-05 15:37:35 -0700276 iter.applyFontToPaint(&runPaint); // No need to re-seed the paint.
kkinnunen68c63b32016-03-04 00:12:33 -0800277 if (runPaint.getTextSize() <= 0) {
278 continue;
279 }
bsalomon5aaef1f2015-11-18 14:11:08 -0800280 TextRun* run = this->addToTail(runPaint);
cdaltoncdd79072015-10-05 15:37:35 -0700281
282 const char* text = reinterpret_cast<const char*>(iter.glyphs());
283 size_t byteLength = sizeof(uint16_t) * iter.glyphCount();
284 const SkPoint& runOffset = iter.offset();
285
286 switch (iter.positioning()) {
287 case SkTextBlob::kDefault_Positioning:
cdalton8585dd22015-10-08 08:04:09 -0700288 run->setText(text, byteLength, runOffset.fX, runOffset.fY);
cdaltoncdd79072015-10-05 15:37:35 -0700289 break;
290 case SkTextBlob::kHorizontal_Positioning:
cdalton8585dd22015-10-08 08:04:09 -0700291 run->setPosText(text, byteLength, iter.pos(), 1, SkPoint::Make(0, runOffset.fY));
cdaltoncdd79072015-10-05 15:37:35 -0700292 break;
293 case SkTextBlob::kFull_Positioning:
cdalton8585dd22015-10-08 08:04:09 -0700294 run->setPosText(text, byteLength, iter.pos(), 2, SkPoint::Make(0, 0));
cdaltoncdd79072015-10-05 15:37:35 -0700295 break;
296 }
297
cdalton8585dd22015-10-08 08:04:09 -0700298 fCpuMemorySize += run->computeSizeInCache();
cdaltoncdd79072015-10-05 15:37:35 -0700299 }
cdalton3bd909a2015-10-05 14:57:20 -0700300}
301
302////////////////////////////////////////////////////////////////////////////////////////////////////
303
cdalton02015e52015-10-05 15:28:20 -0700304class GrStencilAndCoverTextContext::FallbackBlobBuilder {
305public:
cdaltoncdd46822015-12-08 10:48:31 -0800306 FallbackBlobBuilder() : fBuffIdx(0), fCount(0) {}
cdalton02015e52015-10-05 15:28:20 -0700307
mtklein5f939ab2016-03-16 10:28:35 -0700308 bool isInitialized() const { return fBuilder != nullptr; }
cdalton02015e52015-10-05 15:28:20 -0700309
310 void init(const SkPaint& font, SkScalar textRatio);
311
312 void appendGlyph(uint16_t glyphId, const SkPoint& pos);
313
fmalita37283c22016-09-13 10:00:23 -0700314 sk_sp<SkTextBlob> makeIfNeeded(int* count);
cdalton02015e52015-10-05 15:28:20 -0700315
316private:
317 enum { kWriteBufferSize = 1024 };
318
319 void flush();
320
Ben Wagner145dbcd2016-11-03 14:40:50 -0400321 std::unique_ptr<SkTextBlobBuilder> fBuilder;
cdalton02015e52015-10-05 15:28:20 -0700322 SkPaint fFont;
323 int fBuffIdx;
cdaltoncdd46822015-12-08 10:48:31 -0800324 int fCount;
cdalton02015e52015-10-05 15:28:20 -0700325 uint16_t fGlyphIds[kWriteBufferSize];
326 SkPoint fPositions[kWriteBufferSize];
327};
328
329////////////////////////////////////////////////////////////////////////////////////////////////////
330
cdalton3bd909a2015-10-05 14:57:20 -0700331GrStencilAndCoverTextContext::TextRun::TextRun(const SkPaint& fontAndStroke)
robertphillipsecbc15d2016-07-19 14:04:39 -0700332 : fStyle(fontAndStroke)
333 , fFont(fontAndStroke)
334 , fTotalGlyphCount(0)
335 , fFallbackGlyphCount(0)
336 , fDetachedGlyphCache(nullptr)
337 , fLastDrawnGlyphsID(SK_InvalidUniqueID) {
kkinnunen68c63b32016-03-04 00:12:33 -0800338 SkASSERT(fFont.getTextSize() > 0);
bsalomon6663acf2016-05-10 09:14:17 -0700339 SkASSERT(!fStyle.hasNonDashPathEffect()); // Arbitrary path effects not supported.
340 SkASSERT(!fStyle.isSimpleHairline()); // Hairlines are not supported.
cdalton3bd909a2015-10-05 14:57:20 -0700341
342 // Setting to "fill" ensures that no strokes get baked into font outlines. (We use the GPU path
343 // rendering API for stroking).
344 fFont.setStyle(SkPaint::kFill_Style);
345
bsalomon6663acf2016-05-10 09:14:17 -0700346 if (fFont.isFakeBoldText() && fStyle.isSimpleFill()) {
347 const SkStrokeRec& stroke = fStyle.strokeRec();
cdalton3bd909a2015-10-05 14:57:20 -0700348 // Instead of letting fake bold get baked into the glyph outlines, do it with GPU stroke.
349 SkScalar fakeBoldScale = SkScalarInterpFunc(fFont.getTextSize(),
350 kStdFakeBoldInterpKeys,
351 kStdFakeBoldInterpValues,
352 kStdFakeBoldInterpLength);
Mike Reed8be952a2017-02-13 20:44:33 -0500353 SkScalar extra = fFont.getTextSize() * fakeBoldScale;
cdalton3bd909a2015-10-05 14:57:20 -0700354
bsalomon6663acf2016-05-10 09:14:17 -0700355 SkStrokeRec strokeRec(SkStrokeRec::kFill_InitStyle);
356 strokeRec.setStrokeStyle(stroke.needToApply() ? stroke.getWidth() + extra : extra,
357 true /*strokeAndFill*/);
Robert Phillipsf809c1e2017-01-13 11:02:42 -0500358 fStyle = GrStyle(strokeRec, fStyle.refPathEffect());
cdalton3bd909a2015-10-05 14:57:20 -0700359 fFont.setFakeBoldText(false);
jvanverthaab626c2014-10-16 08:04:39 -0700360 }
361
bsalomon6663acf2016-05-10 09:14:17 -0700362 if (!fFont.getPathEffect() && !fStyle.isDashed()) {
363 const SkStrokeRec& stroke = fStyle.strokeRec();
cdalton3bd909a2015-10-05 14:57:20 -0700364 // We can draw the glyphs from canonically sized paths.
365 fTextRatio = fFont.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
366 fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fFont.getTextSize();
jvanverthaab626c2014-10-16 08:04:39 -0700367
cdalton3bd909a2015-10-05 14:57:20 -0700368 // Compensate for the glyphs being scaled by fTextRatio.
bsalomon6663acf2016-05-10 09:14:17 -0700369 if (!fStyle.isSimpleFill()) {
370 SkStrokeRec strokeRec(SkStrokeRec::kFill_InitStyle);
371 strokeRec.setStrokeStyle(stroke.getWidth() / fTextRatio,
372 SkStrokeRec::kStrokeAndFill_Style == stroke.getStyle());
Robert Phillipsf809c1e2017-01-13 11:02:42 -0500373 fStyle = GrStyle(strokeRec, fStyle.refPathEffect());
cdalton3bd909a2015-10-05 14:57:20 -0700374 }
375
376 fFont.setLinearText(true);
377 fFont.setLCDRenderText(false);
378 fFont.setAutohinted(false);
379 fFont.setHinting(SkPaint::kNo_Hinting);
380 fFont.setSubpixelText(true);
381 fFont.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
382
383 fUsingRawGlyphPaths = SK_Scalar1 == fFont.getTextScaleX() &&
384 0 == fFont.getTextSkewX() &&
385 !fFont.isFakeBoldText() &&
386 !fFont.isVerticalText();
387 } else {
388 fTextRatio = fTextInverseRatio = 1.0f;
389 fUsingRawGlyphPaths = false;
390 }
391
cdalton8585dd22015-10-08 08:04:09 -0700392 // Generate the key that will be used to cache the GPU glyph path objects.
bsalomon6663acf2016-05-10 09:14:17 -0700393 if (fUsingRawGlyphPaths && fStyle.isSimpleFill()) {
cdalton8585dd22015-10-08 08:04:09 -0700394 static const GrUniqueKey::Domain kRawFillPathGlyphDomain = GrUniqueKey::GenerateDomain();
395
396 const SkTypeface* typeface = fFont.getTypeface();
397 GrUniqueKey::Builder builder(&fGlyphPathsKey, kRawFillPathGlyphDomain, 1);
398 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
399 } else {
400 static const GrUniqueKey::Domain kPathGlyphDomain = GrUniqueKey::GenerateDomain();
401
bsalomon6663acf2016-05-10 09:14:17 -0700402 int styleDataCount = GrStyle::KeySize(fStyle, GrStyle::Apply::kPathEffectAndStrokeRec);
403 // Key should be valid since we opted out of drawing arbitrary path effects.
404 SkASSERT(styleDataCount >= 0);
cdalton8585dd22015-10-08 08:04:09 -0700405 if (fUsingRawGlyphPaths) {
406 const SkTypeface* typeface = fFont.getTypeface();
bsalomon6663acf2016-05-10 09:14:17 -0700407 GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain, 2 + styleDataCount);
cdalton8585dd22015-10-08 08:04:09 -0700408 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
bsalomon6663acf2016-05-10 09:14:17 -0700409 reinterpret_cast<uint32_t&>(builder[1]) = styleDataCount;
410 if (styleDataCount) {
411 write_style_key(&builder[2], fStyle);
412 }
cdalton8585dd22015-10-08 08:04:09 -0700413 } else {
414 SkGlyphCache* glyphCache = this->getGlyphCache();
415 const SkTypeface* typeface = glyphCache->getScalerContext()->getTypeface();
416 const SkDescriptor* desc = &glyphCache->getDescriptor();
417 int descDataCount = (desc->getLength() + 3) / 4;
418 GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain,
bsalomon6663acf2016-05-10 09:14:17 -0700419 2 + styleDataCount + descDataCount);
cdalton8585dd22015-10-08 08:04:09 -0700420 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
bsalomon6663acf2016-05-10 09:14:17 -0700421 reinterpret_cast<uint32_t&>(builder[1]) = styleDataCount | (descDataCount << 16);
422 if (styleDataCount) {
423 write_style_key(&builder[2], fStyle);
424 }
425 memcpy(&builder[2 + styleDataCount], desc, desc->getLength());
cdalton8585dd22015-10-08 08:04:09 -0700426 }
427 }
cdalton3bd909a2015-10-05 14:57:20 -0700428}
429
430GrStencilAndCoverTextContext::TextRun::~TextRun() {
cdalton8585dd22015-10-08 08:04:09 -0700431 this->releaseGlyphCache();
cdalton3bd909a2015-10-05 14:57:20 -0700432}
433
434void GrStencilAndCoverTextContext::TextRun::setText(const char text[], size_t byteLength,
cdalton8585dd22015-10-08 08:04:09 -0700435 SkScalar x, SkScalar y) {
cdalton3bd909a2015-10-05 14:57:20 -0700436 SkASSERT(byteLength == 0 || text != nullptr);
437
cdalton8585dd22015-10-08 08:04:09 -0700438 SkGlyphCache* glyphCache = this->getGlyphCache();
robertphillipse34f17d2016-07-19 07:59:22 -0700439 SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(fFont.getTextEncoding(),
440 fFont.isDevKernText(),
441 true);
jvanverthaab626c2014-10-16 08:04:39 -0700442
cdaltoncdd46822015-12-08 10:48:31 -0800443 fTotalGlyphCount = fFont.countText(text, byteLength);
444 fInstanceData.reset(InstanceData::Alloc(GrPathRendering::kTranslate_PathTransformType,
445 fTotalGlyphCount));
cdalton8585dd22015-10-08 08:04:09 -0700446
jvanverthaab626c2014-10-16 08:04:39 -0700447 const char* stop = text + byteLength;
448
449 // Measure first if needed.
cdalton3bd909a2015-10-05 14:57:20 -0700450 if (fFont.getTextAlign() != SkPaint::kLeft_Align) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700451 SkScalar stopX = 0;
452 SkScalar stopY = 0;
jvanverthaab626c2014-10-16 08:04:39 -0700453
454 const char* textPtr = text;
455 while (textPtr < stop) {
456 // We don't need x, y here, since all subpixel variants will have the
457 // same advance.
benjaminwagnerd936f632016-02-23 10:44:31 -0800458 const SkGlyph& glyph = glyphCacheProc(glyphCache, &textPtr);
jvanverthaab626c2014-10-16 08:04:39 -0700459
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700460 stopX += SkFloatToScalar(glyph.fAdvanceX);
461 stopY += SkFloatToScalar(glyph.fAdvanceY);
jvanverthaab626c2014-10-16 08:04:39 -0700462 }
463 SkASSERT(textPtr == stop);
464
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700465 SkScalar alignX = stopX * fTextRatio;
466 SkScalar alignY = stopY * fTextRatio;
jvanverthaab626c2014-10-16 08:04:39 -0700467
cdalton3bd909a2015-10-05 14:57:20 -0700468 if (fFont.getTextAlign() == SkPaint::kCenter_Align) {
jvanverthaab626c2014-10-16 08:04:39 -0700469 alignX = SkScalarHalf(alignX);
470 alignY = SkScalarHalf(alignY);
471 }
472
473 x -= alignX;
474 y -= alignY;
475 }
476
477 SkAutoKern autokern;
478
cdalton02015e52015-10-05 15:28:20 -0700479 FallbackBlobBuilder fallback;
jvanverthaab626c2014-10-16 08:04:39 -0700480 while (text < stop) {
benjaminwagnerd936f632016-02-23 10:44:31 -0800481 const SkGlyph& glyph = glyphCacheProc(glyphCache, &text);
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700482 x += autokern.adjust(glyph) * fTextRatio;
jvanverthaab626c2014-10-16 08:04:39 -0700483 if (glyph.fWidth) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700484 this->appendGlyph(glyph, SkPoint::Make(x, y), &fallback);
jvanverthaab626c2014-10-16 08:04:39 -0700485 }
486
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700487 x += SkFloatToScalar(glyph.fAdvanceX) * fTextRatio;
488 y += SkFloatToScalar(glyph.fAdvanceY) * fTextRatio;
jvanverthaab626c2014-10-16 08:04:39 -0700489 }
cdalton02015e52015-10-05 15:28:20 -0700490
fmalita37283c22016-09-13 10:00:23 -0700491 fFallbackTextBlob = fallback.makeIfNeeded(&fFallbackGlyphCount);
jvanverthaab626c2014-10-16 08:04:39 -0700492}
493
cdalton3bd909a2015-10-05 14:57:20 -0700494void GrStencilAndCoverTextContext::TextRun::setPosText(const char text[], size_t byteLength,
495 const SkScalar pos[], int scalarsPerPosition,
cdalton8585dd22015-10-08 08:04:09 -0700496 const SkPoint& offset) {
halcanary96fcdcc2015-08-27 07:41:13 -0700497 SkASSERT(byteLength == 0 || text != nullptr);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700498 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
499
cdalton8585dd22015-10-08 08:04:09 -0700500 SkGlyphCache* glyphCache = this->getGlyphCache();
robertphillipse34f17d2016-07-19 07:59:22 -0700501 SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(fFont.getTextEncoding(),
502 fFont.isDevKernText(),
503 true);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700504
cdaltoncdd46822015-12-08 10:48:31 -0800505 fTotalGlyphCount = fFont.countText(text, byteLength);
506 fInstanceData.reset(InstanceData::Alloc(GrPathRendering::kTranslate_PathTransformType,
507 fTotalGlyphCount));
cdalton8585dd22015-10-08 08:04:09 -0700508
kkinnunenc6cb56f2014-06-24 00:12:27 -0700509 const char* stop = text + byteLength;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700510
cdalton38e13ad2014-11-07 06:02:15 -0800511 SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
cdalton3bd909a2015-10-05 14:57:20 -0700512 SkTextAlignProc alignProc(fFont.getTextAlign());
cdalton02015e52015-10-05 15:28:20 -0700513 FallbackBlobBuilder fallback;
cdalton38e13ad2014-11-07 06:02:15 -0800514 while (text < stop) {
benjaminwagnerd936f632016-02-23 10:44:31 -0800515 const SkGlyph& glyph = glyphCacheProc(glyphCache, &text);
cdalton38e13ad2014-11-07 06:02:15 -0800516 if (glyph.fWidth) {
517 SkPoint tmsLoc;
518 tmsProc(pos, &tmsLoc);
519 SkPoint loc;
520 alignProc(tmsLoc, glyph, &loc);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700521
cdalton02015e52015-10-05 15:28:20 -0700522 this->appendGlyph(glyph, loc, &fallback);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700523 }
cdalton38e13ad2014-11-07 06:02:15 -0800524 pos += scalarsPerPosition;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700525 }
cdalton02015e52015-10-05 15:28:20 -0700526
fmalita37283c22016-09-13 10:00:23 -0700527 fFallbackTextBlob = fallback.makeIfNeeded(&fFallbackGlyphCount);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700528}
529
Robert Phillips26c90e02017-03-14 14:39:29 -0400530GrPathRange* GrStencilAndCoverTextContext::TextRun::createGlyphs(
531 GrResourceProvider* resourceProvider) const {
cdalton8585dd22015-10-08 08:04:09 -0700532 GrPathRange* glyphs = static_cast<GrPathRange*>(
Robert Phillips26c90e02017-03-14 14:39:29 -0400533 resourceProvider->findAndRefResourceByUniqueKey(fGlyphPathsKey));
halcanary96fcdcc2015-08-27 07:41:13 -0700534 if (nullptr == glyphs) {
cdalton8585dd22015-10-08 08:04:09 -0700535 if (fUsingRawGlyphPaths) {
reeda9322c22016-04-12 06:47:05 -0700536 SkScalerContextEffects noeffects;
Robert Phillips26c90e02017-03-14 14:39:29 -0400537 glyphs = resourceProvider->createGlyphs(fFont.getTypeface(), noeffects,
538 nullptr, fStyle);
cdalton8585dd22015-10-08 08:04:09 -0700539 } else {
540 SkGlyphCache* cache = this->getGlyphCache();
Robert Phillips26c90e02017-03-14 14:39:29 -0400541 glyphs = resourceProvider->createGlyphs(cache->getScalerContext()->getTypeface(),
542 cache->getScalerContext()->getEffects(),
543 &cache->getDescriptor(),
544 fStyle);
cdalton8585dd22015-10-08 08:04:09 -0700545 }
Robert Phillips26c90e02017-03-14 14:39:29 -0400546 resourceProvider->assignUniqueKeyToResource(fGlyphPathsKey, glyphs);
cdalton855d83f2014-09-18 13:51:53 -0700547 }
cdalton8585dd22015-10-08 08:04:09 -0700548 return glyphs;
cdalton855d83f2014-09-18 13:51:53 -0700549}
550
cdalton3bd909a2015-10-05 14:57:20 -0700551inline void GrStencilAndCoverTextContext::TextRun::appendGlyph(const SkGlyph& glyph,
cdalton02015e52015-10-05 15:28:20 -0700552 const SkPoint& pos,
553 FallbackBlobBuilder* fallback) {
554 // Stick the glyphs we can't draw into the fallback text blob.
bsalomon1fcc01c2015-09-09 09:48:06 -0700555 if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
cdalton02015e52015-10-05 15:28:20 -0700556 if (!fallback->isInitialized()) {
557 fallback->init(fFont, fTextRatio);
558 }
559 fallback->appendGlyph(glyph.getGlyphID(), pos);
bsalomon1fcc01c2015-09-09 09:48:06 -0700560 } else {
cdaltoncdd46822015-12-08 10:48:31 -0800561 fInstanceData->append(glyph.getGlyphID(), fTextInverseRatio * pos.x(),
562 fTextInverseRatio * pos.y());
cdaltonb2808cd2014-07-25 14:13:57 -0700563 }
cdalton20b373c2014-12-01 08:38:55 -0800564}
565
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500566void GrStencilAndCoverTextContext::TextRun::draw(GrContext* ctx,
567 GrRenderTargetContext* renderTargetContext,
568 const GrClip& clip, const SkMatrix& viewMatrix,
569 const SkSurfaceProps& props, SkScalar x,
570 SkScalar y, const SkIRect& clipBounds,
571 GrAtlasTextContext* fallbackTextContext,
572 const SkPaint& originalSkPaint) const {
cdaltoncdd46822015-12-08 10:48:31 -0800573 SkASSERT(fInstanceData);
robertphillipsea461502015-05-26 11:38:03 -0700574
cdaltoncdd46822015-12-08 10:48:31 -0800575 if (fInstanceData->count()) {
cdalton93a379b2016-05-11 13:58:08 -0700576 static constexpr GrUserStencilSettings kCoverPass(
577 GrUserStencilSettings::StaticInit<
578 0x0000,
579 GrUserStencilTest::kNotEqual, // Stencil pass accounts for clip.
580 0xffff,
581 GrUserStencilOp::kZero,
582 GrUserStencilOp::kKeep,
583 0xffff>()
584 );
joshualitt7b670db2015-07-09 13:25:02 -0700585
Robert Phillips26c90e02017-03-14 14:39:29 -0400586 sk_sp<GrPathRange> glyphs(this->createGlyphs(ctx->resourceProvider()));
robertphillips8abb3702016-08-31 14:04:06 -0700587 if (fLastDrawnGlyphsID != glyphs->uniqueID()) {
cdalton8585dd22015-10-08 08:04:09 -0700588 // Either this is the first draw or the glyphs object was purged since last draw.
cdaltoncdd46822015-12-08 10:48:31 -0800589 glyphs->loadPathsIfNeeded(fInstanceData->indices(), fInstanceData->count());
robertphillips8abb3702016-08-31 14:04:06 -0700590 fLastDrawnGlyphsID = glyphs->uniqueID();
cdalton8585dd22015-10-08 08:04:09 -0700591 }
592
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500593 GrPaint grPaint;
Robert Phillips26c90e02017-03-14 14:39:29 -0400594 if (!SkPaintToGrPaint(ctx, renderTargetContext, originalSkPaint, viewMatrix, &grPaint)) {
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500595 return;
596 }
597
bsalomonbf074552015-11-23 14:25:19 -0800598 // Don't compute a bounding box. For dst copy texture, we'll opt instead for it to just copy
599 // the entire dst. Realistically this is a moot point, because any context that supports
600 // NV_path_rendering will also support NV_blend_equation_advanced.
601 // For clipping we'll just skip any optimizations based on the bounds. This does, however,
Brian Salomon09d994e2016-12-21 11:14:46 -0500602 // hurt GrOp combining.
Brian Osman11052242016-10-27 14:47:55 -0400603 const SkRect bounds = SkRect::MakeIWH(renderTargetContext->width(),
604 renderTargetContext->height());
bsalomonbf074552015-11-23 14:25:19 -0800605
Brian Salomon54d212e2017-03-21 14:22:38 -0400606 // The run's "font" overrides the anti-aliasing of the passed in SkPaint!
Brian Salomon48d1b4c2017-04-08 07:38:53 -0400607 GrAAType aaType;
608 if (this->aa() == GrAA::kYes) {
609 SkASSERT(renderTargetContext->isStencilBufferMultisampled());
610 aaType = renderTargetContext->isUnifiedMultisampled() ? GrAAType::kMSAA
611 : GrAAType::kMixedSamples;
612 } else {
613 aaType = GrAAType::kNone;
614 }
615
Brian Salomonf8334782017-01-03 09:42:58 -0500616 std::unique_ptr<GrDrawOp> op = GrDrawPathRangeOp::Make(
617 viewMatrix, fTextRatio, fTextInverseRatio * x, fTextInverseRatio * y,
Brian Salomon48d1b4c2017-04-08 07:38:53 -0400618 std::move(grPaint), GrPathRendering::kWinding_FillType, aaType, glyphs.get(),
Brian Salomonf8334782017-01-03 09:42:58 -0500619 fInstanceData.get(), bounds);
cdalton8ff8d242015-12-08 10:20:32 -0800620
Brian Salomon54d212e2017-03-21 14:22:38 -0400621 renderTargetContext->addDrawOp(clip, std::move(op));
kkinnunenc6cb56f2014-06-24 00:12:27 -0700622 }
623
cdalton02015e52015-10-05 15:28:20 -0700624 if (fFallbackTextBlob) {
cdalton3bd909a2015-10-05 14:57:20 -0700625 SkPaint fallbackSkPaint(originalSkPaint);
bsalomon6663acf2016-05-10 09:14:17 -0700626 fStyle.strokeRec().applyToPaint(&fallbackSkPaint);
627 if (!fStyle.isSimpleFill()) {
628 fallbackSkPaint.setStrokeWidth(fStyle.strokeRec().getWidth() * fTextRatio);
cdalton20b373c2014-12-01 08:38:55 -0800629 }
cdalton20b373c2014-12-01 08:38:55 -0800630
Brian Osman11052242016-10-27 14:47:55 -0400631 fallbackTextContext->drawTextBlob(ctx, renderTargetContext, clip, fallbackSkPaint,
632 viewMatrix, props, fFallbackTextBlob.get(), x, y, nullptr,
fmalita37283c22016-09-13 10:00:23 -0700633 clipBounds);
cdalton20b373c2014-12-01 08:38:55 -0800634 }
kkinnunenc6cb56f2014-06-24 00:12:27 -0700635}
cdalton02015e52015-10-05 15:28:20 -0700636
cdalton8585dd22015-10-08 08:04:09 -0700637SkGlyphCache* GrStencilAndCoverTextContext::TextRun::getGlyphCache() const {
638 if (!fDetachedGlyphCache) {
brianosmana1e8f8d2016-04-08 06:47:54 -0700639 fDetachedGlyphCache = fFont.detachCache(nullptr, SkPaint::kNone_ScalerContextFlags,
640 nullptr);
cdalton8585dd22015-10-08 08:04:09 -0700641 }
642 return fDetachedGlyphCache;
643}
644
645
646void GrStencilAndCoverTextContext::TextRun::releaseGlyphCache() const {
647 if (fDetachedGlyphCache) {
648 SkGlyphCache::AttachCache(fDetachedGlyphCache);
649 fDetachedGlyphCache = nullptr;
650 }
651}
652
653size_t GrStencilAndCoverTextContext::TextRun::computeSizeInCache() const {
cdaltoncdd46822015-12-08 10:48:31 -0800654 size_t size = sizeof(TextRun) + fGlyphPathsKey.size();
655 // The instance data always reserves enough space for every glyph.
656 size += (fTotalGlyphCount + fFallbackGlyphCount) * (sizeof(uint16_t) + 2 * sizeof(float));
657 if (fInstanceData) {
658 size += sizeof(InstanceData);
cdaltoncdd79072015-10-05 15:37:35 -0700659 }
660 if (fFallbackTextBlob) {
661 size += sizeof(SkTextBlob);
662 }
663 return size;
664}
665
cdalton02015e52015-10-05 15:28:20 -0700666////////////////////////////////////////////////////////////////////////////////////////////////////
667
668void GrStencilAndCoverTextContext::FallbackBlobBuilder::init(const SkPaint& font,
669 SkScalar textRatio) {
670 SkASSERT(!this->isInitialized());
671 fBuilder.reset(new SkTextBlobBuilder);
672 fFont = font;
673 fFont.setTextAlign(SkPaint::kLeft_Align); // The glyph positions will already account for align.
674 fFont.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
675 // No need for subpixel positioning with bitmap glyphs. TODO: revisit if non-bitmap color glyphs
676 // show up and https://code.google.com/p/skia/issues/detail?id=4408 gets resolved.
677 fFont.setSubpixelText(false);
678 fFont.setTextSize(fFont.getTextSize() * textRatio);
679 fBuffIdx = 0;
680}
681
682void GrStencilAndCoverTextContext::FallbackBlobBuilder::appendGlyph(uint16_t glyphId,
683 const SkPoint& pos) {
684 SkASSERT(this->isInitialized());
685 if (fBuffIdx >= kWriteBufferSize) {
686 this->flush();
687 }
688 fGlyphIds[fBuffIdx] = glyphId;
689 fPositions[fBuffIdx] = pos;
690 fBuffIdx++;
cdaltoncdd46822015-12-08 10:48:31 -0800691 fCount++;
cdalton02015e52015-10-05 15:28:20 -0700692}
693
694void GrStencilAndCoverTextContext::FallbackBlobBuilder::flush() {
695 SkASSERT(this->isInitialized());
696 SkASSERT(fBuffIdx <= kWriteBufferSize);
697 if (!fBuffIdx) {
698 return;
699 }
700 // This will automatically merge with previous runs since we use the same font.
701 const SkTextBlobBuilder::RunBuffer& buff = fBuilder->allocRunPos(fFont, fBuffIdx);
702 memcpy(buff.glyphs, fGlyphIds, fBuffIdx * sizeof(uint16_t));
703 memcpy(buff.pos, fPositions[0].asScalars(), fBuffIdx * 2 * sizeof(SkScalar));
704 fBuffIdx = 0;
705}
706
fmalita37283c22016-09-13 10:00:23 -0700707sk_sp<SkTextBlob> GrStencilAndCoverTextContext::FallbackBlobBuilder::makeIfNeeded(int *count) {
cdaltoncdd46822015-12-08 10:48:31 -0800708 *count = fCount;
709 if (fCount) {
710 this->flush();
fmalita37283c22016-09-13 10:00:23 -0700711 return fBuilder->make();
cdalton02015e52015-10-05 15:28:20 -0700712 }
cdaltoncdd46822015-12-08 10:48:31 -0800713 return nullptr;
cdalton02015e52015-10-05 15:28:20 -0700714}