blob: 502f4adc56f6ab553cbe34e0b807f97eae112214 [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"
Brian Salomon6f1d36c2017-01-13 12:02:17 -050013#include "GrRenderTargetContext.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"
Brian Salomon6f1d36c2017-01-13 12:02:17 -050018#include "SkDrawFilter.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070019#include "SkDrawProcs.h"
20#include "SkGlyphCache.h"
Brian Osman3b655982017-03-07 16:58:08 -050021#include "SkGr.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070022#include "SkPath.h"
halcanary33779752015-10-27 14:01:05 -070023#include "SkTextBlobRunIterator.h"
cdalton855d83f2014-09-18 13:51:53 -070024#include "SkTextFormatParams.h"
Brian Salomon6f1d36c2017-01-13 12:02:17 -050025#include "SkTextMapStateProc.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070026
Brian Salomon89527432016-12-16 09:52:16 -050027#include "ops/GrDrawPathOp.h"
bsalomon1fcc01c2015-09-09 09:48:06 -070028
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
brianosman86e76262016-08-11 12:17:31 -070039GrStencilAndCoverTextContext::GrStencilAndCoverTextContext(GrAtlasTextContext* fallbackTextContext)
40 : fFallbackTextContext(fallbackTextContext)
joshualitte55750e2016-02-10 12:52:21 -080041 , fCacheSize(0) {
kkinnunenc6cb56f2014-06-24 00:12:27 -070042}
43
joshualitt6e8cd962015-03-20 10:30:14 -070044GrStencilAndCoverTextContext*
brianosman86e76262016-08-11 12:17:31 -070045GrStencilAndCoverTextContext::Create(GrAtlasTextContext* fallbackTextContext) {
46 return new GrStencilAndCoverTextContext(fallbackTextContext);;
jvanverth8c27a182014-10-14 08:45:50 -070047}
48
kkinnunenc6cb56f2014-06-24 00:12:27 -070049GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() {
cdaltoncdd79072015-10-05 15:37:35 -070050 fBlobIdCache.foreach(delete_hash_map_entry<uint32_t, TextBlob*>);
51 fBlobKeyCache.foreach(delete_hash_table_entry<TextBlob*>);
kkinnunenc6cb56f2014-06-24 00:12:27 -070052}
53
cdaltoncdd79072015-10-05 15:37:35 -070054bool GrStencilAndCoverTextContext::internalCanDraw(const SkPaint& skPaint) {
cdaltone68f7362015-03-25 14:02:37 -070055 if (skPaint.getRasterizer()) {
jvanverth0fedb192014-10-08 09:07:27 -070056 return false;
57 }
cdaltone68f7362015-03-25 14:02:37 -070058 if (skPaint.getMaskFilter()) {
jvanverth0fedb192014-10-08 09:07:27 -070059 return false;
60 }
kkinnunen50b58e62015-05-18 23:02:07 -070061 if (SkPathEffect* pe = skPaint.getPathEffect()) {
halcanary96fcdcc2015-08-27 07:41:13 -070062 if (pe->asADash(nullptr) != SkPathEffect::kDash_DashType) {
kkinnunen50b58e62015-05-18 23:02:07 -070063 return false;
64 }
jvanverth0fedb192014-10-08 09:07:27 -070065 }
cdalton7d5c9502015-10-03 13:28:35 -070066 // No hairlines. They would require new paths with customized strokes for every new draw matrix.
67 return SkPaint::kStroke_Style != skPaint.getStyle() || 0 != skPaint.getStrokeWidth();
jvanverth0fedb192014-10-08 09:07:27 -070068}
69
Brian Osman11052242016-10-27 14:47:55 -040070void GrStencilAndCoverTextContext::drawText(GrContext* context, GrRenderTargetContext* rtc,
Brian Salomon6f1d36c2017-01-13 12:02:17 -050071 const GrClip& clip, const SkPaint& skPaint,
72 const SkMatrix& viewMatrix, const SkSurfaceProps& props,
73 const char text[], size_t byteLength, SkScalar x,
74 SkScalar y, const SkIRect& clipBounds) {
joshualitt27004b72016-02-11 12:00:33 -080075 if (context->abandoned()) {
joshualitte55750e2016-02-10 12:52:21 -080076 return;
77 } else if (this->canDraw(skPaint, viewMatrix)) {
kkinnunen68c63b32016-03-04 00:12:33 -080078 if (skPaint.getTextSize() > 0) {
79 TextRun run(skPaint);
kkinnunen68c63b32016-03-04 00:12:33 -080080 run.setText(text, byteLength, x, y);
Brian Salomon6f1d36c2017-01-13 12:02:17 -050081 run.draw(context, rtc, clip, viewMatrix, props, 0, 0, clipBounds, fFallbackTextContext,
82 skPaint);
kkinnunen68c63b32016-03-04 00:12:33 -080083 }
joshualitte55750e2016-02-10 12:52:21 -080084 return;
joshualitt27004b72016-02-11 12:00:33 -080085 } else if (fFallbackTextContext->canDraw(skPaint, viewMatrix, props,
86 *context->caps()->shaderCaps())) {
Brian Salomon6f1d36c2017-01-13 12:02:17 -050087 fFallbackTextContext->drawText(context, rtc, clip, skPaint, viewMatrix, props, text,
88 byteLength, x, y, clipBounds);
joshualitte55750e2016-02-10 12:52:21 -080089 return;
90 }
91
92 // fall back to drawing as a path
Brian Osman11052242016-10-27 14:47:55 -040093 GrTextUtils::DrawTextAsPath(context, rtc, clip, skPaint, viewMatrix, text, byteLength, x, y,
joshualitte55750e2016-02-10 12:52:21 -080094 clipBounds);
cdalton3bd909a2015-10-05 14:57:20 -070095}
jvanverthaab626c2014-10-16 08:04:39 -070096
Brian Osman11052242016-10-27 14:47:55 -040097void GrStencilAndCoverTextContext::drawPosText(GrContext* context, GrRenderTargetContext* rtc,
Brian Salomon6f1d36c2017-01-13 12:02:17 -050098 const GrClip& clip, const SkPaint& skPaint,
99 const SkMatrix& viewMatrix,
Brian Salomon82f44312017-01-11 13:42:54 -0500100 const SkSurfaceProps& props, const char text[],
101 size_t byteLength, const SkScalar pos[],
102 int scalarsPerPosition, const SkPoint& offset,
joshualitte55750e2016-02-10 12:52:21 -0800103 const SkIRect& clipBounds) {
joshualitt27004b72016-02-11 12:00:33 -0800104 if (context->abandoned()) {
joshualitte55750e2016-02-10 12:52:21 -0800105 return;
106 } else if (this->canDraw(skPaint, viewMatrix)) {
kkinnunen68c63b32016-03-04 00:12:33 -0800107 if (skPaint.getTextSize() > 0) {
108 TextRun run(skPaint);
kkinnunen68c63b32016-03-04 00:12:33 -0800109 run.setPosText(text, byteLength, pos, scalarsPerPosition, offset);
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500110 run.draw(context, rtc, clip, viewMatrix, props, 0, 0, clipBounds, fFallbackTextContext,
111 skPaint);
kkinnunen68c63b32016-03-04 00:12:33 -0800112 }
joshualitte55750e2016-02-10 12:52:21 -0800113 return;
joshualitt27004b72016-02-11 12:00:33 -0800114 } else if (fFallbackTextContext->canDraw(skPaint, viewMatrix, props,
115 *context->caps()->shaderCaps())) {
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500116 fFallbackTextContext->drawPosText(context, rtc, clip, skPaint, viewMatrix, props, text,
117 byteLength, pos, scalarsPerPosition, offset, clipBounds);
joshualitte55750e2016-02-10 12:52:21 -0800118 return;
119 }
120
121 // fall back to drawing as a path
Brian Osman11052242016-10-27 14:47:55 -0400122 GrTextUtils::DrawPosTextAsPath(context, rtc, props, clip, skPaint, viewMatrix, text,
joshualitte55750e2016-02-10 12:52:21 -0800123 byteLength, pos, scalarsPerPosition, offset, clipBounds);
124}
125
joshualitt27004b72016-02-11 12:00:33 -0800126void GrStencilAndCoverTextContext::uncachedDrawTextBlob(GrContext* context,
Brian Osman11052242016-10-27 14:47:55 -0400127 GrRenderTargetContext* rtc,
robertphillipsd2b6d642016-07-21 08:55:08 -0700128 const GrClip& clip,
129 const SkPaint& skPaint,
joshualitte55750e2016-02-10 12:52:21 -0800130 const SkMatrix& viewMatrix,
joshualitt2c89bc12016-02-11 05:42:30 -0800131 const SkSurfaceProps& props,
joshualitte55750e2016-02-10 12:52:21 -0800132 const SkTextBlob* blob,
133 SkScalar x, SkScalar y,
134 SkDrawFilter* drawFilter,
135 const SkIRect& clipBounds) {
Brian Salomonf3569f02017-10-24 12:52:33 -0400136 GrTextUtils::Paint paint(&skPaint, rtc->colorSpaceInfo());
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500137 GrTextUtils::RunPaint runPaint(&paint, drawFilter, props);
joshualitte55750e2016-02-10 12:52:21 -0800138 SkTextBlobRunIterator it(blob);
139 for (;!it.done(); it.next()) {
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500140 if (!runPaint.modifyForRun(it)) {
141 continue;
142 }
joshualitte55750e2016-02-10 12:52:21 -0800143 size_t textLen = it.glyphCount() * sizeof(uint16_t);
144 const SkPoint& offset = it.offset();
145
joshualitte55750e2016-02-10 12:52:21 -0800146 switch (it.positioning()) {
147 case SkTextBlob::kDefault_Positioning:
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500148 this->drawText(context, rtc, clip, runPaint, viewMatrix, props,
Brian Salomon82f44312017-01-11 13:42:54 -0500149 (const char*)it.glyphs(), textLen, x + offset.x(), y + offset.y(),
150 clipBounds);
joshualitte55750e2016-02-10 12:52:21 -0800151 break;
152 case SkTextBlob::kHorizontal_Positioning:
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500153 this->drawPosText(context, rtc, clip, runPaint, viewMatrix, props,
154 (const char*)it.glyphs(), textLen, it.pos(), 1,
Brian Salomon82f44312017-01-11 13:42:54 -0500155 SkPoint::Make(x, y + offset.y()), clipBounds);
joshualitte55750e2016-02-10 12:52:21 -0800156 break;
157 case SkTextBlob::kFull_Positioning:
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500158 this->drawPosText(context, rtc, clip, runPaint, viewMatrix, props,
159 (const char*)it.glyphs(), textLen, it.pos(), 2,
Brian Salomon82f44312017-01-11 13:42:54 -0500160 SkPoint::Make(x, y), clipBounds);
joshualitte55750e2016-02-10 12:52:21 -0800161 break;
162 }
joshualitte55750e2016-02-10 12:52:21 -0800163 }
cdaltoncdd79072015-10-05 15:37:35 -0700164}
165
Brian Osman11052242016-10-27 14:47:55 -0400166void GrStencilAndCoverTextContext::drawTextBlob(GrContext* context, GrRenderTargetContext* rtc,
cdaltoncdd79072015-10-05 15:37:35 -0700167 const GrClip& clip, const SkPaint& skPaint,
168 const SkMatrix& viewMatrix,
joshualitt2c89bc12016-02-11 05:42:30 -0800169 const SkSurfaceProps& props,
cdaltoncdd79072015-10-05 15:37:35 -0700170 const SkTextBlob* skBlob, SkScalar x, SkScalar y,
171 SkDrawFilter* drawFilter,
172 const SkIRect& clipBounds) {
joshualitt27004b72016-02-11 12:00:33 -0800173 if (context->abandoned()) {
joshualitte55750e2016-02-10 12:52:21 -0800174 return;
175 }
176
cdaltoncdd79072015-10-05 15:37:35 -0700177 if (!this->internalCanDraw(skPaint)) {
Brian Osman11052242016-10-27 14:47:55 -0400178 fFallbackTextContext->drawTextBlob(context, rtc, clip, skPaint, viewMatrix, props, skBlob,
joshualitt27004b72016-02-11 12:00:33 -0800179 x, y, drawFilter, clipBounds);
cdaltoncdd79072015-10-05 15:37:35 -0700180 return;
181 }
182
183 if (drawFilter || skPaint.getPathEffect()) {
184 // This draw can't be cached.
Brian Osman11052242016-10-27 14:47:55 -0400185 this->uncachedDrawTextBlob(context, rtc, clip, skPaint, viewMatrix, props, skBlob, x, y,
joshualitt27004b72016-02-11 12:00:33 -0800186 drawFilter, clipBounds);
cdaltoncdd79072015-10-05 15:37:35 -0700187 return;
188 }
189
cdaltoncdd79072015-10-05 15:37:35 -0700190 const TextBlob& blob = this->findOrCreateTextBlob(skBlob, skPaint);
cdaltoncdd79072015-10-05 15:37:35 -0700191
192 TextBlob::Iter iter(blob);
Brian Salomon82f44312017-01-11 13:42:54 -0500193 for (TextRun *run = iter.get(), *nextRun; run; run = nextRun) {
194 nextRun = iter.next();
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500195 run->draw(context, rtc, clip, viewMatrix, props, x, y, clipBounds, fFallbackTextContext,
196 skPaint);
cdalton8585dd22015-10-08 08:04:09 -0700197 run->releaseGlyphCache();
cdaltoncdd79072015-10-05 15:37:35 -0700198 }
199}
200
bsalomon6663acf2016-05-10 09:14:17 -0700201static inline int style_key_cnt(const GrStyle& style) {
202 int cnt = GrStyle::KeySize(style, GrStyle::Apply::kPathEffectAndStrokeRec);
203 // We should be able to make a key because we filtered out arbitrary path effects.
204 SkASSERT(cnt > 0);
205 return cnt;
206}
207
208static inline void write_style_key(uint32_t* dst, const GrStyle& style) {
209 // Pass 1 for the scale since the GPU will apply the style not GrStyle::applyToPath().
210 GrStyle::WriteKey(dst, style, GrStyle::Apply::kPathEffectAndStrokeRec, SK_Scalar1);
211}
212
cdaltoncdd79072015-10-05 15:37:35 -0700213const GrStencilAndCoverTextContext::TextBlob&
214GrStencilAndCoverTextContext::findOrCreateTextBlob(const SkTextBlob* skBlob,
215 const SkPaint& skPaint) {
216 // The font-related parameters are baked into the text blob and will override this skPaint, so
217 // the only remaining properties that can affect a TextBlob are the ones related to stroke.
218 if (SkPaint::kFill_Style == skPaint.getStyle()) { // Fast path.
219 if (TextBlob** found = fBlobIdCache.find(skBlob->uniqueID())) {
220 fLRUList.remove(*found);
221 fLRUList.addToTail(*found);
222 return **found;
223 }
cdalton8585dd22015-10-08 08:04:09 -0700224 TextBlob* blob = new TextBlob(skBlob->uniqueID(), skBlob, skPaint);
cdaltoncdd79072015-10-05 15:37:35 -0700225 this->purgeToFit(*blob);
226 fBlobIdCache.set(skBlob->uniqueID(), blob);
227 fLRUList.addToTail(blob);
228 fCacheSize += blob->cpuMemorySize();
229 return *blob;
230 } else {
bsalomon6663acf2016-05-10 09:14:17 -0700231 GrStyle style(skPaint);
cdaltoncdd79072015-10-05 15:37:35 -0700232 SkSTArray<4, uint32_t, true> key;
bsalomon6663acf2016-05-10 09:14:17 -0700233 key.reset(1 + style_key_cnt(style));
cdaltoncdd79072015-10-05 15:37:35 -0700234 key[0] = skBlob->uniqueID();
bsalomon6663acf2016-05-10 09:14:17 -0700235 write_style_key(&key[1], style);
cdaltoncdd79072015-10-05 15:37:35 -0700236 if (TextBlob** found = fBlobKeyCache.find(key)) {
237 fLRUList.remove(*found);
238 fLRUList.addToTail(*found);
239 return **found;
240 }
cdalton8585dd22015-10-08 08:04:09 -0700241 TextBlob* blob = new TextBlob(key, skBlob, skPaint);
cdaltoncdd79072015-10-05 15:37:35 -0700242 this->purgeToFit(*blob);
243 fBlobKeyCache.set(blob);
244 fLRUList.addToTail(blob);
245 fCacheSize += blob->cpuMemorySize();
246 return *blob;
247 }
248}
249
250void GrStencilAndCoverTextContext::purgeToFit(const TextBlob& blob) {
cdalton8585dd22015-10-08 08:04:09 -0700251 static const size_t maxCacheSize = 4 * 1024 * 1024; // Allow up to 4 MB for caching text blobs.
cdaltoncdd79072015-10-05 15:37:35 -0700252
cdalton8585dd22015-10-08 08:04:09 -0700253 size_t maxSizeForNewBlob = maxCacheSize - blob.cpuMemorySize();
cdaltoncdd79072015-10-05 15:37:35 -0700254 while (fCacheSize && fCacheSize > maxSizeForNewBlob) {
255 TextBlob* lru = fLRUList.head();
256 if (1 == lru->key().count()) {
257 // 1-length keys are unterstood to be the blob id.
258 fBlobIdCache.remove(lru->key()[0]);
259 } else {
260 fBlobKeyCache.remove(lru->key());
261 }
262 fLRUList.remove(lru);
263 fCacheSize -= lru->cpuMemorySize();
264 delete lru;
265 }
266}
267
268////////////////////////////////////////////////////////////////////////////////////////////////////
269
cdalton8585dd22015-10-08 08:04:09 -0700270void GrStencilAndCoverTextContext::TextBlob::init(const SkTextBlob* skBlob,
271 const SkPaint& skPaint) {
cdaltoncdd79072015-10-05 15:37:35 -0700272 fCpuMemorySize = sizeof(TextBlob);
273 SkPaint runPaint(skPaint);
halcanary33779752015-10-27 14:01:05 -0700274 for (SkTextBlobRunIterator iter(skBlob); !iter.done(); iter.next()) {
cdaltoncdd79072015-10-05 15:37:35 -0700275 iter.applyFontToPaint(&runPaint); // No need to re-seed the paint.
kkinnunen68c63b32016-03-04 00:12:33 -0800276 if (runPaint.getTextSize() <= 0) {
277 continue;
278 }
bsalomon5aaef1f2015-11-18 14:11:08 -0800279 TextRun* run = this->addToTail(runPaint);
cdaltoncdd79072015-10-05 15:37:35 -0700280
281 const char* text = reinterpret_cast<const char*>(iter.glyphs());
282 size_t byteLength = sizeof(uint16_t) * iter.glyphCount();
283 const SkPoint& runOffset = iter.offset();
284
285 switch (iter.positioning()) {
286 case SkTextBlob::kDefault_Positioning:
cdalton8585dd22015-10-08 08:04:09 -0700287 run->setText(text, byteLength, runOffset.fX, runOffset.fY);
cdaltoncdd79072015-10-05 15:37:35 -0700288 break;
289 case SkTextBlob::kHorizontal_Positioning:
cdalton8585dd22015-10-08 08:04:09 -0700290 run->setPosText(text, byteLength, iter.pos(), 1, SkPoint::Make(0, runOffset.fY));
cdaltoncdd79072015-10-05 15:37:35 -0700291 break;
292 case SkTextBlob::kFull_Positioning:
cdalton8585dd22015-10-08 08:04:09 -0700293 run->setPosText(text, byteLength, iter.pos(), 2, SkPoint::Make(0, 0));
cdaltoncdd79072015-10-05 15:37:35 -0700294 break;
295 }
296
cdalton8585dd22015-10-08 08:04:09 -0700297 fCpuMemorySize += run->computeSizeInCache();
cdaltoncdd79072015-10-05 15:37:35 -0700298 }
cdalton3bd909a2015-10-05 14:57:20 -0700299}
300
301////////////////////////////////////////////////////////////////////////////////////////////////////
302
cdalton02015e52015-10-05 15:28:20 -0700303class GrStencilAndCoverTextContext::FallbackBlobBuilder {
304public:
cdaltoncdd46822015-12-08 10:48:31 -0800305 FallbackBlobBuilder() : fBuffIdx(0), fCount(0) {}
cdalton02015e52015-10-05 15:28:20 -0700306
mtklein5f939ab2016-03-16 10:28:35 -0700307 bool isInitialized() const { return fBuilder != nullptr; }
cdalton02015e52015-10-05 15:28:20 -0700308
309 void init(const SkPaint& font, SkScalar textRatio);
310
311 void appendGlyph(uint16_t glyphId, const SkPoint& pos);
312
fmalita37283c22016-09-13 10:00:23 -0700313 sk_sp<SkTextBlob> makeIfNeeded(int* count);
cdalton02015e52015-10-05 15:28:20 -0700314
315private:
316 enum { kWriteBufferSize = 1024 };
317
318 void flush();
319
Ben Wagner145dbcd2016-11-03 14:40:50 -0400320 std::unique_ptr<SkTextBlobBuilder> fBuilder;
cdalton02015e52015-10-05 15:28:20 -0700321 SkPaint fFont;
322 int fBuffIdx;
cdaltoncdd46822015-12-08 10:48:31 -0800323 int fCount;
cdalton02015e52015-10-05 15:28:20 -0700324 uint16_t fGlyphIds[kWriteBufferSize];
325 SkPoint fPositions[kWriteBufferSize];
326};
327
328////////////////////////////////////////////////////////////////////////////////////////////////////
329
cdalton3bd909a2015-10-05 14:57:20 -0700330GrStencilAndCoverTextContext::TextRun::TextRun(const SkPaint& fontAndStroke)
robertphillipsecbc15d2016-07-19 14:04:39 -0700331 : fStyle(fontAndStroke)
332 , fFont(fontAndStroke)
333 , fTotalGlyphCount(0)
334 , fFallbackGlyphCount(0)
335 , fDetachedGlyphCache(nullptr)
336 , fLastDrawnGlyphsID(SK_InvalidUniqueID) {
kkinnunen68c63b32016-03-04 00:12:33 -0800337 SkASSERT(fFont.getTextSize() > 0);
bsalomon6663acf2016-05-10 09:14:17 -0700338 SkASSERT(!fStyle.hasNonDashPathEffect()); // Arbitrary path effects not supported.
339 SkASSERT(!fStyle.isSimpleHairline()); // Hairlines are not supported.
cdalton3bd909a2015-10-05 14:57:20 -0700340
341 // Setting to "fill" ensures that no strokes get baked into font outlines. (We use the GPU path
342 // rendering API for stroking).
343 fFont.setStyle(SkPaint::kFill_Style);
344
bsalomon6663acf2016-05-10 09:14:17 -0700345 if (fFont.isFakeBoldText() && fStyle.isSimpleFill()) {
346 const SkStrokeRec& stroke = fStyle.strokeRec();
cdalton3bd909a2015-10-05 14:57:20 -0700347 // Instead of letting fake bold get baked into the glyph outlines, do it with GPU stroke.
348 SkScalar fakeBoldScale = SkScalarInterpFunc(fFont.getTextSize(),
349 kStdFakeBoldInterpKeys,
350 kStdFakeBoldInterpValues,
351 kStdFakeBoldInterpLength);
Mike Reed8be952a2017-02-13 20:44:33 -0500352 SkScalar extra = fFont.getTextSize() * fakeBoldScale;
cdalton3bd909a2015-10-05 14:57:20 -0700353
bsalomon6663acf2016-05-10 09:14:17 -0700354 SkStrokeRec strokeRec(SkStrokeRec::kFill_InitStyle);
355 strokeRec.setStrokeStyle(stroke.needToApply() ? stroke.getWidth() + extra : extra,
356 true /*strokeAndFill*/);
Robert Phillipsf809c1e2017-01-13 11:02:42 -0500357 fStyle = GrStyle(strokeRec, fStyle.refPathEffect());
cdalton3bd909a2015-10-05 14:57:20 -0700358 fFont.setFakeBoldText(false);
jvanverthaab626c2014-10-16 08:04:39 -0700359 }
360
bsalomon6663acf2016-05-10 09:14:17 -0700361 if (!fFont.getPathEffect() && !fStyle.isDashed()) {
362 const SkStrokeRec& stroke = fStyle.strokeRec();
cdalton3bd909a2015-10-05 14:57:20 -0700363 // We can draw the glyphs from canonically sized paths.
364 fTextRatio = fFont.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
365 fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fFont.getTextSize();
jvanverthaab626c2014-10-16 08:04:39 -0700366
cdalton3bd909a2015-10-05 14:57:20 -0700367 // Compensate for the glyphs being scaled by fTextRatio.
bsalomon6663acf2016-05-10 09:14:17 -0700368 if (!fStyle.isSimpleFill()) {
369 SkStrokeRec strokeRec(SkStrokeRec::kFill_InitStyle);
370 strokeRec.setStrokeStyle(stroke.getWidth() / fTextRatio,
371 SkStrokeRec::kStrokeAndFill_Style == stroke.getStyle());
Robert Phillipsf809c1e2017-01-13 11:02:42 -0500372 fStyle = GrStyle(strokeRec, fStyle.refPathEffect());
cdalton3bd909a2015-10-05 14:57:20 -0700373 }
374
375 fFont.setLinearText(true);
376 fFont.setLCDRenderText(false);
377 fFont.setAutohinted(false);
378 fFont.setHinting(SkPaint::kNo_Hinting);
379 fFont.setSubpixelText(true);
380 fFont.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
381
382 fUsingRawGlyphPaths = SK_Scalar1 == fFont.getTextScaleX() &&
383 0 == fFont.getTextSkewX() &&
384 !fFont.isFakeBoldText() &&
385 !fFont.isVerticalText();
386 } else {
387 fTextRatio = fTextInverseRatio = 1.0f;
388 fUsingRawGlyphPaths = false;
389 }
390
cdalton8585dd22015-10-08 08:04:09 -0700391 // Generate the key that will be used to cache the GPU glyph path objects.
bsalomon6663acf2016-05-10 09:14:17 -0700392 if (fUsingRawGlyphPaths && fStyle.isSimpleFill()) {
cdalton8585dd22015-10-08 08:04:09 -0700393 static const GrUniqueKey::Domain kRawFillPathGlyphDomain = GrUniqueKey::GenerateDomain();
394
395 const SkTypeface* typeface = fFont.getTypeface();
396 GrUniqueKey::Builder builder(&fGlyphPathsKey, kRawFillPathGlyphDomain, 1);
397 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
398 } else {
399 static const GrUniqueKey::Domain kPathGlyphDomain = GrUniqueKey::GenerateDomain();
400
bsalomon6663acf2016-05-10 09:14:17 -0700401 int styleDataCount = GrStyle::KeySize(fStyle, GrStyle::Apply::kPathEffectAndStrokeRec);
402 // Key should be valid since we opted out of drawing arbitrary path effects.
403 SkASSERT(styleDataCount >= 0);
cdalton8585dd22015-10-08 08:04:09 -0700404 if (fUsingRawGlyphPaths) {
405 const SkTypeface* typeface = fFont.getTypeface();
bsalomon6663acf2016-05-10 09:14:17 -0700406 GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain, 2 + styleDataCount);
cdalton8585dd22015-10-08 08:04:09 -0700407 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
bsalomon6663acf2016-05-10 09:14:17 -0700408 reinterpret_cast<uint32_t&>(builder[1]) = styleDataCount;
409 if (styleDataCount) {
410 write_style_key(&builder[2], fStyle);
411 }
cdalton8585dd22015-10-08 08:04:09 -0700412 } else {
413 SkGlyphCache* glyphCache = this->getGlyphCache();
414 const SkTypeface* typeface = glyphCache->getScalerContext()->getTypeface();
415 const SkDescriptor* desc = &glyphCache->getDescriptor();
416 int descDataCount = (desc->getLength() + 3) / 4;
417 GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain,
bsalomon6663acf2016-05-10 09:14:17 -0700418 2 + styleDataCount + descDataCount);
cdalton8585dd22015-10-08 08:04:09 -0700419 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
bsalomon6663acf2016-05-10 09:14:17 -0700420 reinterpret_cast<uint32_t&>(builder[1]) = styleDataCount | (descDataCount << 16);
421 if (styleDataCount) {
422 write_style_key(&builder[2], fStyle);
423 }
424 memcpy(&builder[2 + styleDataCount], desc, desc->getLength());
cdalton8585dd22015-10-08 08:04:09 -0700425 }
426 }
cdalton3bd909a2015-10-05 14:57:20 -0700427}
428
429GrStencilAndCoverTextContext::TextRun::~TextRun() {
cdalton8585dd22015-10-08 08:04:09 -0700430 this->releaseGlyphCache();
cdalton3bd909a2015-10-05 14:57:20 -0700431}
432
433void GrStencilAndCoverTextContext::TextRun::setText(const char text[], size_t byteLength,
cdalton8585dd22015-10-08 08:04:09 -0700434 SkScalar x, SkScalar y) {
cdalton3bd909a2015-10-05 14:57:20 -0700435 SkASSERT(byteLength == 0 || text != nullptr);
436
cdalton8585dd22015-10-08 08:04:09 -0700437 SkGlyphCache* glyphCache = this->getGlyphCache();
robertphillipse34f17d2016-07-19 07:59:22 -0700438 SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(fFont.getTextEncoding(),
439 fFont.isDevKernText(),
440 true);
jvanverthaab626c2014-10-16 08:04:39 -0700441
cdaltoncdd46822015-12-08 10:48:31 -0800442 fTotalGlyphCount = fFont.countText(text, byteLength);
443 fInstanceData.reset(InstanceData::Alloc(GrPathRendering::kTranslate_PathTransformType,
444 fTotalGlyphCount));
cdalton8585dd22015-10-08 08:04:09 -0700445
jvanverthaab626c2014-10-16 08:04:39 -0700446 const char* stop = text + byteLength;
447
448 // Measure first if needed.
cdalton3bd909a2015-10-05 14:57:20 -0700449 if (fFont.getTextAlign() != SkPaint::kLeft_Align) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700450 SkScalar stopX = 0;
451 SkScalar stopY = 0;
jvanverthaab626c2014-10-16 08:04:39 -0700452
453 const char* textPtr = text;
454 while (textPtr < stop) {
455 // We don't need x, y here, since all subpixel variants will have the
456 // same advance.
benjaminwagnerd936f632016-02-23 10:44:31 -0800457 const SkGlyph& glyph = glyphCacheProc(glyphCache, &textPtr);
jvanverthaab626c2014-10-16 08:04:39 -0700458
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700459 stopX += SkFloatToScalar(glyph.fAdvanceX);
460 stopY += SkFloatToScalar(glyph.fAdvanceY);
jvanverthaab626c2014-10-16 08:04:39 -0700461 }
462 SkASSERT(textPtr == stop);
463
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700464 SkScalar alignX = stopX * fTextRatio;
465 SkScalar alignY = stopY * fTextRatio;
jvanverthaab626c2014-10-16 08:04:39 -0700466
cdalton3bd909a2015-10-05 14:57:20 -0700467 if (fFont.getTextAlign() == SkPaint::kCenter_Align) {
jvanverthaab626c2014-10-16 08:04:39 -0700468 alignX = SkScalarHalf(alignX);
469 alignY = SkScalarHalf(alignY);
470 }
471
472 x -= alignX;
473 y -= alignY;
474 }
475
476 SkAutoKern autokern;
477
cdalton02015e52015-10-05 15:28:20 -0700478 FallbackBlobBuilder fallback;
jvanverthaab626c2014-10-16 08:04:39 -0700479 while (text < stop) {
benjaminwagnerd936f632016-02-23 10:44:31 -0800480 const SkGlyph& glyph = glyphCacheProc(glyphCache, &text);
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700481 x += autokern.adjust(glyph) * fTextRatio;
jvanverthaab626c2014-10-16 08:04:39 -0700482 if (glyph.fWidth) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700483 this->appendGlyph(glyph, SkPoint::Make(x, y), &fallback);
jvanverthaab626c2014-10-16 08:04:39 -0700484 }
485
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700486 x += SkFloatToScalar(glyph.fAdvanceX) * fTextRatio;
487 y += SkFloatToScalar(glyph.fAdvanceY) * fTextRatio;
jvanverthaab626c2014-10-16 08:04:39 -0700488 }
cdalton02015e52015-10-05 15:28:20 -0700489
fmalita37283c22016-09-13 10:00:23 -0700490 fFallbackTextBlob = fallback.makeIfNeeded(&fFallbackGlyphCount);
jvanverthaab626c2014-10-16 08:04:39 -0700491}
492
cdalton3bd909a2015-10-05 14:57:20 -0700493void GrStencilAndCoverTextContext::TextRun::setPosText(const char text[], size_t byteLength,
494 const SkScalar pos[], int scalarsPerPosition,
cdalton8585dd22015-10-08 08:04:09 -0700495 const SkPoint& offset) {
halcanary96fcdcc2015-08-27 07:41:13 -0700496 SkASSERT(byteLength == 0 || text != nullptr);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700497 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
498
cdalton8585dd22015-10-08 08:04:09 -0700499 SkGlyphCache* glyphCache = this->getGlyphCache();
robertphillipse34f17d2016-07-19 07:59:22 -0700500 SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(fFont.getTextEncoding(),
501 fFont.isDevKernText(),
502 true);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700503
cdaltoncdd46822015-12-08 10:48:31 -0800504 fTotalGlyphCount = fFont.countText(text, byteLength);
505 fInstanceData.reset(InstanceData::Alloc(GrPathRendering::kTranslate_PathTransformType,
506 fTotalGlyphCount));
cdalton8585dd22015-10-08 08:04:09 -0700507
kkinnunenc6cb56f2014-06-24 00:12:27 -0700508 const char* stop = text + byteLength;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700509
cdalton38e13ad2014-11-07 06:02:15 -0800510 SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
cdalton3bd909a2015-10-05 14:57:20 -0700511 SkTextAlignProc alignProc(fFont.getTextAlign());
cdalton02015e52015-10-05 15:28:20 -0700512 FallbackBlobBuilder fallback;
cdalton38e13ad2014-11-07 06:02:15 -0800513 while (text < stop) {
benjaminwagnerd936f632016-02-23 10:44:31 -0800514 const SkGlyph& glyph = glyphCacheProc(glyphCache, &text);
cdalton38e13ad2014-11-07 06:02:15 -0800515 if (glyph.fWidth) {
516 SkPoint tmsLoc;
517 tmsProc(pos, &tmsLoc);
518 SkPoint loc;
519 alignProc(tmsLoc, glyph, &loc);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700520
cdalton02015e52015-10-05 15:28:20 -0700521 this->appendGlyph(glyph, loc, &fallback);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700522 }
cdalton38e13ad2014-11-07 06:02:15 -0800523 pos += scalarsPerPosition;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700524 }
cdalton02015e52015-10-05 15:28:20 -0700525
fmalita37283c22016-09-13 10:00:23 -0700526 fFallbackTextBlob = fallback.makeIfNeeded(&fFallbackGlyphCount);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700527}
528
Robert Phillips67d52cf2017-06-05 13:38:13 -0400529sk_sp<GrPathRange> GrStencilAndCoverTextContext::TextRun::createGlyphs(
Robert Phillips26c90e02017-03-14 14:39:29 -0400530 GrResourceProvider* resourceProvider) const {
Brian Salomond28a79d2017-10-16 13:01:07 -0400531 sk_sp<GrPathRange> glyphs =
532 resourceProvider->findByUniqueKey<GrPathRange>(fGlyphPathsKey);
Robert Phillips67d52cf2017-06-05 13:38:13 -0400533 if (!glyphs) {
cdalton8585dd22015-10-08 08:04:09 -0700534 if (fUsingRawGlyphPaths) {
reeda9322c22016-04-12 06:47:05 -0700535 SkScalerContextEffects noeffects;
Robert Phillips26c90e02017-03-14 14:39:29 -0400536 glyphs = resourceProvider->createGlyphs(fFont.getTypeface(), noeffects,
537 nullptr, fStyle);
cdalton8585dd22015-10-08 08:04:09 -0700538 } else {
539 SkGlyphCache* cache = this->getGlyphCache();
Robert Phillips26c90e02017-03-14 14:39:29 -0400540 glyphs = resourceProvider->createGlyphs(cache->getScalerContext()->getTypeface(),
541 cache->getScalerContext()->getEffects(),
542 &cache->getDescriptor(),
543 fStyle);
cdalton8585dd22015-10-08 08:04:09 -0700544 }
Robert Phillips67d52cf2017-06-05 13:38:13 -0400545 resourceProvider->assignUniqueKeyToResource(fGlyphPathsKey, glyphs.get());
cdalton855d83f2014-09-18 13:51:53 -0700546 }
cdalton8585dd22015-10-08 08:04:09 -0700547 return glyphs;
cdalton855d83f2014-09-18 13:51:53 -0700548}
549
cdalton3bd909a2015-10-05 14:57:20 -0700550inline void GrStencilAndCoverTextContext::TextRun::appendGlyph(const SkGlyph& glyph,
cdalton02015e52015-10-05 15:28:20 -0700551 const SkPoint& pos,
552 FallbackBlobBuilder* fallback) {
553 // Stick the glyphs we can't draw into the fallback text blob.
bsalomon1fcc01c2015-09-09 09:48:06 -0700554 if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
cdalton02015e52015-10-05 15:28:20 -0700555 if (!fallback->isInitialized()) {
556 fallback->init(fFont, fTextRatio);
557 }
558 fallback->appendGlyph(glyph.getGlyphID(), pos);
bsalomon1fcc01c2015-09-09 09:48:06 -0700559 } else {
cdaltoncdd46822015-12-08 10:48:31 -0800560 fInstanceData->append(glyph.getGlyphID(), fTextInverseRatio * pos.x(),
561 fTextInverseRatio * pos.y());
cdaltonb2808cd2014-07-25 14:13:57 -0700562 }
cdalton20b373c2014-12-01 08:38:55 -0800563}
564
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500565void GrStencilAndCoverTextContext::TextRun::draw(GrContext* ctx,
566 GrRenderTargetContext* renderTargetContext,
567 const GrClip& clip, const SkMatrix& viewMatrix,
568 const SkSurfaceProps& props, SkScalar x,
569 SkScalar y, const SkIRect& clipBounds,
570 GrAtlasTextContext* fallbackTextContext,
571 const SkPaint& originalSkPaint) const {
cdaltoncdd46822015-12-08 10:48:31 -0800572 SkASSERT(fInstanceData);
robertphillipsea461502015-05-26 11:38:03 -0700573
cdaltoncdd46822015-12-08 10:48:31 -0800574 if (fInstanceData->count()) {
Robert Phillips26c90e02017-03-14 14:39:29 -0400575 sk_sp<GrPathRange> glyphs(this->createGlyphs(ctx->resourceProvider()));
robertphillips8abb3702016-08-31 14:04:06 -0700576 if (fLastDrawnGlyphsID != glyphs->uniqueID()) {
cdalton8585dd22015-10-08 08:04:09 -0700577 // Either this is the first draw or the glyphs object was purged since last draw.
cdaltoncdd46822015-12-08 10:48:31 -0800578 glyphs->loadPathsIfNeeded(fInstanceData->indices(), fInstanceData->count());
robertphillips8abb3702016-08-31 14:04:06 -0700579 fLastDrawnGlyphsID = glyphs->uniqueID();
cdalton8585dd22015-10-08 08:04:09 -0700580 }
581
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500582 GrPaint grPaint;
Brian Salomonf3569f02017-10-24 12:52:33 -0400583 if (!SkPaintToGrPaint(ctx, renderTargetContext->colorSpaceInfo(), originalSkPaint,
584 viewMatrix, &grPaint)) {
Brian Salomon6f1d36c2017-01-13 12:02:17 -0500585 return;
586 }
587
bsalomonbf074552015-11-23 14:25:19 -0800588 // Don't compute a bounding box. For dst copy texture, we'll opt instead for it to just copy
589 // the entire dst. Realistically this is a moot point, because any context that supports
590 // NV_path_rendering will also support NV_blend_equation_advanced.
591 // For clipping we'll just skip any optimizations based on the bounds. This does, however,
Brian Salomon09d994e2016-12-21 11:14:46 -0500592 // hurt GrOp combining.
Brian Osman11052242016-10-27 14:47:55 -0400593 const SkRect bounds = SkRect::MakeIWH(renderTargetContext->width(),
594 renderTargetContext->height());
bsalomonbf074552015-11-23 14:25:19 -0800595
Brian Salomon54d212e2017-03-21 14:22:38 -0400596 // The run's "font" overrides the anti-aliasing of the passed in SkPaint!
Brian Salomon7c8460e2017-05-12 11:36:10 -0400597 GrAAType aaType = GrChooseAAType(this->aa(), renderTargetContext->fsaaType(),
Brian Salomone225b562017-06-14 13:00:03 -0400598 GrAllowMixedSamples::kYes, *renderTargetContext->caps());
Brian Salomon48d1b4c2017-04-08 07:38:53 -0400599
Brian Salomonf8334782017-01-03 09:42:58 -0500600 std::unique_ptr<GrDrawOp> op = GrDrawPathRangeOp::Make(
601 viewMatrix, fTextRatio, fTextInverseRatio * x, fTextInverseRatio * y,
Brian Salomon48d1b4c2017-04-08 07:38:53 -0400602 std::move(grPaint), GrPathRendering::kWinding_FillType, aaType, glyphs.get(),
Brian Salomonf8334782017-01-03 09:42:58 -0500603 fInstanceData.get(), bounds);
cdalton8ff8d242015-12-08 10:20:32 -0800604
Brian Salomon54d212e2017-03-21 14:22:38 -0400605 renderTargetContext->addDrawOp(clip, std::move(op));
kkinnunenc6cb56f2014-06-24 00:12:27 -0700606 }
607
cdalton02015e52015-10-05 15:28:20 -0700608 if (fFallbackTextBlob) {
cdalton3bd909a2015-10-05 14:57:20 -0700609 SkPaint fallbackSkPaint(originalSkPaint);
bsalomon6663acf2016-05-10 09:14:17 -0700610 fStyle.strokeRec().applyToPaint(&fallbackSkPaint);
611 if (!fStyle.isSimpleFill()) {
612 fallbackSkPaint.setStrokeWidth(fStyle.strokeRec().getWidth() * fTextRatio);
cdalton20b373c2014-12-01 08:38:55 -0800613 }
cdalton20b373c2014-12-01 08:38:55 -0800614
Brian Osman11052242016-10-27 14:47:55 -0400615 fallbackTextContext->drawTextBlob(ctx, renderTargetContext, clip, fallbackSkPaint,
616 viewMatrix, props, fFallbackTextBlob.get(), x, y, nullptr,
fmalita37283c22016-09-13 10:00:23 -0700617 clipBounds);
cdalton20b373c2014-12-01 08:38:55 -0800618 }
kkinnunenc6cb56f2014-06-24 00:12:27 -0700619}
cdalton02015e52015-10-05 15:28:20 -0700620
cdalton8585dd22015-10-08 08:04:09 -0700621SkGlyphCache* GrStencilAndCoverTextContext::TextRun::getGlyphCache() const {
622 if (!fDetachedGlyphCache) {
brianosmana1e8f8d2016-04-08 06:47:54 -0700623 fDetachedGlyphCache = fFont.detachCache(nullptr, SkPaint::kNone_ScalerContextFlags,
624 nullptr);
cdalton8585dd22015-10-08 08:04:09 -0700625 }
626 return fDetachedGlyphCache;
627}
628
629
630void GrStencilAndCoverTextContext::TextRun::releaseGlyphCache() const {
631 if (fDetachedGlyphCache) {
632 SkGlyphCache::AttachCache(fDetachedGlyphCache);
633 fDetachedGlyphCache = nullptr;
634 }
635}
636
637size_t GrStencilAndCoverTextContext::TextRun::computeSizeInCache() const {
cdaltoncdd46822015-12-08 10:48:31 -0800638 size_t size = sizeof(TextRun) + fGlyphPathsKey.size();
639 // The instance data always reserves enough space for every glyph.
640 size += (fTotalGlyphCount + fFallbackGlyphCount) * (sizeof(uint16_t) + 2 * sizeof(float));
641 if (fInstanceData) {
642 size += sizeof(InstanceData);
cdaltoncdd79072015-10-05 15:37:35 -0700643 }
644 if (fFallbackTextBlob) {
645 size += sizeof(SkTextBlob);
646 }
647 return size;
648}
649
cdalton02015e52015-10-05 15:28:20 -0700650////////////////////////////////////////////////////////////////////////////////////////////////////
651
652void GrStencilAndCoverTextContext::FallbackBlobBuilder::init(const SkPaint& font,
653 SkScalar textRatio) {
654 SkASSERT(!this->isInitialized());
655 fBuilder.reset(new SkTextBlobBuilder);
656 fFont = font;
657 fFont.setTextAlign(SkPaint::kLeft_Align); // The glyph positions will already account for align.
658 fFont.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
659 // No need for subpixel positioning with bitmap glyphs. TODO: revisit if non-bitmap color glyphs
660 // show up and https://code.google.com/p/skia/issues/detail?id=4408 gets resolved.
661 fFont.setSubpixelText(false);
662 fFont.setTextSize(fFont.getTextSize() * textRatio);
663 fBuffIdx = 0;
664}
665
666void GrStencilAndCoverTextContext::FallbackBlobBuilder::appendGlyph(uint16_t glyphId,
667 const SkPoint& pos) {
668 SkASSERT(this->isInitialized());
669 if (fBuffIdx >= kWriteBufferSize) {
670 this->flush();
671 }
672 fGlyphIds[fBuffIdx] = glyphId;
673 fPositions[fBuffIdx] = pos;
674 fBuffIdx++;
cdaltoncdd46822015-12-08 10:48:31 -0800675 fCount++;
cdalton02015e52015-10-05 15:28:20 -0700676}
677
678void GrStencilAndCoverTextContext::FallbackBlobBuilder::flush() {
679 SkASSERT(this->isInitialized());
680 SkASSERT(fBuffIdx <= kWriteBufferSize);
681 if (!fBuffIdx) {
682 return;
683 }
684 // This will automatically merge with previous runs since we use the same font.
685 const SkTextBlobBuilder::RunBuffer& buff = fBuilder->allocRunPos(fFont, fBuffIdx);
686 memcpy(buff.glyphs, fGlyphIds, fBuffIdx * sizeof(uint16_t));
687 memcpy(buff.pos, fPositions[0].asScalars(), fBuffIdx * 2 * sizeof(SkScalar));
688 fBuffIdx = 0;
689}
690
fmalita37283c22016-09-13 10:00:23 -0700691sk_sp<SkTextBlob> GrStencilAndCoverTextContext::FallbackBlobBuilder::makeIfNeeded(int *count) {
cdaltoncdd46822015-12-08 10:48:31 -0800692 *count = fCount;
693 if (fCount) {
694 this->flush();
fmalita37283c22016-09-13 10:00:23 -0700695 return fBuilder->make();
cdalton02015e52015-10-05 15:28:20 -0700696 }
cdaltoncdd46822015-12-08 10:48:31 -0800697 return nullptr;
cdalton02015e52015-10-05 15:28:20 -0700698}