blob: 4c2a615a663f0d318de04e70b782b400b5bc4279 [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"
robertphillipsea461502015-05-26 11:38:03 -070010#include "GrDrawContext.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070011#include "GrPath.h"
cdaltonb85a0aa2014-07-21 15:32:44 -070012#include "GrPathRange.h"
bsalomond309e7a2015-04-30 14:18:54 -070013#include "GrResourceProvider.h"
joshualitte55750e2016-02-10 12:52:21 -080014#include "GrTextUtils.h"
jvanverthaab626c2014-10-16 08:04:39 -070015#include "SkAutoKern.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070016#include "SkDraw.h"
17#include "SkDrawProcs.h"
18#include "SkGlyphCache.h"
19#include "SkGpuDevice.h"
cdaltoncdd79072015-10-05 15:37:35 -070020#include "SkGrPriv.h"
joshualitte55750e2016-02-10 12:52:21 -080021#include "SkDrawFilter.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070022#include "SkPath.h"
halcanary33779752015-10-27 14:01:05 -070023#include "SkTextBlobRunIterator.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070024#include "SkTextMapStateProc.h"
cdalton855d83f2014-09-18 13:51:53 -070025#include "SkTextFormatParams.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070026
bsalomon1fcc01c2015-09-09 09:48:06 -070027#include "batches/GrDrawPathBatch.h"
28
cdaltoncdd79072015-10-05 15:37:35 -070029template<typename Key, typename Val> static void delete_hash_map_entry(const Key&, Val* val) {
30 SkASSERT(*val);
31 delete *val;
32}
33
34template<typename T> static void delete_hash_table_entry(T* val) {
35 SkASSERT(*val);
36 delete *val;
37}
38
joshualitt27004b72016-02-11 12:00:33 -080039GrStencilAndCoverTextContext::GrStencilAndCoverTextContext()
40 : fFallbackTextContext(nullptr)
joshualitte55750e2016-02-10 12:52:21 -080041 , fCacheSize(0) {
kkinnunenc6cb56f2014-06-24 00:12:27 -070042}
43
joshualitt6e8cd962015-03-20 10:30:14 -070044GrStencilAndCoverTextContext*
joshualitt27004b72016-02-11 12:00:33 -080045GrStencilAndCoverTextContext::Create() {
46 GrStencilAndCoverTextContext* textContext = new GrStencilAndCoverTextContext();
47 textContext->fFallbackTextContext = GrAtlasTextContext::Create();
jvanverth8c27a182014-10-14 08:45:50 -070048
49 return textContext;
50}
51
kkinnunenc6cb56f2014-06-24 00:12:27 -070052GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() {
joshualitte55750e2016-02-10 12:52:21 -080053 delete fFallbackTextContext;
cdaltoncdd79072015-10-05 15:37:35 -070054 fBlobIdCache.foreach(delete_hash_map_entry<uint32_t, TextBlob*>);
55 fBlobKeyCache.foreach(delete_hash_table_entry<TextBlob*>);
kkinnunenc6cb56f2014-06-24 00:12:27 -070056}
57
cdaltoncdd79072015-10-05 15:37:35 -070058bool GrStencilAndCoverTextContext::internalCanDraw(const SkPaint& skPaint) {
cdaltone68f7362015-03-25 14:02:37 -070059 if (skPaint.getRasterizer()) {
jvanverth0fedb192014-10-08 09:07:27 -070060 return false;
61 }
cdaltone68f7362015-03-25 14:02:37 -070062 if (skPaint.getMaskFilter()) {
jvanverth0fedb192014-10-08 09:07:27 -070063 return false;
64 }
kkinnunen50b58e62015-05-18 23:02:07 -070065 if (SkPathEffect* pe = skPaint.getPathEffect()) {
halcanary96fcdcc2015-08-27 07:41:13 -070066 if (pe->asADash(nullptr) != SkPathEffect::kDash_DashType) {
kkinnunen50b58e62015-05-18 23:02:07 -070067 return false;
68 }
jvanverth0fedb192014-10-08 09:07:27 -070069 }
cdalton7d5c9502015-10-03 13:28:35 -070070 // No hairlines. They would require new paths with customized strokes for every new draw matrix.
71 return SkPaint::kStroke_Style != skPaint.getStyle() || 0 != skPaint.getStrokeWidth();
jvanverth0fedb192014-10-08 09:07:27 -070072}
73
joshualitt27004b72016-02-11 12:00:33 -080074void GrStencilAndCoverTextContext::drawText(GrContext* context, GrDrawContext* dc,
joshualitte55750e2016-02-10 12:52:21 -080075 const GrClip& clip, const GrPaint& paint,
76 const SkPaint& skPaint, const SkMatrix& viewMatrix,
joshualitt2c89bc12016-02-11 05:42:30 -080077 const SkSurfaceProps& props,
joshualitte55750e2016-02-10 12:52:21 -080078 const char text[], size_t byteLength,
79 SkScalar x, SkScalar y, const SkIRect& clipBounds) {
joshualitt27004b72016-02-11 12:00:33 -080080 if (context->abandoned()) {
joshualitte55750e2016-02-10 12:52:21 -080081 return;
82 } else if (this->canDraw(skPaint, viewMatrix)) {
kkinnunen68c63b32016-03-04 00:12:33 -080083 if (skPaint.getTextSize() > 0) {
84 TextRun run(skPaint);
85 GrPipelineBuilder pipelineBuilder(paint, dc->accessRenderTarget(), clip);
86 run.setText(text, byteLength, x, y);
87 run.draw(context, dc, &pipelineBuilder, paint.getColor(), viewMatrix, props, 0, 0,
88 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);
120 GrPipelineBuilder pipelineBuilder(paint, dc->accessRenderTarget(), clip);
121 run.setPosText(text, byteLength, pos, scalarsPerPosition, offset);
122 run.draw(context, dc, &pipelineBuilder, paint.getColor(), viewMatrix, props, 0, 0,
123 clipBounds, fFallbackTextContext, skPaint);
124 }
joshualitte55750e2016-02-10 12:52:21 -0800125 return;
joshualitt27004b72016-02-11 12:00:33 -0800126 } else if (fFallbackTextContext->canDraw(skPaint, viewMatrix, props,
127 *context->caps()->shaderCaps())) {
128 fFallbackTextContext->drawPosText(context, dc, clip, paint, skPaint, viewMatrix, props,
joshualitte55750e2016-02-10 12:52:21 -0800129 text, byteLength, pos,
130 scalarsPerPosition, offset, clipBounds);
131 return;
132 }
133
134 // fall back to drawing as a path
joshualitt27004b72016-02-11 12:00:33 -0800135 GrTextUtils::DrawPosTextAsPath(context, dc, props, clip, skPaint, viewMatrix, text,
joshualitte55750e2016-02-10 12:52:21 -0800136 byteLength, pos, scalarsPerPosition, offset, clipBounds);
137}
138
joshualitt27004b72016-02-11 12:00:33 -0800139void GrStencilAndCoverTextContext::uncachedDrawTextBlob(GrContext* context,
140 GrDrawContext* dc,
joshualitte55750e2016-02-10 12:52:21 -0800141 const GrClip& clip, const SkPaint& skPaint,
142 const SkMatrix& viewMatrix,
joshualitt2c89bc12016-02-11 05:42:30 -0800143 const SkSurfaceProps& props,
joshualitte55750e2016-02-10 12:52:21 -0800144 const SkTextBlob* blob,
145 SkScalar x, SkScalar y,
146 SkDrawFilter* drawFilter,
147 const SkIRect& clipBounds) {
148 SkPaint runPaint = skPaint;
149
150 SkTextBlobRunIterator it(blob);
151 for (;!it.done(); it.next()) {
152 size_t textLen = it.glyphCount() * sizeof(uint16_t);
153 const SkPoint& offset = it.offset();
154
155 // applyFontToPaint() always overwrites the exact same attributes,
156 // so it is safe to not re-seed the paint for this reason.
157 it.applyFontToPaint(&runPaint);
158
159 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
160 // A false return from filter() means we should abort the current draw.
161 runPaint = skPaint;
162 continue;
163 }
164
joshualitt8e84a1e2016-02-16 11:09:25 -0800165 runPaint.setFlags(GrTextUtils::FilterTextFlags(props, runPaint));
joshualitte55750e2016-02-10 12:52:21 -0800166
167 GrPaint grPaint;
joshualitt27004b72016-02-11 12:00:33 -0800168 if (!SkPaintToGrPaint(context, runPaint, viewMatrix, &grPaint)) {
joshualitte55750e2016-02-10 12:52:21 -0800169 return;
170 }
171
172 switch (it.positioning()) {
173 case SkTextBlob::kDefault_Positioning:
joshualitt27004b72016-02-11 12:00:33 -0800174 this->drawText(context, dc, clip, grPaint, runPaint, viewMatrix, props,
joshualitt2c89bc12016-02-11 05:42:30 -0800175 (const char *)it.glyphs(),
joshualitte55750e2016-02-10 12:52:21 -0800176 textLen, x + offset.x(), y + offset.y(), clipBounds);
177 break;
178 case SkTextBlob::kHorizontal_Positioning:
joshualitt27004b72016-02-11 12:00:33 -0800179 this->drawPosText(context, dc, clip, grPaint, runPaint, viewMatrix, props,
joshualitt2c89bc12016-02-11 05:42:30 -0800180 (const char*)it.glyphs(),
joshualitte55750e2016-02-10 12:52:21 -0800181 textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()),
182 clipBounds);
183 break;
184 case SkTextBlob::kFull_Positioning:
joshualitt27004b72016-02-11 12:00:33 -0800185 this->drawPosText(context, dc, clip, grPaint, runPaint, viewMatrix, props,
joshualitt2c89bc12016-02-11 05:42:30 -0800186 (const char*)it.glyphs(),
joshualitte55750e2016-02-10 12:52:21 -0800187 textLen, it.pos(), 2, SkPoint::Make(x, y), clipBounds);
188 break;
189 }
190
191 if (drawFilter) {
192 // A draw filter may change the paint arbitrarily, so we must re-seed in this case.
193 runPaint = skPaint;
194 }
195 }
cdaltoncdd79072015-10-05 15:37:35 -0700196}
197
joshualitt27004b72016-02-11 12:00:33 -0800198void GrStencilAndCoverTextContext::drawTextBlob(GrContext* context, GrDrawContext* dc,
cdaltoncdd79072015-10-05 15:37:35 -0700199 const GrClip& clip, const SkPaint& skPaint,
200 const SkMatrix& viewMatrix,
joshualitt2c89bc12016-02-11 05:42:30 -0800201 const SkSurfaceProps& props,
cdaltoncdd79072015-10-05 15:37:35 -0700202 const SkTextBlob* skBlob, SkScalar x, SkScalar y,
203 SkDrawFilter* drawFilter,
204 const SkIRect& clipBounds) {
joshualitt27004b72016-02-11 12:00:33 -0800205 if (context->abandoned()) {
joshualitte55750e2016-02-10 12:52:21 -0800206 return;
207 }
208
cdaltoncdd79072015-10-05 15:37:35 -0700209 if (!this->internalCanDraw(skPaint)) {
joshualitt27004b72016-02-11 12:00:33 -0800210 fFallbackTextContext->drawTextBlob(context, dc, clip, skPaint, viewMatrix, props, skBlob,
211 x, y, drawFilter, clipBounds);
cdaltoncdd79072015-10-05 15:37:35 -0700212 return;
213 }
214
215 if (drawFilter || skPaint.getPathEffect()) {
216 // This draw can't be cached.
joshualitt27004b72016-02-11 12:00:33 -0800217 this->uncachedDrawTextBlob(context, dc, clip, skPaint, viewMatrix, props, skBlob, x, y,
218 drawFilter, clipBounds);
cdaltoncdd79072015-10-05 15:37:35 -0700219 return;
220 }
221
222 GrPaint paint;
joshualitt27004b72016-02-11 12:00:33 -0800223 if (!SkPaintToGrPaint(context, skPaint, viewMatrix, &paint)) {
cdaltoncdd79072015-10-05 15:37:35 -0700224 return;
225 }
226
227 const TextBlob& blob = this->findOrCreateTextBlob(skBlob, skPaint);
robertphillips433625e2015-12-04 06:58:16 -0800228 GrPipelineBuilder pipelineBuilder(paint, dc->accessRenderTarget(), clip);
cdaltoncdd79072015-10-05 15:37:35 -0700229
230 TextBlob::Iter iter(blob);
231 for (TextRun* run = iter.get(); run; run = iter.next()) {
joshualitt27004b72016-02-11 12:00:33 -0800232 run->draw(context, dc, &pipelineBuilder, paint.getColor(), viewMatrix, props, x, y,
joshualitt2c89bc12016-02-11 05:42:30 -0800233 clipBounds, fFallbackTextContext, skPaint);
cdalton8585dd22015-10-08 08:04:09 -0700234 run->releaseGlyphCache();
cdaltoncdd79072015-10-05 15:37:35 -0700235 }
236}
237
238const GrStencilAndCoverTextContext::TextBlob&
239GrStencilAndCoverTextContext::findOrCreateTextBlob(const SkTextBlob* skBlob,
240 const SkPaint& skPaint) {
241 // The font-related parameters are baked into the text blob and will override this skPaint, so
242 // the only remaining properties that can affect a TextBlob are the ones related to stroke.
243 if (SkPaint::kFill_Style == skPaint.getStyle()) { // Fast path.
244 if (TextBlob** found = fBlobIdCache.find(skBlob->uniqueID())) {
245 fLRUList.remove(*found);
246 fLRUList.addToTail(*found);
247 return **found;
248 }
cdalton8585dd22015-10-08 08:04:09 -0700249 TextBlob* blob = new TextBlob(skBlob->uniqueID(), skBlob, skPaint);
cdaltoncdd79072015-10-05 15:37:35 -0700250 this->purgeToFit(*blob);
251 fBlobIdCache.set(skBlob->uniqueID(), blob);
252 fLRUList.addToTail(blob);
253 fCacheSize += blob->cpuMemorySize();
254 return *blob;
255 } else {
256 GrStrokeInfo stroke(skPaint);
257 SkSTArray<4, uint32_t, true> key;
258 key.reset(1 + stroke.computeUniqueKeyFragmentData32Cnt());
259 key[0] = skBlob->uniqueID();
260 stroke.asUniqueKeyFragment(&key[1]);
261 if (TextBlob** found = fBlobKeyCache.find(key)) {
262 fLRUList.remove(*found);
263 fLRUList.addToTail(*found);
264 return **found;
265 }
cdalton8585dd22015-10-08 08:04:09 -0700266 TextBlob* blob = new TextBlob(key, skBlob, skPaint);
cdaltoncdd79072015-10-05 15:37:35 -0700267 this->purgeToFit(*blob);
268 fBlobKeyCache.set(blob);
269 fLRUList.addToTail(blob);
270 fCacheSize += blob->cpuMemorySize();
271 return *blob;
272 }
273}
274
275void GrStencilAndCoverTextContext::purgeToFit(const TextBlob& blob) {
cdalton8585dd22015-10-08 08:04:09 -0700276 static const size_t maxCacheSize = 4 * 1024 * 1024; // Allow up to 4 MB for caching text blobs.
cdaltoncdd79072015-10-05 15:37:35 -0700277
cdalton8585dd22015-10-08 08:04:09 -0700278 size_t maxSizeForNewBlob = maxCacheSize - blob.cpuMemorySize();
cdaltoncdd79072015-10-05 15:37:35 -0700279 while (fCacheSize && fCacheSize > maxSizeForNewBlob) {
280 TextBlob* lru = fLRUList.head();
281 if (1 == lru->key().count()) {
282 // 1-length keys are unterstood to be the blob id.
283 fBlobIdCache.remove(lru->key()[0]);
284 } else {
285 fBlobKeyCache.remove(lru->key());
286 }
287 fLRUList.remove(lru);
288 fCacheSize -= lru->cpuMemorySize();
289 delete lru;
290 }
291}
292
293////////////////////////////////////////////////////////////////////////////////////////////////////
294
cdalton8585dd22015-10-08 08:04:09 -0700295void GrStencilAndCoverTextContext::TextBlob::init(const SkTextBlob* skBlob,
296 const SkPaint& skPaint) {
cdaltoncdd79072015-10-05 15:37:35 -0700297 fCpuMemorySize = sizeof(TextBlob);
298 SkPaint runPaint(skPaint);
halcanary33779752015-10-27 14:01:05 -0700299 for (SkTextBlobRunIterator iter(skBlob); !iter.done(); iter.next()) {
cdaltoncdd79072015-10-05 15:37:35 -0700300 iter.applyFontToPaint(&runPaint); // No need to re-seed the paint.
kkinnunen68c63b32016-03-04 00:12:33 -0800301 if (runPaint.getTextSize() <= 0) {
302 continue;
303 }
bsalomon5aaef1f2015-11-18 14:11:08 -0800304 TextRun* run = this->addToTail(runPaint);
cdaltoncdd79072015-10-05 15:37:35 -0700305
306 const char* text = reinterpret_cast<const char*>(iter.glyphs());
307 size_t byteLength = sizeof(uint16_t) * iter.glyphCount();
308 const SkPoint& runOffset = iter.offset();
309
310 switch (iter.positioning()) {
311 case SkTextBlob::kDefault_Positioning:
cdalton8585dd22015-10-08 08:04:09 -0700312 run->setText(text, byteLength, runOffset.fX, runOffset.fY);
cdaltoncdd79072015-10-05 15:37:35 -0700313 break;
314 case SkTextBlob::kHorizontal_Positioning:
cdalton8585dd22015-10-08 08:04:09 -0700315 run->setPosText(text, byteLength, iter.pos(), 1, SkPoint::Make(0, runOffset.fY));
cdaltoncdd79072015-10-05 15:37:35 -0700316 break;
317 case SkTextBlob::kFull_Positioning:
cdalton8585dd22015-10-08 08:04:09 -0700318 run->setPosText(text, byteLength, iter.pos(), 2, SkPoint::Make(0, 0));
cdaltoncdd79072015-10-05 15:37:35 -0700319 break;
320 }
321
cdalton8585dd22015-10-08 08:04:09 -0700322 fCpuMemorySize += run->computeSizeInCache();
cdaltoncdd79072015-10-05 15:37:35 -0700323 }
cdalton3bd909a2015-10-05 14:57:20 -0700324}
325
326////////////////////////////////////////////////////////////////////////////////////////////////////
327
cdalton02015e52015-10-05 15:28:20 -0700328class GrStencilAndCoverTextContext::FallbackBlobBuilder {
329public:
cdaltoncdd46822015-12-08 10:48:31 -0800330 FallbackBlobBuilder() : fBuffIdx(0), fCount(0) {}
cdalton02015e52015-10-05 15:28:20 -0700331
mtklein5f939ab2016-03-16 10:28:35 -0700332 bool isInitialized() const { return fBuilder != nullptr; }
cdalton02015e52015-10-05 15:28:20 -0700333
334 void init(const SkPaint& font, SkScalar textRatio);
335
336 void appendGlyph(uint16_t glyphId, const SkPoint& pos);
337
cdaltoncdd46822015-12-08 10:48:31 -0800338 const SkTextBlob* buildIfNeeded(int* count);
cdalton02015e52015-10-05 15:28:20 -0700339
340private:
341 enum { kWriteBufferSize = 1024 };
342
343 void flush();
344
345 SkAutoTDelete<SkTextBlobBuilder> fBuilder;
346 SkPaint fFont;
347 int fBuffIdx;
cdaltoncdd46822015-12-08 10:48:31 -0800348 int fCount;
cdalton02015e52015-10-05 15:28:20 -0700349 uint16_t fGlyphIds[kWriteBufferSize];
350 SkPoint fPositions[kWriteBufferSize];
351};
352
353////////////////////////////////////////////////////////////////////////////////////////////////////
354
cdalton3bd909a2015-10-05 14:57:20 -0700355GrStencilAndCoverTextContext::TextRun::TextRun(const SkPaint& fontAndStroke)
356 : fStroke(fontAndStroke),
cdaltoncdd79072015-10-05 15:37:35 -0700357 fFont(fontAndStroke),
cdalton8585dd22015-10-08 08:04:09 -0700358 fTotalGlyphCount(0),
cdaltoncdd46822015-12-08 10:48:31 -0800359 fFallbackGlyphCount(0),
cdalton8585dd22015-10-08 08:04:09 -0700360 fDetachedGlyphCache(nullptr),
361 fLastDrawnGlyphsID(SK_InvalidUniqueID) {
kkinnunen68c63b32016-03-04 00:12:33 -0800362 SkASSERT(fFont.getTextSize() > 0);
cdalton3bd909a2015-10-05 14:57:20 -0700363 SkASSERT(!fStroke.isHairlineStyle()); // Hairlines are not supported.
364
365 // Setting to "fill" ensures that no strokes get baked into font outlines. (We use the GPU path
366 // rendering API for stroking).
367 fFont.setStyle(SkPaint::kFill_Style);
368
369 if (fFont.isFakeBoldText() && SkStrokeRec::kStroke_Style != fStroke.getStyle()) {
370 // Instead of letting fake bold get baked into the glyph outlines, do it with GPU stroke.
371 SkScalar fakeBoldScale = SkScalarInterpFunc(fFont.getTextSize(),
372 kStdFakeBoldInterpKeys,
373 kStdFakeBoldInterpValues,
374 kStdFakeBoldInterpLength);
375 SkScalar extra = SkScalarMul(fFont.getTextSize(), fakeBoldScale);
376 fStroke.setStrokeStyle(fStroke.needToApply() ? fStroke.getWidth() + extra : extra,
377 true /*strokeAndFill*/);
378
379 fFont.setFakeBoldText(false);
jvanverthaab626c2014-10-16 08:04:39 -0700380 }
381
cdalton3bd909a2015-10-05 14:57:20 -0700382 if (!fFont.getPathEffect() && !fStroke.isDashed()) {
383 // We can draw the glyphs from canonically sized paths.
384 fTextRatio = fFont.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
385 fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fFont.getTextSize();
jvanverthaab626c2014-10-16 08:04:39 -0700386
cdalton3bd909a2015-10-05 14:57:20 -0700387 // Compensate for the glyphs being scaled by fTextRatio.
388 if (!fStroke.isFillStyle()) {
389 fStroke.setStrokeStyle(fStroke.getWidth() / fTextRatio,
390 SkStrokeRec::kStrokeAndFill_Style == fStroke.getStyle());
391 }
392
393 fFont.setLinearText(true);
394 fFont.setLCDRenderText(false);
395 fFont.setAutohinted(false);
396 fFont.setHinting(SkPaint::kNo_Hinting);
397 fFont.setSubpixelText(true);
398 fFont.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
399
400 fUsingRawGlyphPaths = SK_Scalar1 == fFont.getTextScaleX() &&
401 0 == fFont.getTextSkewX() &&
402 !fFont.isFakeBoldText() &&
403 !fFont.isVerticalText();
404 } else {
405 fTextRatio = fTextInverseRatio = 1.0f;
406 fUsingRawGlyphPaths = false;
407 }
408
cdalton8585dd22015-10-08 08:04:09 -0700409 // Generate the key that will be used to cache the GPU glyph path objects.
410 if (fUsingRawGlyphPaths && fStroke.isFillStyle()) {
411 static const GrUniqueKey::Domain kRawFillPathGlyphDomain = GrUniqueKey::GenerateDomain();
412
413 const SkTypeface* typeface = fFont.getTypeface();
414 GrUniqueKey::Builder builder(&fGlyphPathsKey, kRawFillPathGlyphDomain, 1);
415 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
416 } else {
417 static const GrUniqueKey::Domain kPathGlyphDomain = GrUniqueKey::GenerateDomain();
418
419 int strokeDataCount = fStroke.computeUniqueKeyFragmentData32Cnt();
420 if (fUsingRawGlyphPaths) {
421 const SkTypeface* typeface = fFont.getTypeface();
422 GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain, 2 + strokeDataCount);
423 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
424 reinterpret_cast<uint32_t&>(builder[1]) = strokeDataCount;
425 fStroke.asUniqueKeyFragment(&builder[2]);
426 } else {
427 SkGlyphCache* glyphCache = this->getGlyphCache();
428 const SkTypeface* typeface = glyphCache->getScalerContext()->getTypeface();
429 const SkDescriptor* desc = &glyphCache->getDescriptor();
430 int descDataCount = (desc->getLength() + 3) / 4;
431 GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain,
432 2 + strokeDataCount + descDataCount);
433 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
434 reinterpret_cast<uint32_t&>(builder[1]) = strokeDataCount | (descDataCount << 16);
435 fStroke.asUniqueKeyFragment(&builder[2]);
436 memcpy(&builder[2 + strokeDataCount], desc, desc->getLength());
437 }
438 }
cdalton3bd909a2015-10-05 14:57:20 -0700439}
440
441GrStencilAndCoverTextContext::TextRun::~TextRun() {
cdalton8585dd22015-10-08 08:04:09 -0700442 this->releaseGlyphCache();
cdalton3bd909a2015-10-05 14:57:20 -0700443}
444
445void GrStencilAndCoverTextContext::TextRun::setText(const char text[], size_t byteLength,
cdalton8585dd22015-10-08 08:04:09 -0700446 SkScalar x, SkScalar y) {
cdalton3bd909a2015-10-05 14:57:20 -0700447 SkASSERT(byteLength == 0 || text != nullptr);
448
cdalton8585dd22015-10-08 08:04:09 -0700449 SkGlyphCache* glyphCache = this->getGlyphCache();
benjaminwagnerd936f632016-02-23 10:44:31 -0800450 SkPaint::GlyphCacheProc glyphCacheProc = fFont.getGlyphCacheProc(true);
jvanverthaab626c2014-10-16 08:04:39 -0700451
cdaltoncdd46822015-12-08 10:48:31 -0800452 fTotalGlyphCount = fFont.countText(text, byteLength);
453 fInstanceData.reset(InstanceData::Alloc(GrPathRendering::kTranslate_PathTransformType,
454 fTotalGlyphCount));
cdalton8585dd22015-10-08 08:04:09 -0700455
jvanverthaab626c2014-10-16 08:04:39 -0700456 const char* stop = text + byteLength;
457
458 // Measure first if needed.
cdalton3bd909a2015-10-05 14:57:20 -0700459 if (fFont.getTextAlign() != SkPaint::kLeft_Align) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700460 SkScalar stopX = 0;
461 SkScalar stopY = 0;
jvanverthaab626c2014-10-16 08:04:39 -0700462
463 const char* textPtr = text;
464 while (textPtr < stop) {
465 // We don't need x, y here, since all subpixel variants will have the
466 // same advance.
benjaminwagnerd936f632016-02-23 10:44:31 -0800467 const SkGlyph& glyph = glyphCacheProc(glyphCache, &textPtr);
jvanverthaab626c2014-10-16 08:04:39 -0700468
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700469 stopX += SkFloatToScalar(glyph.fAdvanceX);
470 stopY += SkFloatToScalar(glyph.fAdvanceY);
jvanverthaab626c2014-10-16 08:04:39 -0700471 }
472 SkASSERT(textPtr == stop);
473
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700474 SkScalar alignX = stopX * fTextRatio;
475 SkScalar alignY = stopY * fTextRatio;
jvanverthaab626c2014-10-16 08:04:39 -0700476
cdalton3bd909a2015-10-05 14:57:20 -0700477 if (fFont.getTextAlign() == SkPaint::kCenter_Align) {
jvanverthaab626c2014-10-16 08:04:39 -0700478 alignX = SkScalarHalf(alignX);
479 alignY = SkScalarHalf(alignY);
480 }
481
482 x -= alignX;
483 y -= alignY;
484 }
485
486 SkAutoKern autokern;
487
cdalton02015e52015-10-05 15:28:20 -0700488 FallbackBlobBuilder fallback;
jvanverthaab626c2014-10-16 08:04:39 -0700489 while (text < stop) {
benjaminwagnerd936f632016-02-23 10:44:31 -0800490 const SkGlyph& glyph = glyphCacheProc(glyphCache, &text);
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700491 x += autokern.adjust(glyph) * fTextRatio;
jvanverthaab626c2014-10-16 08:04:39 -0700492 if (glyph.fWidth) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700493 this->appendGlyph(glyph, SkPoint::Make(x, y), &fallback);
jvanverthaab626c2014-10-16 08:04:39 -0700494 }
495
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700496 x += SkFloatToScalar(glyph.fAdvanceX) * fTextRatio;
497 y += SkFloatToScalar(glyph.fAdvanceY) * fTextRatio;
jvanverthaab626c2014-10-16 08:04:39 -0700498 }
cdalton02015e52015-10-05 15:28:20 -0700499
cdaltoncdd46822015-12-08 10:48:31 -0800500 fFallbackTextBlob.reset(fallback.buildIfNeeded(&fFallbackGlyphCount));
jvanverthaab626c2014-10-16 08:04:39 -0700501}
502
cdalton3bd909a2015-10-05 14:57:20 -0700503void GrStencilAndCoverTextContext::TextRun::setPosText(const char text[], size_t byteLength,
504 const SkScalar pos[], int scalarsPerPosition,
cdalton8585dd22015-10-08 08:04:09 -0700505 const SkPoint& offset) {
halcanary96fcdcc2015-08-27 07:41:13 -0700506 SkASSERT(byteLength == 0 || text != nullptr);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700507 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
508
cdalton8585dd22015-10-08 08:04:09 -0700509 SkGlyphCache* glyphCache = this->getGlyphCache();
benjaminwagnerd936f632016-02-23 10:44:31 -0800510 SkPaint::GlyphCacheProc glyphCacheProc = fFont.getGlyphCacheProc(true);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700511
cdaltoncdd46822015-12-08 10:48:31 -0800512 fTotalGlyphCount = fFont.countText(text, byteLength);
513 fInstanceData.reset(InstanceData::Alloc(GrPathRendering::kTranslate_PathTransformType,
514 fTotalGlyphCount));
cdalton8585dd22015-10-08 08:04:09 -0700515
kkinnunenc6cb56f2014-06-24 00:12:27 -0700516 const char* stop = text + byteLength;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700517
cdalton38e13ad2014-11-07 06:02:15 -0800518 SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
cdalton3bd909a2015-10-05 14:57:20 -0700519 SkTextAlignProc alignProc(fFont.getTextAlign());
cdalton02015e52015-10-05 15:28:20 -0700520 FallbackBlobBuilder fallback;
cdalton38e13ad2014-11-07 06:02:15 -0800521 while (text < stop) {
benjaminwagnerd936f632016-02-23 10:44:31 -0800522 const SkGlyph& glyph = glyphCacheProc(glyphCache, &text);
cdalton38e13ad2014-11-07 06:02:15 -0800523 if (glyph.fWidth) {
524 SkPoint tmsLoc;
525 tmsProc(pos, &tmsLoc);
526 SkPoint loc;
527 alignProc(tmsLoc, glyph, &loc);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700528
cdalton02015e52015-10-05 15:28:20 -0700529 this->appendGlyph(glyph, loc, &fallback);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700530 }
cdalton38e13ad2014-11-07 06:02:15 -0800531 pos += scalarsPerPosition;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700532 }
cdalton02015e52015-10-05 15:28:20 -0700533
cdaltoncdd46822015-12-08 10:48:31 -0800534 fFallbackTextBlob.reset(fallback.buildIfNeeded(&fFallbackGlyphCount));
kkinnunenc6cb56f2014-06-24 00:12:27 -0700535}
536
cdalton8585dd22015-10-08 08:04:09 -0700537GrPathRange* GrStencilAndCoverTextContext::TextRun::createGlyphs(GrContext* ctx) const {
538 GrPathRange* glyphs = static_cast<GrPathRange*>(
539 ctx->resourceProvider()->findAndRefResourceByUniqueKey(fGlyphPathsKey));
halcanary96fcdcc2015-08-27 07:41:13 -0700540 if (nullptr == glyphs) {
cdalton8585dd22015-10-08 08:04:09 -0700541 if (fUsingRawGlyphPaths) {
542 glyphs = ctx->resourceProvider()->createGlyphs(fFont.getTypeface(), nullptr, fStroke);
543 } else {
544 SkGlyphCache* cache = this->getGlyphCache();
545 glyphs = ctx->resourceProvider()->createGlyphs(cache->getScalerContext()->getTypeface(),
546 &cache->getDescriptor(),
547 fStroke);
548 }
549 ctx->resourceProvider()->assignUniqueKeyToResource(fGlyphPathsKey, glyphs);
cdalton855d83f2014-09-18 13:51:53 -0700550 }
cdalton8585dd22015-10-08 08:04:09 -0700551 return glyphs;
cdalton855d83f2014-09-18 13:51:53 -0700552}
553
cdalton3bd909a2015-10-05 14:57:20 -0700554inline void GrStencilAndCoverTextContext::TextRun::appendGlyph(const SkGlyph& glyph,
cdalton02015e52015-10-05 15:28:20 -0700555 const SkPoint& pos,
556 FallbackBlobBuilder* fallback) {
557 // Stick the glyphs we can't draw into the fallback text blob.
bsalomon1fcc01c2015-09-09 09:48:06 -0700558 if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
cdalton02015e52015-10-05 15:28:20 -0700559 if (!fallback->isInitialized()) {
560 fallback->init(fFont, fTextRatio);
561 }
562 fallback->appendGlyph(glyph.getGlyphID(), pos);
bsalomon1fcc01c2015-09-09 09:48:06 -0700563 } else {
cdaltoncdd46822015-12-08 10:48:31 -0800564 fInstanceData->append(glyph.getGlyphID(), fTextInverseRatio * pos.x(),
565 fTextInverseRatio * pos.y());
cdaltonb2808cd2014-07-25 14:13:57 -0700566 }
cdalton20b373c2014-12-01 08:38:55 -0800567}
568
cdalton8585dd22015-10-08 08:04:09 -0700569void GrStencilAndCoverTextContext::TextRun::draw(GrContext* ctx,
570 GrDrawContext* dc,
cdaltoncdd79072015-10-05 15:37:35 -0700571 GrPipelineBuilder* pipelineBuilder,
572 GrColor color,
cdalton3bd909a2015-10-05 14:57:20 -0700573 const SkMatrix& viewMatrix,
joshualitt2c89bc12016-02-11 05:42:30 -0800574 const SkSurfaceProps& props,
cdaltoncdd79072015-10-05 15:37:35 -0700575 SkScalar x, SkScalar y,
576 const SkIRect& clipBounds,
joshualitt8e84a1e2016-02-16 11:09:25 -0800577 GrAtlasTextContext* fallbackTextContext,
cdalton3bd909a2015-10-05 14:57:20 -0700578 const SkPaint& originalSkPaint) const {
cdaltoncdd46822015-12-08 10:48:31 -0800579 SkASSERT(fInstanceData);
robertphillips433625e2015-12-04 06:58:16 -0800580 SkASSERT(dc->accessRenderTarget()->isStencilBufferMultisampled() || !fFont.isAntiAlias());
robertphillipsea461502015-05-26 11:38:03 -0700581
cdaltoncdd46822015-12-08 10:48:31 -0800582 if (fInstanceData->count()) {
cdaltoncdd79072015-10-05 15:37:35 -0700583 pipelineBuilder->setState(GrPipelineBuilder::kHWAntialias_Flag, fFont.isAntiAlias());
joshualitt7b670db2015-07-09 13:25:02 -0700584
585 GR_STATIC_CONST_SAME_STENCIL(kStencilPass,
586 kZero_StencilOp,
vbuzinovc5d58f02015-08-21 05:24:24 -0700587 kKeep_StencilOp,
joshualitt7b670db2015-07-09 13:25:02 -0700588 kNotEqual_StencilFunc,
589 0xffff,
590 0x0000,
591 0xffff);
592
cdaltoncdd79072015-10-05 15:37:35 -0700593 *pipelineBuilder->stencil() = kStencilPass;
joshualitt7b670db2015-07-09 13:25:02 -0700594
cdalton8585dd22015-10-08 08:04:09 -0700595 SkAutoTUnref<GrPathRange> glyphs(this->createGlyphs(ctx));
596 if (fLastDrawnGlyphsID != glyphs->getUniqueID()) {
597 // Either this is the first draw or the glyphs object was purged since last draw.
cdaltoncdd46822015-12-08 10:48:31 -0800598 glyphs->loadPathsIfNeeded(fInstanceData->indices(), fInstanceData->count());
cdalton8585dd22015-10-08 08:04:09 -0700599 fLastDrawnGlyphsID = glyphs->getUniqueID();
600 }
601
bsalomonbf074552015-11-23 14:25:19 -0800602 // Don't compute a bounding box. For dst copy texture, we'll opt instead for it to just copy
603 // the entire dst. Realistically this is a moot point, because any context that supports
604 // NV_path_rendering will also support NV_blend_equation_advanced.
605 // For clipping we'll just skip any optimizations based on the bounds. This does, however,
606 // hurt batching.
607 SkRect bounds = SkRect::MakeIWH(pipelineBuilder->getRenderTarget()->width(),
608 pipelineBuilder->getRenderTarget()->height());
609
cdalton8ff8d242015-12-08 10:20:32 -0800610 SkAutoTUnref<GrDrawPathBatchBase> batch(
cdaltoncdd46822015-12-08 10:48:31 -0800611 GrDrawPathRangeBatch::Create(viewMatrix, fTextRatio, fTextInverseRatio * x,
612 fTextInverseRatio * y, color,
613 GrPathRendering::kWinding_FillType, glyphs, fInstanceData,
cdalton8ff8d242015-12-08 10:20:32 -0800614 bounds));
615
616 dc->drawPathBatch(*pipelineBuilder, batch);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700617 }
618
cdalton02015e52015-10-05 15:28:20 -0700619 if (fFallbackTextBlob) {
cdalton3bd909a2015-10-05 14:57:20 -0700620 SkPaint fallbackSkPaint(originalSkPaint);
cdalton7d5c9502015-10-03 13:28:35 -0700621 fStroke.applyToPaint(&fallbackSkPaint);
622 if (!fStroke.isFillStyle()) {
623 fallbackSkPaint.setStrokeWidth(fStroke.getWidth() * fTextRatio);
cdalton20b373c2014-12-01 08:38:55 -0800624 }
cdalton20b373c2014-12-01 08:38:55 -0800625
joshualitt27004b72016-02-11 12:00:33 -0800626 fallbackTextContext->drawTextBlob(ctx, dc, pipelineBuilder->clip(), fallbackSkPaint,
627 viewMatrix, props, fFallbackTextBlob, x, y, nullptr,
628 clipBounds);
cdalton20b373c2014-12-01 08:38:55 -0800629 }
kkinnunenc6cb56f2014-06-24 00:12:27 -0700630}
cdalton02015e52015-10-05 15:28:20 -0700631
cdalton8585dd22015-10-08 08:04:09 -0700632SkGlyphCache* GrStencilAndCoverTextContext::TextRun::getGlyphCache() const {
633 if (!fDetachedGlyphCache) {
bungemanf6d1e602016-02-22 13:20:28 -0800634 fDetachedGlyphCache = fFont.detachCache(nullptr, SkPaint::FakeGamma::Off, nullptr);
cdalton8585dd22015-10-08 08:04:09 -0700635 }
636 return fDetachedGlyphCache;
637}
638
639
640void GrStencilAndCoverTextContext::TextRun::releaseGlyphCache() const {
641 if (fDetachedGlyphCache) {
642 SkGlyphCache::AttachCache(fDetachedGlyphCache);
643 fDetachedGlyphCache = nullptr;
644 }
645}
646
647size_t GrStencilAndCoverTextContext::TextRun::computeSizeInCache() const {
cdaltoncdd46822015-12-08 10:48:31 -0800648 size_t size = sizeof(TextRun) + fGlyphPathsKey.size();
649 // The instance data always reserves enough space for every glyph.
650 size += (fTotalGlyphCount + fFallbackGlyphCount) * (sizeof(uint16_t) + 2 * sizeof(float));
651 if (fInstanceData) {
652 size += sizeof(InstanceData);
cdaltoncdd79072015-10-05 15:37:35 -0700653 }
654 if (fFallbackTextBlob) {
655 size += sizeof(SkTextBlob);
656 }
657 return size;
658}
659
cdalton02015e52015-10-05 15:28:20 -0700660////////////////////////////////////////////////////////////////////////////////////////////////////
661
662void GrStencilAndCoverTextContext::FallbackBlobBuilder::init(const SkPaint& font,
663 SkScalar textRatio) {
664 SkASSERT(!this->isInitialized());
665 fBuilder.reset(new SkTextBlobBuilder);
666 fFont = font;
667 fFont.setTextAlign(SkPaint::kLeft_Align); // The glyph positions will already account for align.
668 fFont.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
669 // No need for subpixel positioning with bitmap glyphs. TODO: revisit if non-bitmap color glyphs
670 // show up and https://code.google.com/p/skia/issues/detail?id=4408 gets resolved.
671 fFont.setSubpixelText(false);
672 fFont.setTextSize(fFont.getTextSize() * textRatio);
673 fBuffIdx = 0;
674}
675
676void GrStencilAndCoverTextContext::FallbackBlobBuilder::appendGlyph(uint16_t glyphId,
677 const SkPoint& pos) {
678 SkASSERT(this->isInitialized());
679 if (fBuffIdx >= kWriteBufferSize) {
680 this->flush();
681 }
682 fGlyphIds[fBuffIdx] = glyphId;
683 fPositions[fBuffIdx] = pos;
684 fBuffIdx++;
cdaltoncdd46822015-12-08 10:48:31 -0800685 fCount++;
cdalton02015e52015-10-05 15:28:20 -0700686}
687
688void GrStencilAndCoverTextContext::FallbackBlobBuilder::flush() {
689 SkASSERT(this->isInitialized());
690 SkASSERT(fBuffIdx <= kWriteBufferSize);
691 if (!fBuffIdx) {
692 return;
693 }
694 // This will automatically merge with previous runs since we use the same font.
695 const SkTextBlobBuilder::RunBuffer& buff = fBuilder->allocRunPos(fFont, fBuffIdx);
696 memcpy(buff.glyphs, fGlyphIds, fBuffIdx * sizeof(uint16_t));
697 memcpy(buff.pos, fPositions[0].asScalars(), fBuffIdx * 2 * sizeof(SkScalar));
698 fBuffIdx = 0;
699}
700
cdaltoncdd46822015-12-08 10:48:31 -0800701const SkTextBlob* GrStencilAndCoverTextContext::FallbackBlobBuilder::buildIfNeeded(int *count) {
702 *count = fCount;
703 if (fCount) {
704 this->flush();
705 return fBuilder->build();
cdalton02015e52015-10-05 15:28:20 -0700706 }
cdaltoncdd46822015-12-08 10:48:31 -0800707 return nullptr;
cdalton02015e52015-10-05 15:28:20 -0700708}