blob: 50ec4f23fa0cc9fb6d55839784518f6074f528f6 [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 "GrDrawTarget.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070012#include "GrPath.h"
cdaltonb85a0aa2014-07-21 15:32:44 -070013#include "GrPathRange.h"
bsalomond309e7a2015-04-30 14:18:54 -070014#include "GrResourceProvider.h"
joshualitte55750e2016-02-10 12:52:21 -080015#include "GrTextUtils.h"
jvanverthaab626c2014-10-16 08:04:39 -070016#include "SkAutoKern.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070017#include "SkDraw.h"
18#include "SkDrawProcs.h"
19#include "SkGlyphCache.h"
20#include "SkGpuDevice.h"
cdaltoncdd79072015-10-05 15:37:35 -070021#include "SkGrPriv.h"
joshualitte55750e2016-02-10 12:52:21 -080022#include "SkDrawFilter.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070023#include "SkPath.h"
halcanary33779752015-10-27 14:01:05 -070024#include "SkTextBlobRunIterator.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070025#include "SkTextMapStateProc.h"
cdalton855d83f2014-09-18 13:51:53 -070026#include "SkTextFormatParams.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070027
bsalomon1fcc01c2015-09-09 09:48:06 -070028#include "batches/GrDrawPathBatch.h"
29
cdaltoncdd79072015-10-05 15:37:35 -070030template<typename Key, typename Val> static void delete_hash_map_entry(const Key&, Val* val) {
31 SkASSERT(*val);
32 delete *val;
33}
34
35template<typename T> static void delete_hash_table_entry(T* val) {
36 SkASSERT(*val);
37 delete *val;
38}
39
joshualitt2c89bc12016-02-11 05:42:30 -080040GrStencilAndCoverTextContext::GrStencilAndCoverTextContext(GrContext* context)
41 : INHERITED(context)
joshualitte55750e2016-02-10 12:52:21 -080042 , fFallbackTextContext(nullptr)
43 , fCacheSize(0) {
kkinnunenc6cb56f2014-06-24 00:12:27 -070044}
45
joshualitt6e8cd962015-03-20 10:30:14 -070046GrStencilAndCoverTextContext*
joshualitt2c89bc12016-02-11 05:42:30 -080047GrStencilAndCoverTextContext::Create(GrContext* context) {
robertphillipsf6703fa2015-09-01 05:36:47 -070048 GrStencilAndCoverTextContext* textContext =
joshualitt2c89bc12016-02-11 05:42:30 -080049 new GrStencilAndCoverTextContext(context);
50 textContext->fFallbackTextContext = GrAtlasTextContext::Create(context);
jvanverth8c27a182014-10-14 08:45:50 -070051
52 return textContext;
53}
54
kkinnunenc6cb56f2014-06-24 00:12:27 -070055GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() {
joshualitte55750e2016-02-10 12:52:21 -080056 delete fFallbackTextContext;
cdaltoncdd79072015-10-05 15:37:35 -070057 fBlobIdCache.foreach(delete_hash_map_entry<uint32_t, TextBlob*>);
58 fBlobKeyCache.foreach(delete_hash_table_entry<TextBlob*>);
kkinnunenc6cb56f2014-06-24 00:12:27 -070059}
60
cdaltoncdd79072015-10-05 15:37:35 -070061bool GrStencilAndCoverTextContext::internalCanDraw(const SkPaint& skPaint) {
cdaltone68f7362015-03-25 14:02:37 -070062 if (skPaint.getRasterizer()) {
jvanverth0fedb192014-10-08 09:07:27 -070063 return false;
64 }
cdaltone68f7362015-03-25 14:02:37 -070065 if (skPaint.getMaskFilter()) {
jvanverth0fedb192014-10-08 09:07:27 -070066 return false;
67 }
kkinnunen50b58e62015-05-18 23:02:07 -070068 if (SkPathEffect* pe = skPaint.getPathEffect()) {
halcanary96fcdcc2015-08-27 07:41:13 -070069 if (pe->asADash(nullptr) != SkPathEffect::kDash_DashType) {
kkinnunen50b58e62015-05-18 23:02:07 -070070 return false;
71 }
jvanverth0fedb192014-10-08 09:07:27 -070072 }
cdalton7d5c9502015-10-03 13:28:35 -070073 // No hairlines. They would require new paths with customized strokes for every new draw matrix.
74 return SkPaint::kStroke_Style != skPaint.getStyle() || 0 != skPaint.getStrokeWidth();
jvanverth0fedb192014-10-08 09:07:27 -070075}
76
joshualitte55750e2016-02-10 12:52:21 -080077void GrStencilAndCoverTextContext::drawText(GrDrawContext* dc,
78 const GrClip& clip, const GrPaint& paint,
79 const SkPaint& skPaint, const SkMatrix& viewMatrix,
joshualitt2c89bc12016-02-11 05:42:30 -080080 const SkSurfaceProps& props,
joshualitte55750e2016-02-10 12:52:21 -080081 const char text[], size_t byteLength,
82 SkScalar x, SkScalar y, const SkIRect& clipBounds) {
83 if (fContext->abandoned()) {
84 return;
85 } else if (this->canDraw(skPaint, viewMatrix)) {
86 TextRun run(skPaint);
87 GrPipelineBuilder pipelineBuilder(paint, dc->accessRenderTarget(), clip);
88 run.setText(text, byteLength, x, y);
joshualitt2c89bc12016-02-11 05:42:30 -080089 run.draw(fContext, dc, &pipelineBuilder, paint.getColor(), viewMatrix, props, 0, 0,
90 clipBounds, fFallbackTextContext, skPaint);
joshualitte55750e2016-02-10 12:52:21 -080091 return;
joshualitt2c89bc12016-02-11 05:42:30 -080092 } else if (fFallbackTextContext->canDraw(skPaint, viewMatrix, props)) {
93 fFallbackTextContext->drawText(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
99 GrTextUtils::DrawTextAsPath(fContext, dc, clip, skPaint, viewMatrix, text, byteLength, x, y,
100 clipBounds);
cdalton3bd909a2015-10-05 14:57:20 -0700101}
jvanverthaab626c2014-10-16 08:04:39 -0700102
joshualitte55750e2016-02-10 12:52:21 -0800103void GrStencilAndCoverTextContext::drawPosText(GrDrawContext* dc,
104 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) {
115 if (fContext->abandoned()) {
116 return;
117 } else if (this->canDraw(skPaint, viewMatrix)) {
118 TextRun run(skPaint);
119 GrPipelineBuilder pipelineBuilder(paint, dc->accessRenderTarget(), clip);
120 run.setPosText(text, byteLength, pos, scalarsPerPosition, offset);
joshualitt2c89bc12016-02-11 05:42:30 -0800121 run.draw(fContext, dc, &pipelineBuilder, paint.getColor(), viewMatrix, props, 0, 0,
122 clipBounds, fFallbackTextContext, skPaint);
joshualitte55750e2016-02-10 12:52:21 -0800123 return;
joshualitt2c89bc12016-02-11 05:42:30 -0800124 } else if (fFallbackTextContext->canDraw(skPaint, viewMatrix, props)) {
125 fFallbackTextContext->drawPosText(dc, clip, paint, skPaint, viewMatrix, props,
joshualitte55750e2016-02-10 12:52:21 -0800126 text, byteLength, pos,
127 scalarsPerPosition, offset, clipBounds);
128 return;
129 }
130
131 // fall back to drawing as a path
joshualitt2c89bc12016-02-11 05:42:30 -0800132 GrTextUtils::DrawPosTextAsPath(fContext, dc, props, clip, skPaint, viewMatrix, text,
joshualitte55750e2016-02-10 12:52:21 -0800133 byteLength, pos, scalarsPerPosition, offset, clipBounds);
134}
135
136void GrStencilAndCoverTextContext::uncachedDrawTextBlob(GrDrawContext* dc,
137 const GrClip& clip, const SkPaint& skPaint,
138 const SkMatrix& viewMatrix,
joshualitt2c89bc12016-02-11 05:42:30 -0800139 const SkSurfaceProps& props,
joshualitte55750e2016-02-10 12:52:21 -0800140 const SkTextBlob* blob,
141 SkScalar x, SkScalar y,
142 SkDrawFilter* drawFilter,
143 const SkIRect& clipBounds) {
144 SkPaint runPaint = skPaint;
145
146 SkTextBlobRunIterator it(blob);
147 for (;!it.done(); it.next()) {
148 size_t textLen = it.glyphCount() * sizeof(uint16_t);
149 const SkPoint& offset = it.offset();
150
151 // applyFontToPaint() always overwrites the exact same attributes,
152 // so it is safe to not re-seed the paint for this reason.
153 it.applyFontToPaint(&runPaint);
154
155 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
156 // A false return from filter() means we should abort the current draw.
157 runPaint = skPaint;
158 continue;
159 }
160
joshualitt2c89bc12016-02-11 05:42:30 -0800161 runPaint.setFlags(FilterTextFlags(props, runPaint));
joshualitte55750e2016-02-10 12:52:21 -0800162
163 GrPaint grPaint;
164 if (!SkPaintToGrPaint(fContext, runPaint, viewMatrix, &grPaint)) {
165 return;
166 }
167
168 switch (it.positioning()) {
169 case SkTextBlob::kDefault_Positioning:
joshualitt2c89bc12016-02-11 05:42:30 -0800170 this->drawText(dc, clip, grPaint, runPaint, viewMatrix, props,
171 (const char *)it.glyphs(),
joshualitte55750e2016-02-10 12:52:21 -0800172 textLen, x + offset.x(), y + offset.y(), clipBounds);
173 break;
174 case SkTextBlob::kHorizontal_Positioning:
joshualitt2c89bc12016-02-11 05:42:30 -0800175 this->drawPosText(dc, clip, grPaint, runPaint, viewMatrix, props,
176 (const char*)it.glyphs(),
joshualitte55750e2016-02-10 12:52:21 -0800177 textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()),
178 clipBounds);
179 break;
180 case SkTextBlob::kFull_Positioning:
joshualitt2c89bc12016-02-11 05:42:30 -0800181 this->drawPosText(dc, clip, grPaint, runPaint, viewMatrix, props,
182 (const char*)it.glyphs(),
joshualitte55750e2016-02-10 12:52:21 -0800183 textLen, it.pos(), 2, SkPoint::Make(x, y), clipBounds);
184 break;
185 }
186
187 if (drawFilter) {
188 // A draw filter may change the paint arbitrarily, so we must re-seed in this case.
189 runPaint = skPaint;
190 }
191 }
cdaltoncdd79072015-10-05 15:37:35 -0700192}
193
robertphillips433625e2015-12-04 06:58:16 -0800194void GrStencilAndCoverTextContext::drawTextBlob(GrDrawContext* dc,
cdaltoncdd79072015-10-05 15:37:35 -0700195 const GrClip& clip, const SkPaint& skPaint,
196 const SkMatrix& viewMatrix,
joshualitt2c89bc12016-02-11 05:42:30 -0800197 const SkSurfaceProps& props,
cdaltoncdd79072015-10-05 15:37:35 -0700198 const SkTextBlob* skBlob, SkScalar x, SkScalar y,
199 SkDrawFilter* drawFilter,
200 const SkIRect& clipBounds) {
joshualitte55750e2016-02-10 12:52:21 -0800201 if (fContext->abandoned()) {
202 return;
203 }
204
cdaltoncdd79072015-10-05 15:37:35 -0700205 if (!this->internalCanDraw(skPaint)) {
joshualitt2c89bc12016-02-11 05:42:30 -0800206 fFallbackTextContext->drawTextBlob(dc, clip, skPaint, viewMatrix, props, skBlob, x, y,
cdaltoncdd79072015-10-05 15:37:35 -0700207 drawFilter, clipBounds);
208 return;
209 }
210
211 if (drawFilter || skPaint.getPathEffect()) {
212 // This draw can't be cached.
joshualitt2c89bc12016-02-11 05:42:30 -0800213 this->uncachedDrawTextBlob(dc, clip, skPaint, viewMatrix, props, skBlob, x, y, drawFilter,
joshualitte55750e2016-02-10 12:52:21 -0800214 clipBounds);
cdaltoncdd79072015-10-05 15:37:35 -0700215 return;
216 }
217
218 GrPaint paint;
219 if (!SkPaintToGrPaint(fContext, skPaint, viewMatrix, &paint)) {
220 return;
221 }
222
223 const TextBlob& blob = this->findOrCreateTextBlob(skBlob, skPaint);
robertphillips433625e2015-12-04 06:58:16 -0800224 GrPipelineBuilder pipelineBuilder(paint, dc->accessRenderTarget(), clip);
cdaltoncdd79072015-10-05 15:37:35 -0700225
226 TextBlob::Iter iter(blob);
227 for (TextRun* run = iter.get(); run; run = iter.next()) {
joshualitt2c89bc12016-02-11 05:42:30 -0800228 run->draw(fContext, dc, &pipelineBuilder, paint.getColor(), viewMatrix, props, x, y,
229 clipBounds, fFallbackTextContext, skPaint);
cdalton8585dd22015-10-08 08:04:09 -0700230 run->releaseGlyphCache();
cdaltoncdd79072015-10-05 15:37:35 -0700231 }
232}
233
234const GrStencilAndCoverTextContext::TextBlob&
235GrStencilAndCoverTextContext::findOrCreateTextBlob(const SkTextBlob* skBlob,
236 const SkPaint& skPaint) {
237 // The font-related parameters are baked into the text blob and will override this skPaint, so
238 // the only remaining properties that can affect a TextBlob are the ones related to stroke.
239 if (SkPaint::kFill_Style == skPaint.getStyle()) { // Fast path.
240 if (TextBlob** found = fBlobIdCache.find(skBlob->uniqueID())) {
241 fLRUList.remove(*found);
242 fLRUList.addToTail(*found);
243 return **found;
244 }
cdalton8585dd22015-10-08 08:04:09 -0700245 TextBlob* blob = new TextBlob(skBlob->uniqueID(), skBlob, skPaint);
cdaltoncdd79072015-10-05 15:37:35 -0700246 this->purgeToFit(*blob);
247 fBlobIdCache.set(skBlob->uniqueID(), blob);
248 fLRUList.addToTail(blob);
249 fCacheSize += blob->cpuMemorySize();
250 return *blob;
251 } else {
252 GrStrokeInfo stroke(skPaint);
253 SkSTArray<4, uint32_t, true> key;
254 key.reset(1 + stroke.computeUniqueKeyFragmentData32Cnt());
255 key[0] = skBlob->uniqueID();
256 stroke.asUniqueKeyFragment(&key[1]);
257 if (TextBlob** found = fBlobKeyCache.find(key)) {
258 fLRUList.remove(*found);
259 fLRUList.addToTail(*found);
260 return **found;
261 }
cdalton8585dd22015-10-08 08:04:09 -0700262 TextBlob* blob = new TextBlob(key, skBlob, skPaint);
cdaltoncdd79072015-10-05 15:37:35 -0700263 this->purgeToFit(*blob);
264 fBlobKeyCache.set(blob);
265 fLRUList.addToTail(blob);
266 fCacheSize += blob->cpuMemorySize();
267 return *blob;
268 }
269}
270
271void GrStencilAndCoverTextContext::purgeToFit(const TextBlob& blob) {
cdalton8585dd22015-10-08 08:04:09 -0700272 static const size_t maxCacheSize = 4 * 1024 * 1024; // Allow up to 4 MB for caching text blobs.
cdaltoncdd79072015-10-05 15:37:35 -0700273
cdalton8585dd22015-10-08 08:04:09 -0700274 size_t maxSizeForNewBlob = maxCacheSize - blob.cpuMemorySize();
cdaltoncdd79072015-10-05 15:37:35 -0700275 while (fCacheSize && fCacheSize > maxSizeForNewBlob) {
276 TextBlob* lru = fLRUList.head();
277 if (1 == lru->key().count()) {
278 // 1-length keys are unterstood to be the blob id.
279 fBlobIdCache.remove(lru->key()[0]);
280 } else {
281 fBlobKeyCache.remove(lru->key());
282 }
283 fLRUList.remove(lru);
284 fCacheSize -= lru->cpuMemorySize();
285 delete lru;
286 }
287}
288
289////////////////////////////////////////////////////////////////////////////////////////////////////
290
cdalton8585dd22015-10-08 08:04:09 -0700291void GrStencilAndCoverTextContext::TextBlob::init(const SkTextBlob* skBlob,
292 const SkPaint& skPaint) {
cdaltoncdd79072015-10-05 15:37:35 -0700293 fCpuMemorySize = sizeof(TextBlob);
294 SkPaint runPaint(skPaint);
halcanary33779752015-10-27 14:01:05 -0700295 for (SkTextBlobRunIterator iter(skBlob); !iter.done(); iter.next()) {
cdaltoncdd79072015-10-05 15:37:35 -0700296 iter.applyFontToPaint(&runPaint); // No need to re-seed the paint.
bsalomon5aaef1f2015-11-18 14:11:08 -0800297 TextRun* run = this->addToTail(runPaint);
cdaltoncdd79072015-10-05 15:37:35 -0700298
299 const char* text = reinterpret_cast<const char*>(iter.glyphs());
300 size_t byteLength = sizeof(uint16_t) * iter.glyphCount();
301 const SkPoint& runOffset = iter.offset();
302
303 switch (iter.positioning()) {
304 case SkTextBlob::kDefault_Positioning:
cdalton8585dd22015-10-08 08:04:09 -0700305 run->setText(text, byteLength, runOffset.fX, runOffset.fY);
cdaltoncdd79072015-10-05 15:37:35 -0700306 break;
307 case SkTextBlob::kHorizontal_Positioning:
cdalton8585dd22015-10-08 08:04:09 -0700308 run->setPosText(text, byteLength, iter.pos(), 1, SkPoint::Make(0, runOffset.fY));
cdaltoncdd79072015-10-05 15:37:35 -0700309 break;
310 case SkTextBlob::kFull_Positioning:
cdalton8585dd22015-10-08 08:04:09 -0700311 run->setPosText(text, byteLength, iter.pos(), 2, SkPoint::Make(0, 0));
cdaltoncdd79072015-10-05 15:37:35 -0700312 break;
313 }
314
cdalton8585dd22015-10-08 08:04:09 -0700315 fCpuMemorySize += run->computeSizeInCache();
cdaltoncdd79072015-10-05 15:37:35 -0700316 }
cdalton3bd909a2015-10-05 14:57:20 -0700317}
318
319////////////////////////////////////////////////////////////////////////////////////////////////////
320
cdalton02015e52015-10-05 15:28:20 -0700321class GrStencilAndCoverTextContext::FallbackBlobBuilder {
322public:
cdaltoncdd46822015-12-08 10:48:31 -0800323 FallbackBlobBuilder() : fBuffIdx(0), fCount(0) {}
cdalton02015e52015-10-05 15:28:20 -0700324
325 bool isInitialized() const { return SkToBool(fBuilder); }
326
327 void init(const SkPaint& font, SkScalar textRatio);
328
329 void appendGlyph(uint16_t glyphId, const SkPoint& pos);
330
cdaltoncdd46822015-12-08 10:48:31 -0800331 const SkTextBlob* buildIfNeeded(int* count);
cdalton02015e52015-10-05 15:28:20 -0700332
333private:
334 enum { kWriteBufferSize = 1024 };
335
336 void flush();
337
338 SkAutoTDelete<SkTextBlobBuilder> fBuilder;
339 SkPaint fFont;
340 int fBuffIdx;
cdaltoncdd46822015-12-08 10:48:31 -0800341 int fCount;
cdalton02015e52015-10-05 15:28:20 -0700342 uint16_t fGlyphIds[kWriteBufferSize];
343 SkPoint fPositions[kWriteBufferSize];
344};
345
346////////////////////////////////////////////////////////////////////////////////////////////////////
347
cdalton3bd909a2015-10-05 14:57:20 -0700348GrStencilAndCoverTextContext::TextRun::TextRun(const SkPaint& fontAndStroke)
349 : fStroke(fontAndStroke),
cdaltoncdd79072015-10-05 15:37:35 -0700350 fFont(fontAndStroke),
cdalton8585dd22015-10-08 08:04:09 -0700351 fTotalGlyphCount(0),
cdaltoncdd46822015-12-08 10:48:31 -0800352 fFallbackGlyphCount(0),
cdalton8585dd22015-10-08 08:04:09 -0700353 fDetachedGlyphCache(nullptr),
354 fLastDrawnGlyphsID(SK_InvalidUniqueID) {
cdalton3bd909a2015-10-05 14:57:20 -0700355 SkASSERT(!fStroke.isHairlineStyle()); // Hairlines are not supported.
356
357 // Setting to "fill" ensures that no strokes get baked into font outlines. (We use the GPU path
358 // rendering API for stroking).
359 fFont.setStyle(SkPaint::kFill_Style);
360
361 if (fFont.isFakeBoldText() && SkStrokeRec::kStroke_Style != fStroke.getStyle()) {
362 // Instead of letting fake bold get baked into the glyph outlines, do it with GPU stroke.
363 SkScalar fakeBoldScale = SkScalarInterpFunc(fFont.getTextSize(),
364 kStdFakeBoldInterpKeys,
365 kStdFakeBoldInterpValues,
366 kStdFakeBoldInterpLength);
367 SkScalar extra = SkScalarMul(fFont.getTextSize(), fakeBoldScale);
368 fStroke.setStrokeStyle(fStroke.needToApply() ? fStroke.getWidth() + extra : extra,
369 true /*strokeAndFill*/);
370
371 fFont.setFakeBoldText(false);
jvanverthaab626c2014-10-16 08:04:39 -0700372 }
373
cdalton3bd909a2015-10-05 14:57:20 -0700374 if (!fFont.getPathEffect() && !fStroke.isDashed()) {
375 // We can draw the glyphs from canonically sized paths.
376 fTextRatio = fFont.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
377 fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fFont.getTextSize();
jvanverthaab626c2014-10-16 08:04:39 -0700378
cdalton3bd909a2015-10-05 14:57:20 -0700379 // Compensate for the glyphs being scaled by fTextRatio.
380 if (!fStroke.isFillStyle()) {
381 fStroke.setStrokeStyle(fStroke.getWidth() / fTextRatio,
382 SkStrokeRec::kStrokeAndFill_Style == fStroke.getStyle());
383 }
384
385 fFont.setLinearText(true);
386 fFont.setLCDRenderText(false);
387 fFont.setAutohinted(false);
388 fFont.setHinting(SkPaint::kNo_Hinting);
389 fFont.setSubpixelText(true);
390 fFont.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
391
392 fUsingRawGlyphPaths = SK_Scalar1 == fFont.getTextScaleX() &&
393 0 == fFont.getTextSkewX() &&
394 !fFont.isFakeBoldText() &&
395 !fFont.isVerticalText();
396 } else {
397 fTextRatio = fTextInverseRatio = 1.0f;
398 fUsingRawGlyphPaths = false;
399 }
400
cdalton8585dd22015-10-08 08:04:09 -0700401 // Generate the key that will be used to cache the GPU glyph path objects.
402 if (fUsingRawGlyphPaths && fStroke.isFillStyle()) {
403 static const GrUniqueKey::Domain kRawFillPathGlyphDomain = GrUniqueKey::GenerateDomain();
404
405 const SkTypeface* typeface = fFont.getTypeface();
406 GrUniqueKey::Builder builder(&fGlyphPathsKey, kRawFillPathGlyphDomain, 1);
407 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
408 } else {
409 static const GrUniqueKey::Domain kPathGlyphDomain = GrUniqueKey::GenerateDomain();
410
411 int strokeDataCount = fStroke.computeUniqueKeyFragmentData32Cnt();
412 if (fUsingRawGlyphPaths) {
413 const SkTypeface* typeface = fFont.getTypeface();
414 GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain, 2 + strokeDataCount);
415 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
416 reinterpret_cast<uint32_t&>(builder[1]) = strokeDataCount;
417 fStroke.asUniqueKeyFragment(&builder[2]);
418 } else {
419 SkGlyphCache* glyphCache = this->getGlyphCache();
420 const SkTypeface* typeface = glyphCache->getScalerContext()->getTypeface();
421 const SkDescriptor* desc = &glyphCache->getDescriptor();
422 int descDataCount = (desc->getLength() + 3) / 4;
423 GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain,
424 2 + strokeDataCount + descDataCount);
425 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
426 reinterpret_cast<uint32_t&>(builder[1]) = strokeDataCount | (descDataCount << 16);
427 fStroke.asUniqueKeyFragment(&builder[2]);
428 memcpy(&builder[2 + strokeDataCount], desc, desc->getLength());
429 }
430 }
cdalton3bd909a2015-10-05 14:57:20 -0700431}
432
433GrStencilAndCoverTextContext::TextRun::~TextRun() {
cdalton8585dd22015-10-08 08:04:09 -0700434 this->releaseGlyphCache();
cdalton3bd909a2015-10-05 14:57:20 -0700435}
436
437void GrStencilAndCoverTextContext::TextRun::setText(const char text[], size_t byteLength,
cdalton8585dd22015-10-08 08:04:09 -0700438 SkScalar x, SkScalar y) {
cdalton3bd909a2015-10-05 14:57:20 -0700439 SkASSERT(byteLength == 0 || text != nullptr);
440
cdalton8585dd22015-10-08 08:04:09 -0700441 SkGlyphCache* glyphCache = this->getGlyphCache();
cdalton3bd909a2015-10-05 14:57:20 -0700442 SkDrawCacheProc glyphCacheProc = fFont.getDrawCacheProc();
jvanverthaab626c2014-10-16 08:04:39 -0700443
cdaltoncdd46822015-12-08 10:48:31 -0800444 fTotalGlyphCount = fFont.countText(text, byteLength);
445 fInstanceData.reset(InstanceData::Alloc(GrPathRendering::kTranslate_PathTransformType,
446 fTotalGlyphCount));
cdalton8585dd22015-10-08 08:04:09 -0700447
jvanverthaab626c2014-10-16 08:04:39 -0700448 const char* stop = text + byteLength;
449
450 // Measure first if needed.
cdalton3bd909a2015-10-05 14:57:20 -0700451 if (fFont.getTextAlign() != SkPaint::kLeft_Align) {
jvanverthaab626c2014-10-16 08:04:39 -0700452 SkFixed stopX = 0;
453 SkFixed stopY = 0;
454
455 const char* textPtr = text;
456 while (textPtr < stop) {
457 // We don't need x, y here, since all subpixel variants will have the
458 // same advance.
cdalton3bd909a2015-10-05 14:57:20 -0700459 const SkGlyph& glyph = glyphCacheProc(glyphCache, &textPtr, 0, 0);
jvanverthaab626c2014-10-16 08:04:39 -0700460
461 stopX += glyph.fAdvanceX;
462 stopY += glyph.fAdvanceY;
463 }
464 SkASSERT(textPtr == stop);
465
466 SkScalar alignX = SkFixedToScalar(stopX) * fTextRatio;
467 SkScalar alignY = SkFixedToScalar(stopY) * fTextRatio;
468
cdalton3bd909a2015-10-05 14:57:20 -0700469 if (fFont.getTextAlign() == SkPaint::kCenter_Align) {
jvanverthaab626c2014-10-16 08:04:39 -0700470 alignX = SkScalarHalf(alignX);
471 alignY = SkScalarHalf(alignY);
472 }
473
474 x -= alignX;
475 y -= alignY;
476 }
477
478 SkAutoKern autokern;
479
480 SkFixed fixedSizeRatio = SkScalarToFixed(fTextRatio);
481
482 SkFixed fx = SkScalarToFixed(x);
483 SkFixed fy = SkScalarToFixed(y);
cdalton02015e52015-10-05 15:28:20 -0700484 FallbackBlobBuilder fallback;
jvanverthaab626c2014-10-16 08:04:39 -0700485 while (text < stop) {
cdalton3bd909a2015-10-05 14:57:20 -0700486 const SkGlyph& glyph = glyphCacheProc(glyphCache, &text, 0, 0);
bungemand709ea82015-03-17 07:23:39 -0700487 fx += SkFixedMul(autokern.adjust(glyph), fixedSizeRatio);
jvanverthaab626c2014-10-16 08:04:39 -0700488 if (glyph.fWidth) {
cdalton02015e52015-10-05 15:28:20 -0700489 this->appendGlyph(glyph, SkPoint::Make(SkFixedToScalar(fx), SkFixedToScalar(fy)),
490 &fallback);
jvanverthaab626c2014-10-16 08:04:39 -0700491 }
492
bungemand709ea82015-03-17 07:23:39 -0700493 fx += SkFixedMul(glyph.fAdvanceX, fixedSizeRatio);
494 fy += SkFixedMul(glyph.fAdvanceY, fixedSizeRatio);
jvanverthaab626c2014-10-16 08:04:39 -0700495 }
cdalton02015e52015-10-05 15:28:20 -0700496
cdaltoncdd46822015-12-08 10:48:31 -0800497 fFallbackTextBlob.reset(fallback.buildIfNeeded(&fFallbackGlyphCount));
jvanverthaab626c2014-10-16 08:04:39 -0700498}
499
cdalton3bd909a2015-10-05 14:57:20 -0700500void GrStencilAndCoverTextContext::TextRun::setPosText(const char text[], size_t byteLength,
501 const SkScalar pos[], int scalarsPerPosition,
cdalton8585dd22015-10-08 08:04:09 -0700502 const SkPoint& offset) {
halcanary96fcdcc2015-08-27 07:41:13 -0700503 SkASSERT(byteLength == 0 || text != nullptr);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700504 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
505
cdalton8585dd22015-10-08 08:04:09 -0700506 SkGlyphCache* glyphCache = this->getGlyphCache();
cdalton3bd909a2015-10-05 14:57:20 -0700507 SkDrawCacheProc glyphCacheProc = fFont.getDrawCacheProc();
kkinnunenc6cb56f2014-06-24 00:12:27 -0700508
cdaltoncdd46822015-12-08 10:48:31 -0800509 fTotalGlyphCount = fFont.countText(text, byteLength);
510 fInstanceData.reset(InstanceData::Alloc(GrPathRendering::kTranslate_PathTransformType,
511 fTotalGlyphCount));
cdalton8585dd22015-10-08 08:04:09 -0700512
kkinnunenc6cb56f2014-06-24 00:12:27 -0700513 const char* stop = text + byteLength;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700514
cdalton38e13ad2014-11-07 06:02:15 -0800515 SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
cdalton3bd909a2015-10-05 14:57:20 -0700516 SkTextAlignProc alignProc(fFont.getTextAlign());
cdalton02015e52015-10-05 15:28:20 -0700517 FallbackBlobBuilder fallback;
cdalton38e13ad2014-11-07 06:02:15 -0800518 while (text < stop) {
cdalton3bd909a2015-10-05 14:57:20 -0700519 const SkGlyph& glyph = glyphCacheProc(glyphCache, &text, 0, 0);
cdalton38e13ad2014-11-07 06:02:15 -0800520 if (glyph.fWidth) {
521 SkPoint tmsLoc;
522 tmsProc(pos, &tmsLoc);
523 SkPoint loc;
524 alignProc(tmsLoc, glyph, &loc);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700525
cdalton02015e52015-10-05 15:28:20 -0700526 this->appendGlyph(glyph, loc, &fallback);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700527 }
cdalton38e13ad2014-11-07 06:02:15 -0800528 pos += scalarsPerPosition;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700529 }
cdalton02015e52015-10-05 15:28:20 -0700530
cdaltoncdd46822015-12-08 10:48:31 -0800531 fFallbackTextBlob.reset(fallback.buildIfNeeded(&fFallbackGlyphCount));
kkinnunenc6cb56f2014-06-24 00:12:27 -0700532}
533
cdalton8585dd22015-10-08 08:04:09 -0700534GrPathRange* GrStencilAndCoverTextContext::TextRun::createGlyphs(GrContext* ctx) const {
535 GrPathRange* glyphs = static_cast<GrPathRange*>(
536 ctx->resourceProvider()->findAndRefResourceByUniqueKey(fGlyphPathsKey));
halcanary96fcdcc2015-08-27 07:41:13 -0700537 if (nullptr == glyphs) {
cdalton8585dd22015-10-08 08:04:09 -0700538 if (fUsingRawGlyphPaths) {
539 glyphs = ctx->resourceProvider()->createGlyphs(fFont.getTypeface(), nullptr, fStroke);
540 } else {
541 SkGlyphCache* cache = this->getGlyphCache();
542 glyphs = ctx->resourceProvider()->createGlyphs(cache->getScalerContext()->getTypeface(),
543 &cache->getDescriptor(),
544 fStroke);
545 }
546 ctx->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
cdalton8585dd22015-10-08 08:04:09 -0700566void GrStencilAndCoverTextContext::TextRun::draw(GrContext* ctx,
567 GrDrawContext* dc,
cdaltoncdd79072015-10-05 15:37:35 -0700568 GrPipelineBuilder* pipelineBuilder,
569 GrColor color,
cdalton3bd909a2015-10-05 14:57:20 -0700570 const SkMatrix& viewMatrix,
joshualitt2c89bc12016-02-11 05:42:30 -0800571 const SkSurfaceProps& props,
cdaltoncdd79072015-10-05 15:37:35 -0700572 SkScalar x, SkScalar y,
573 const SkIRect& clipBounds,
cdalton3bd909a2015-10-05 14:57:20 -0700574 GrTextContext* fallbackTextContext,
575 const SkPaint& originalSkPaint) const {
cdaltoncdd46822015-12-08 10:48:31 -0800576 SkASSERT(fInstanceData);
robertphillips433625e2015-12-04 06:58:16 -0800577 SkASSERT(dc->accessRenderTarget()->isStencilBufferMultisampled() || !fFont.isAntiAlias());
robertphillipsea461502015-05-26 11:38:03 -0700578
cdaltoncdd46822015-12-08 10:48:31 -0800579 if (fInstanceData->count()) {
cdaltoncdd79072015-10-05 15:37:35 -0700580 pipelineBuilder->setState(GrPipelineBuilder::kHWAntialias_Flag, fFont.isAntiAlias());
joshualitt7b670db2015-07-09 13:25:02 -0700581
582 GR_STATIC_CONST_SAME_STENCIL(kStencilPass,
583 kZero_StencilOp,
vbuzinovc5d58f02015-08-21 05:24:24 -0700584 kKeep_StencilOp,
joshualitt7b670db2015-07-09 13:25:02 -0700585 kNotEqual_StencilFunc,
586 0xffff,
587 0x0000,
588 0xffff);
589
cdaltoncdd79072015-10-05 15:37:35 -0700590 *pipelineBuilder->stencil() = kStencilPass;
joshualitt7b670db2015-07-09 13:25:02 -0700591
cdalton8585dd22015-10-08 08:04:09 -0700592 SkAutoTUnref<GrPathRange> glyphs(this->createGlyphs(ctx));
593 if (fLastDrawnGlyphsID != glyphs->getUniqueID()) {
594 // Either this is the first draw or the glyphs object was purged since last draw.
cdaltoncdd46822015-12-08 10:48:31 -0800595 glyphs->loadPathsIfNeeded(fInstanceData->indices(), fInstanceData->count());
cdalton8585dd22015-10-08 08:04:09 -0700596 fLastDrawnGlyphsID = glyphs->getUniqueID();
597 }
598
bsalomonbf074552015-11-23 14:25:19 -0800599 // Don't compute a bounding box. For dst copy texture, we'll opt instead for it to just copy
600 // the entire dst. Realistically this is a moot point, because any context that supports
601 // NV_path_rendering will also support NV_blend_equation_advanced.
602 // For clipping we'll just skip any optimizations based on the bounds. This does, however,
603 // hurt batching.
604 SkRect bounds = SkRect::MakeIWH(pipelineBuilder->getRenderTarget()->width(),
605 pipelineBuilder->getRenderTarget()->height());
606
cdalton8ff8d242015-12-08 10:20:32 -0800607 SkAutoTUnref<GrDrawPathBatchBase> batch(
cdaltoncdd46822015-12-08 10:48:31 -0800608 GrDrawPathRangeBatch::Create(viewMatrix, fTextRatio, fTextInverseRatio * x,
609 fTextInverseRatio * y, color,
610 GrPathRendering::kWinding_FillType, glyphs, fInstanceData,
cdalton8ff8d242015-12-08 10:20:32 -0800611 bounds));
612
613 dc->drawPathBatch(*pipelineBuilder, batch);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700614 }
615
cdalton02015e52015-10-05 15:28:20 -0700616 if (fFallbackTextBlob) {
cdalton3bd909a2015-10-05 14:57:20 -0700617 SkPaint fallbackSkPaint(originalSkPaint);
cdalton7d5c9502015-10-03 13:28:35 -0700618 fStroke.applyToPaint(&fallbackSkPaint);
619 if (!fStroke.isFillStyle()) {
620 fallbackSkPaint.setStrokeWidth(fStroke.getWidth() * fTextRatio);
cdalton20b373c2014-12-01 08:38:55 -0800621 }
cdalton20b373c2014-12-01 08:38:55 -0800622
robertphillips433625e2015-12-04 06:58:16 -0800623 fallbackTextContext->drawTextBlob(dc, pipelineBuilder->clip(), fallbackSkPaint, viewMatrix,
joshualitt2c89bc12016-02-11 05:42:30 -0800624 props, fFallbackTextBlob, x, y, nullptr, clipBounds);
cdalton20b373c2014-12-01 08:38:55 -0800625 }
kkinnunenc6cb56f2014-06-24 00:12:27 -0700626}
cdalton02015e52015-10-05 15:28:20 -0700627
cdalton8585dd22015-10-08 08:04:09 -0700628SkGlyphCache* GrStencilAndCoverTextContext::TextRun::getGlyphCache() const {
629 if (!fDetachedGlyphCache) {
630 fDetachedGlyphCache = fFont.detachCache(nullptr, nullptr, true /*ignoreGamma*/);
631 }
632 return fDetachedGlyphCache;
633}
634
635
636void GrStencilAndCoverTextContext::TextRun::releaseGlyphCache() const {
637 if (fDetachedGlyphCache) {
638 SkGlyphCache::AttachCache(fDetachedGlyphCache);
639 fDetachedGlyphCache = nullptr;
640 }
641}
642
643size_t GrStencilAndCoverTextContext::TextRun::computeSizeInCache() const {
cdaltoncdd46822015-12-08 10:48:31 -0800644 size_t size = sizeof(TextRun) + fGlyphPathsKey.size();
645 // The instance data always reserves enough space for every glyph.
646 size += (fTotalGlyphCount + fFallbackGlyphCount) * (sizeof(uint16_t) + 2 * sizeof(float));
647 if (fInstanceData) {
648 size += sizeof(InstanceData);
cdaltoncdd79072015-10-05 15:37:35 -0700649 }
650 if (fFallbackTextBlob) {
651 size += sizeof(SkTextBlob);
652 }
653 return size;
654}
655
cdalton02015e52015-10-05 15:28:20 -0700656////////////////////////////////////////////////////////////////////////////////////////////////////
657
658void GrStencilAndCoverTextContext::FallbackBlobBuilder::init(const SkPaint& font,
659 SkScalar textRatio) {
660 SkASSERT(!this->isInitialized());
661 fBuilder.reset(new SkTextBlobBuilder);
662 fFont = font;
663 fFont.setTextAlign(SkPaint::kLeft_Align); // The glyph positions will already account for align.
664 fFont.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
665 // No need for subpixel positioning with bitmap glyphs. TODO: revisit if non-bitmap color glyphs
666 // show up and https://code.google.com/p/skia/issues/detail?id=4408 gets resolved.
667 fFont.setSubpixelText(false);
668 fFont.setTextSize(fFont.getTextSize() * textRatio);
669 fBuffIdx = 0;
670}
671
672void GrStencilAndCoverTextContext::FallbackBlobBuilder::appendGlyph(uint16_t glyphId,
673 const SkPoint& pos) {
674 SkASSERT(this->isInitialized());
675 if (fBuffIdx >= kWriteBufferSize) {
676 this->flush();
677 }
678 fGlyphIds[fBuffIdx] = glyphId;
679 fPositions[fBuffIdx] = pos;
680 fBuffIdx++;
cdaltoncdd46822015-12-08 10:48:31 -0800681 fCount++;
cdalton02015e52015-10-05 15:28:20 -0700682}
683
684void GrStencilAndCoverTextContext::FallbackBlobBuilder::flush() {
685 SkASSERT(this->isInitialized());
686 SkASSERT(fBuffIdx <= kWriteBufferSize);
687 if (!fBuffIdx) {
688 return;
689 }
690 // This will automatically merge with previous runs since we use the same font.
691 const SkTextBlobBuilder::RunBuffer& buff = fBuilder->allocRunPos(fFont, fBuffIdx);
692 memcpy(buff.glyphs, fGlyphIds, fBuffIdx * sizeof(uint16_t));
693 memcpy(buff.pos, fPositions[0].asScalars(), fBuffIdx * 2 * sizeof(SkScalar));
694 fBuffIdx = 0;
695}
696
cdaltoncdd46822015-12-08 10:48:31 -0800697const SkTextBlob* GrStencilAndCoverTextContext::FallbackBlobBuilder::buildIfNeeded(int *count) {
698 *count = fCount;
699 if (fCount) {
700 this->flush();
701 return fBuilder->build();
cdalton02015e52015-10-05 15:28:20 -0700702 }
cdaltoncdd46822015-12-08 10:48:31 -0800703 return nullptr;
cdalton02015e52015-10-05 15:28:20 -0700704}