blob: c3c49f92bfd4579d25ec454800b67e00909fa895 [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"
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"
kkinnunenc6cb56f2014-06-24 00:12:27 -070021#include "SkPath.h"
22#include "SkTextMapStateProc.h"
cdalton855d83f2014-09-18 13:51:53 -070023#include "SkTextFormatParams.h"
kkinnunenc6cb56f2014-06-24 00:12:27 -070024
bsalomon1fcc01c2015-09-09 09:48:06 -070025#include "batches/GrDrawPathBatch.h"
26
cdaltoncdd79072015-10-05 15:37:35 -070027template<typename Key, typename Val> static void delete_hash_map_entry(const Key&, Val* val) {
28 SkASSERT(*val);
29 delete *val;
30}
31
32template<typename T> static void delete_hash_table_entry(T* val) {
33 SkASSERT(*val);
34 delete *val;
35}
36
joshualitt6e8cd962015-03-20 10:30:14 -070037GrStencilAndCoverTextContext::GrStencilAndCoverTextContext(GrContext* context,
robertphillipsfcf78292015-06-19 11:49:52 -070038 const SkSurfaceProps& surfaceProps)
cdaltoncdd79072015-10-05 15:37:35 -070039 : INHERITED(context, surfaceProps),
40 fCacheSize(0) {
kkinnunenc6cb56f2014-06-24 00:12:27 -070041}
42
joshualitt6e8cd962015-03-20 10:30:14 -070043GrStencilAndCoverTextContext*
robertphillipsf6703fa2015-09-01 05:36:47 -070044GrStencilAndCoverTextContext::Create(GrContext* context, const SkSurfaceProps& surfaceProps) {
45 GrStencilAndCoverTextContext* textContext =
46 new GrStencilAndCoverTextContext(context, surfaceProps);
47 textContext->fFallbackTextContext = GrAtlasTextContext::Create(context, surfaceProps);
jvanverth8c27a182014-10-14 08:45:50 -070048
49 return textContext;
50}
51
kkinnunenc6cb56f2014-06-24 00:12:27 -070052GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() {
cdaltoncdd79072015-10-05 15:37:35 -070053 fBlobIdCache.foreach(delete_hash_map_entry<uint32_t, TextBlob*>);
54 fBlobKeyCache.foreach(delete_hash_table_entry<TextBlob*>);
kkinnunenc6cb56f2014-06-24 00:12:27 -070055}
56
cdaltoncdd79072015-10-05 15:37:35 -070057bool GrStencilAndCoverTextContext::internalCanDraw(const SkPaint& skPaint) {
cdaltone68f7362015-03-25 14:02:37 -070058 if (skPaint.getRasterizer()) {
jvanverth0fedb192014-10-08 09:07:27 -070059 return false;
60 }
cdaltone68f7362015-03-25 14:02:37 -070061 if (skPaint.getMaskFilter()) {
jvanverth0fedb192014-10-08 09:07:27 -070062 return false;
63 }
kkinnunen50b58e62015-05-18 23:02:07 -070064 if (SkPathEffect* pe = skPaint.getPathEffect()) {
halcanary96fcdcc2015-08-27 07:41:13 -070065 if (pe->asADash(nullptr) != SkPathEffect::kDash_DashType) {
kkinnunen50b58e62015-05-18 23:02:07 -070066 return false;
67 }
jvanverth0fedb192014-10-08 09:07:27 -070068 }
cdalton7d5c9502015-10-03 13:28:35 -070069 // No hairlines. They would require new paths with customized strokes for every new draw matrix.
70 return SkPaint::kStroke_Style != skPaint.getStyle() || 0 != skPaint.getStrokeWidth();
jvanverth0fedb192014-10-08 09:07:27 -070071}
72
robertphillipsf6703fa2015-09-01 05:36:47 -070073void GrStencilAndCoverTextContext::onDrawText(GrDrawContext* dc, GrRenderTarget* rt,
joshualitt570d2f82015-02-25 13:19:48 -080074 const GrClip& clip,
joshualitt25d9c152015-02-18 12:29:52 -080075 const GrPaint& paint,
cdalton20b373c2014-12-01 08:38:55 -080076 const SkPaint& skPaint,
joshualitt5531d512014-12-17 15:50:11 -080077 const SkMatrix& viewMatrix,
cdalton20b373c2014-12-01 08:38:55 -080078 const char text[],
79 size_t byteLength,
joshualitt6e8cd962015-03-20 10:30:14 -070080 SkScalar x, SkScalar y,
cdaltoncdd79072015-10-05 15:37:35 -070081 const SkIRect& clipBounds) {
cdalton3bd909a2015-10-05 14:57:20 -070082 TextRun run(skPaint);
cdaltoncdd79072015-10-05 15:37:35 -070083 GrPipelineBuilder pipelineBuilder(paint, rt, clip);
cdalton3bd909a2015-10-05 14:57:20 -070084 run.setText(text, byteLength, x, y, fContext, &fSurfaceProps);
cdaltoncdd79072015-10-05 15:37:35 -070085 run.draw(dc, &pipelineBuilder, paint.getColor(), viewMatrix, 0, 0, clipBounds,
86 fFallbackTextContext, skPaint);
cdalton3bd909a2015-10-05 14:57:20 -070087}
jvanverthaab626c2014-10-16 08:04:39 -070088
cdalton3bd909a2015-10-05 14:57:20 -070089void GrStencilAndCoverTextContext::onDrawPosText(GrDrawContext* dc, GrRenderTarget* rt,
90 const GrClip& clip,
91 const GrPaint& paint,
92 const SkPaint& skPaint,
93 const SkMatrix& viewMatrix,
94 const char text[],
95 size_t byteLength,
96 const SkScalar pos[],
97 int scalarsPerPosition,
98 const SkPoint& offset,
cdaltoncdd79072015-10-05 15:37:35 -070099 const SkIRect& clipBounds) {
cdalton3bd909a2015-10-05 14:57:20 -0700100 TextRun run(skPaint);
cdaltoncdd79072015-10-05 15:37:35 -0700101 GrPipelineBuilder pipelineBuilder(paint, rt, clip);
cdalton3bd909a2015-10-05 14:57:20 -0700102 run.setPosText(text, byteLength, pos, scalarsPerPosition, offset, fContext, &fSurfaceProps);
cdaltoncdd79072015-10-05 15:37:35 -0700103 run.draw(dc, &pipelineBuilder, paint.getColor(), viewMatrix, 0, 0, clipBounds,
104 fFallbackTextContext, skPaint);
105}
106
107void GrStencilAndCoverTextContext::drawTextBlob(GrDrawContext* dc, GrRenderTarget* rt,
108 const GrClip& clip, const SkPaint& skPaint,
109 const SkMatrix& viewMatrix,
110 const SkTextBlob* skBlob, SkScalar x, SkScalar y,
111 SkDrawFilter* drawFilter,
112 const SkIRect& clipBounds) {
113 if (!this->internalCanDraw(skPaint)) {
114 fFallbackTextContext->drawTextBlob(dc, rt, clip, skPaint, viewMatrix, skBlob, x, y,
115 drawFilter, clipBounds);
116 return;
117 }
118
119 if (drawFilter || skPaint.getPathEffect()) {
120 // This draw can't be cached.
121 INHERITED::drawTextBlob(dc, rt, clip, skPaint, viewMatrix, skBlob, x, y, drawFilter,
122 clipBounds);
123 return;
124 }
125
126 if (fContext->abandoned()) {
127 return;
128 }
129
130 GrPaint paint;
131 if (!SkPaintToGrPaint(fContext, skPaint, viewMatrix, &paint)) {
132 return;
133 }
134
135 const TextBlob& blob = this->findOrCreateTextBlob(skBlob, skPaint);
136 GrPipelineBuilder pipelineBuilder(paint, rt, clip);
137
138 TextBlob::Iter iter(blob);
139 for (TextRun* run = iter.get(); run; run = iter.next()) {
140 run->draw(dc, &pipelineBuilder, paint.getColor(), viewMatrix, x, y, clipBounds,
141 fFallbackTextContext, skPaint);
142 }
143}
144
145const GrStencilAndCoverTextContext::TextBlob&
146GrStencilAndCoverTextContext::findOrCreateTextBlob(const SkTextBlob* skBlob,
147 const SkPaint& skPaint) {
148 // The font-related parameters are baked into the text blob and will override this skPaint, so
149 // the only remaining properties that can affect a TextBlob are the ones related to stroke.
150 if (SkPaint::kFill_Style == skPaint.getStyle()) { // Fast path.
151 if (TextBlob** found = fBlobIdCache.find(skBlob->uniqueID())) {
152 fLRUList.remove(*found);
153 fLRUList.addToTail(*found);
154 return **found;
155 }
156 TextBlob* blob = new TextBlob(skBlob->uniqueID(), skBlob, skPaint, fContext,
157 &fSurfaceProps);
158 this->purgeToFit(*blob);
159 fBlobIdCache.set(skBlob->uniqueID(), blob);
160 fLRUList.addToTail(blob);
161 fCacheSize += blob->cpuMemorySize();
162 return *blob;
163 } else {
164 GrStrokeInfo stroke(skPaint);
165 SkSTArray<4, uint32_t, true> key;
166 key.reset(1 + stroke.computeUniqueKeyFragmentData32Cnt());
167 key[0] = skBlob->uniqueID();
168 stroke.asUniqueKeyFragment(&key[1]);
169 if (TextBlob** found = fBlobKeyCache.find(key)) {
170 fLRUList.remove(*found);
171 fLRUList.addToTail(*found);
172 return **found;
173 }
174 TextBlob* blob = new TextBlob(key, skBlob, skPaint, fContext, &fSurfaceProps);
175 this->purgeToFit(*blob);
176 fBlobKeyCache.set(blob);
177 fLRUList.addToTail(blob);
178 fCacheSize += blob->cpuMemorySize();
179 return *blob;
180 }
181}
182
183void GrStencilAndCoverTextContext::purgeToFit(const TextBlob& blob) {
184 static const int maxCacheSize = 4 * 1024 * 1024; // Allow up to 4 MB for caching text blobs.
185
186 int maxSizeForNewBlob = maxCacheSize - blob.cpuMemorySize();
187 while (fCacheSize && fCacheSize > maxSizeForNewBlob) {
188 TextBlob* lru = fLRUList.head();
189 if (1 == lru->key().count()) {
190 // 1-length keys are unterstood to be the blob id.
191 fBlobIdCache.remove(lru->key()[0]);
192 } else {
193 fBlobKeyCache.remove(lru->key());
194 }
195 fLRUList.remove(lru);
196 fCacheSize -= lru->cpuMemorySize();
197 delete lru;
198 }
199}
200
201////////////////////////////////////////////////////////////////////////////////////////////////////
202
203void GrStencilAndCoverTextContext::TextBlob::init(const SkTextBlob* skBlob, const SkPaint& skPaint,
204 GrContext* ctx, const SkSurfaceProps* props) {
205 fCpuMemorySize = sizeof(TextBlob);
206 SkPaint runPaint(skPaint);
207 for (SkTextBlob::RunIterator iter(skBlob); !iter.done(); iter.next()) {
208 iter.applyFontToPaint(&runPaint); // No need to re-seed the paint.
209 TextRun* run = SkNEW_INSERT_AT_LLIST_TAIL(this, TextRun, (runPaint));
210
211 const char* text = reinterpret_cast<const char*>(iter.glyphs());
212 size_t byteLength = sizeof(uint16_t) * iter.glyphCount();
213 const SkPoint& runOffset = iter.offset();
214
215 switch (iter.positioning()) {
216 case SkTextBlob::kDefault_Positioning:
217 run->setText(text, byteLength, runOffset.fX, runOffset.fY, ctx, props);
218 break;
219 case SkTextBlob::kHorizontal_Positioning:
220 run->setPosText(text, byteLength, iter.pos(), 1, SkPoint::Make(0, runOffset.fY),
221 ctx, props);
222 break;
223 case SkTextBlob::kFull_Positioning:
224 run->setPosText(text, byteLength, iter.pos(), 2, SkPoint::Make(0, 0), ctx, props);
225 break;
226 }
227
228 fCpuMemorySize += run->cpuMemorySize();
229 }
cdalton3bd909a2015-10-05 14:57:20 -0700230}
231
232////////////////////////////////////////////////////////////////////////////////////////////////////
233
cdalton02015e52015-10-05 15:28:20 -0700234class GrStencilAndCoverTextContext::FallbackBlobBuilder {
235public:
236 FallbackBlobBuilder() : fBuffIdx(0) {}
237
238 bool isInitialized() const { return SkToBool(fBuilder); }
239
240 void init(const SkPaint& font, SkScalar textRatio);
241
242 void appendGlyph(uint16_t glyphId, const SkPoint& pos);
243
244 const SkTextBlob* buildIfInitialized();
245
246private:
247 enum { kWriteBufferSize = 1024 };
248
249 void flush();
250
251 SkAutoTDelete<SkTextBlobBuilder> fBuilder;
252 SkPaint fFont;
253 int fBuffIdx;
254 uint16_t fGlyphIds[kWriteBufferSize];
255 SkPoint fPositions[kWriteBufferSize];
256};
257
258////////////////////////////////////////////////////////////////////////////////////////////////////
259
cdalton3bd909a2015-10-05 14:57:20 -0700260GrStencilAndCoverTextContext::TextRun::TextRun(const SkPaint& fontAndStroke)
261 : fStroke(fontAndStroke),
cdaltoncdd79072015-10-05 15:37:35 -0700262 fFont(fontAndStroke),
263 fTotalGlyphCount(0) {
cdalton3bd909a2015-10-05 14:57:20 -0700264 SkASSERT(!fStroke.isHairlineStyle()); // Hairlines are not supported.
265
266 // Setting to "fill" ensures that no strokes get baked into font outlines. (We use the GPU path
267 // rendering API for stroking).
268 fFont.setStyle(SkPaint::kFill_Style);
269
270 if (fFont.isFakeBoldText() && SkStrokeRec::kStroke_Style != fStroke.getStyle()) {
271 // Instead of letting fake bold get baked into the glyph outlines, do it with GPU stroke.
272 SkScalar fakeBoldScale = SkScalarInterpFunc(fFont.getTextSize(),
273 kStdFakeBoldInterpKeys,
274 kStdFakeBoldInterpValues,
275 kStdFakeBoldInterpLength);
276 SkScalar extra = SkScalarMul(fFont.getTextSize(), fakeBoldScale);
277 fStroke.setStrokeStyle(fStroke.needToApply() ? fStroke.getWidth() + extra : extra,
278 true /*strokeAndFill*/);
279
280 fFont.setFakeBoldText(false);
jvanverthaab626c2014-10-16 08:04:39 -0700281 }
282
cdalton3bd909a2015-10-05 14:57:20 -0700283 if (!fFont.getPathEffect() && !fStroke.isDashed()) {
284 // We can draw the glyphs from canonically sized paths.
285 fTextRatio = fFont.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
286 fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fFont.getTextSize();
jvanverthaab626c2014-10-16 08:04:39 -0700287
cdalton3bd909a2015-10-05 14:57:20 -0700288 // Compensate for the glyphs being scaled by fTextRatio.
289 if (!fStroke.isFillStyle()) {
290 fStroke.setStrokeStyle(fStroke.getWidth() / fTextRatio,
291 SkStrokeRec::kStrokeAndFill_Style == fStroke.getStyle());
292 }
293
294 fFont.setLinearText(true);
295 fFont.setLCDRenderText(false);
296 fFont.setAutohinted(false);
297 fFont.setHinting(SkPaint::kNo_Hinting);
298 fFont.setSubpixelText(true);
299 fFont.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
300
301 fUsingRawGlyphPaths = SK_Scalar1 == fFont.getTextScaleX() &&
302 0 == fFont.getTextSkewX() &&
303 !fFont.isFakeBoldText() &&
304 !fFont.isVerticalText();
305 } else {
306 fTextRatio = fTextInverseRatio = 1.0f;
307 fUsingRawGlyphPaths = false;
308 }
309
310 // When drawing from canonically sized paths, the actual local coords are fTextRatio * coords.
cdaltoncdd79072015-10-05 15:37:35 -0700311 fLocalMatrixTemplate.setScale(fTextRatio, fTextRatio);
cdalton3bd909a2015-10-05 14:57:20 -0700312}
313
314GrStencilAndCoverTextContext::TextRun::~TextRun() {
315}
316
317void GrStencilAndCoverTextContext::TextRun::setText(const char text[], size_t byteLength,
318 SkScalar x, SkScalar y, GrContext* ctx,
319 const SkSurfaceProps* surfaceProps) {
320 SkASSERT(byteLength == 0 || text != nullptr);
321
322 SkAutoGlyphCacheNoGamma autoGlyphCache(fFont, surfaceProps, nullptr);
323 SkGlyphCache* glyphCache = autoGlyphCache.getCache();
324
cdaltoncdd79072015-10-05 15:37:35 -0700325 fTotalGlyphCount = fFont.countText(text, byteLength);
cdalton3bd909a2015-10-05 14:57:20 -0700326 fDraw.reset(GrPathRangeDraw::Create(this->createGlyphs(ctx, glyphCache),
327 GrPathRendering::kTranslate_PathTransformType,
cdaltoncdd79072015-10-05 15:37:35 -0700328 fTotalGlyphCount));
cdalton3bd909a2015-10-05 14:57:20 -0700329
330 SkDrawCacheProc glyphCacheProc = fFont.getDrawCacheProc();
jvanverthaab626c2014-10-16 08:04:39 -0700331
jvanverthaab626c2014-10-16 08:04:39 -0700332 const char* stop = text + byteLength;
333
334 // Measure first if needed.
cdalton3bd909a2015-10-05 14:57:20 -0700335 if (fFont.getTextAlign() != SkPaint::kLeft_Align) {
jvanverthaab626c2014-10-16 08:04:39 -0700336 SkFixed stopX = 0;
337 SkFixed stopY = 0;
338
339 const char* textPtr = text;
340 while (textPtr < stop) {
341 // We don't need x, y here, since all subpixel variants will have the
342 // same advance.
cdalton3bd909a2015-10-05 14:57:20 -0700343 const SkGlyph& glyph = glyphCacheProc(glyphCache, &textPtr, 0, 0);
jvanverthaab626c2014-10-16 08:04:39 -0700344
345 stopX += glyph.fAdvanceX;
346 stopY += glyph.fAdvanceY;
347 }
348 SkASSERT(textPtr == stop);
349
350 SkScalar alignX = SkFixedToScalar(stopX) * fTextRatio;
351 SkScalar alignY = SkFixedToScalar(stopY) * fTextRatio;
352
cdalton3bd909a2015-10-05 14:57:20 -0700353 if (fFont.getTextAlign() == SkPaint::kCenter_Align) {
jvanverthaab626c2014-10-16 08:04:39 -0700354 alignX = SkScalarHalf(alignX);
355 alignY = SkScalarHalf(alignY);
356 }
357
358 x -= alignX;
359 y -= alignY;
360 }
361
362 SkAutoKern autokern;
363
364 SkFixed fixedSizeRatio = SkScalarToFixed(fTextRatio);
365
366 SkFixed fx = SkScalarToFixed(x);
367 SkFixed fy = SkScalarToFixed(y);
cdalton02015e52015-10-05 15:28:20 -0700368 FallbackBlobBuilder fallback;
jvanverthaab626c2014-10-16 08:04:39 -0700369 while (text < stop) {
cdalton3bd909a2015-10-05 14:57:20 -0700370 const SkGlyph& glyph = glyphCacheProc(glyphCache, &text, 0, 0);
bungemand709ea82015-03-17 07:23:39 -0700371 fx += SkFixedMul(autokern.adjust(glyph), fixedSizeRatio);
jvanverthaab626c2014-10-16 08:04:39 -0700372 if (glyph.fWidth) {
cdalton02015e52015-10-05 15:28:20 -0700373 this->appendGlyph(glyph, SkPoint::Make(SkFixedToScalar(fx), SkFixedToScalar(fy)),
374 &fallback);
jvanverthaab626c2014-10-16 08:04:39 -0700375 }
376
bungemand709ea82015-03-17 07:23:39 -0700377 fx += SkFixedMul(glyph.fAdvanceX, fixedSizeRatio);
378 fy += SkFixedMul(glyph.fAdvanceY, fixedSizeRatio);
jvanverthaab626c2014-10-16 08:04:39 -0700379 }
cdalton02015e52015-10-05 15:28:20 -0700380
381 fFallbackTextBlob.reset(fallback.buildIfInitialized());
jvanverthaab626c2014-10-16 08:04:39 -0700382}
383
cdalton3bd909a2015-10-05 14:57:20 -0700384void GrStencilAndCoverTextContext::TextRun::setPosText(const char text[], size_t byteLength,
385 const SkScalar pos[], int scalarsPerPosition,
386 const SkPoint& offset, GrContext* ctx,
387 const SkSurfaceProps* surfaceProps) {
halcanary96fcdcc2015-08-27 07:41:13 -0700388 SkASSERT(byteLength == 0 || text != nullptr);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700389 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
390
cdalton3bd909a2015-10-05 14:57:20 -0700391 SkAutoGlyphCacheNoGamma autoGlyphCache(fFont, surfaceProps, nullptr);
392 SkGlyphCache* glyphCache = autoGlyphCache.getCache();
kkinnunenc6cb56f2014-06-24 00:12:27 -0700393
cdaltoncdd79072015-10-05 15:37:35 -0700394 fTotalGlyphCount = fFont.countText(text, byteLength);
cdalton3bd909a2015-10-05 14:57:20 -0700395 fDraw.reset(GrPathRangeDraw::Create(this->createGlyphs(ctx, glyphCache),
396 GrPathRendering::kTranslate_PathTransformType,
cdaltoncdd79072015-10-05 15:37:35 -0700397 fTotalGlyphCount));
kkinnunenc6cb56f2014-06-24 00:12:27 -0700398
cdalton3bd909a2015-10-05 14:57:20 -0700399 SkDrawCacheProc glyphCacheProc = fFont.getDrawCacheProc();
kkinnunenc6cb56f2014-06-24 00:12:27 -0700400
kkinnunenc6cb56f2014-06-24 00:12:27 -0700401 const char* stop = text + byteLength;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700402
cdalton38e13ad2014-11-07 06:02:15 -0800403 SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
cdalton3bd909a2015-10-05 14:57:20 -0700404 SkTextAlignProc alignProc(fFont.getTextAlign());
cdalton02015e52015-10-05 15:28:20 -0700405 FallbackBlobBuilder fallback;
cdalton38e13ad2014-11-07 06:02:15 -0800406 while (text < stop) {
cdalton3bd909a2015-10-05 14:57:20 -0700407 const SkGlyph& glyph = glyphCacheProc(glyphCache, &text, 0, 0);
cdalton38e13ad2014-11-07 06:02:15 -0800408 if (glyph.fWidth) {
409 SkPoint tmsLoc;
410 tmsProc(pos, &tmsLoc);
411 SkPoint loc;
412 alignProc(tmsLoc, glyph, &loc);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700413
cdalton02015e52015-10-05 15:28:20 -0700414 this->appendGlyph(glyph, loc, &fallback);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700415 }
cdalton38e13ad2014-11-07 06:02:15 -0800416 pos += scalarsPerPosition;
kkinnunenc6cb56f2014-06-24 00:12:27 -0700417 }
cdalton02015e52015-10-05 15:28:20 -0700418
419 fFallbackTextBlob.reset(fallback.buildIfInitialized());
kkinnunenc6cb56f2014-06-24 00:12:27 -0700420}
421
cdalton3bd909a2015-10-05 14:57:20 -0700422GrPathRange* GrStencilAndCoverTextContext::TextRun::createGlyphs(GrContext* ctx,
423 SkGlyphCache* glyphCache) {
424 SkTypeface* typeface = fUsingRawGlyphPaths ? fFont.getTypeface()
425 : glyphCache->getScalerContext()->getTypeface();
426 const SkDescriptor* desc = fUsingRawGlyphPaths ? nullptr : &glyphCache->getDescriptor();
kkinnunen50b58e62015-05-18 23:02:07 -0700427
428 static const GrUniqueKey::Domain kPathGlyphDomain = GrUniqueKey::GenerateDomain();
cdalton3bd909a2015-10-05 14:57:20 -0700429 int strokeDataCount = fStroke.computeUniqueKeyFragmentData32Cnt();
kkinnunen50b58e62015-05-18 23:02:07 -0700430 GrUniqueKey glyphKey;
431 GrUniqueKey::Builder builder(&glyphKey, kPathGlyphDomain, 2 + strokeDataCount);
432 reinterpret_cast<uint32_t&>(builder[0]) = desc ? desc->getChecksum() : 0;
433 reinterpret_cast<uint32_t&>(builder[1]) = typeface ? typeface->uniqueID() : 0;
434 if (strokeDataCount > 0) {
cdalton3bd909a2015-10-05 14:57:20 -0700435 fStroke.asUniqueKeyFragment(&builder[2]);
kkinnunen50b58e62015-05-18 23:02:07 -0700436 }
bsalomon24db3b12015-01-23 04:24:04 -0800437 builder.finish();
cdalton855d83f2014-09-18 13:51:53 -0700438
439 SkAutoTUnref<GrPathRange> glyphs(
kkinnunen50b58e62015-05-18 23:02:07 -0700440 static_cast<GrPathRange*>(
441 ctx->resourceProvider()->findAndRefResourceByUniqueKey(glyphKey)));
halcanary96fcdcc2015-08-27 07:41:13 -0700442 if (nullptr == glyphs) {
cdalton3bd909a2015-10-05 14:57:20 -0700443 glyphs.reset(ctx->resourceProvider()->createGlyphs(typeface, desc, fStroke));
kkinnunen50b58e62015-05-18 23:02:07 -0700444 ctx->resourceProvider()->assignUniqueKeyToResource(glyphKey, glyphs);
445 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700446 SkASSERT(nullptr == desc || glyphs->isEqualTo(*desc));
cdalton855d83f2014-09-18 13:51:53 -0700447 }
448
449 return glyphs.detach();
450}
451
cdalton3bd909a2015-10-05 14:57:20 -0700452inline void GrStencilAndCoverTextContext::TextRun::appendGlyph(const SkGlyph& glyph,
cdalton02015e52015-10-05 15:28:20 -0700453 const SkPoint& pos,
454 FallbackBlobBuilder* fallback) {
455 // Stick the glyphs we can't draw into the fallback text blob.
bsalomon1fcc01c2015-09-09 09:48:06 -0700456 if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
cdalton02015e52015-10-05 15:28:20 -0700457 if (!fallback->isInitialized()) {
458 fallback->init(fFont, fTextRatio);
459 }
460 fallback->appendGlyph(glyph.getGlyphID(), pos);
bsalomon1fcc01c2015-09-09 09:48:06 -0700461 } else {
cdalton7d5c9502015-10-03 13:28:35 -0700462 float translate[] = { fTextInverseRatio * pos.x(), fTextInverseRatio * pos.y() };
bsalomon1fcc01c2015-09-09 09:48:06 -0700463 fDraw->append(glyph.getGlyphID(), translate);
cdaltonb2808cd2014-07-25 14:13:57 -0700464 }
cdalton20b373c2014-12-01 08:38:55 -0800465}
466
cdalton3bd909a2015-10-05 14:57:20 -0700467void GrStencilAndCoverTextContext::TextRun::draw(GrDrawContext* dc,
cdaltoncdd79072015-10-05 15:37:35 -0700468 GrPipelineBuilder* pipelineBuilder,
469 GrColor color,
cdalton3bd909a2015-10-05 14:57:20 -0700470 const SkMatrix& viewMatrix,
cdaltoncdd79072015-10-05 15:37:35 -0700471 SkScalar x, SkScalar y,
472 const SkIRect& clipBounds,
cdalton3bd909a2015-10-05 14:57:20 -0700473 GrTextContext* fallbackTextContext,
474 const SkPaint& originalSkPaint) const {
475 SkASSERT(fDraw);
cdaltoncdd79072015-10-05 15:37:35 -0700476 SkASSERT(pipelineBuilder->getRenderTarget()->isStencilBufferMultisampled() ||
477 !fFont.isAntiAlias());
robertphillipsea461502015-05-26 11:38:03 -0700478
cdalton3bd909a2015-10-05 14:57:20 -0700479 if (fDraw->count()) {
cdaltoncdd79072015-10-05 15:37:35 -0700480 pipelineBuilder->setState(GrPipelineBuilder::kHWAntialias_Flag, fFont.isAntiAlias());
joshualitt7b670db2015-07-09 13:25:02 -0700481
482 GR_STATIC_CONST_SAME_STENCIL(kStencilPass,
483 kZero_StencilOp,
vbuzinovc5d58f02015-08-21 05:24:24 -0700484 kKeep_StencilOp,
joshualitt7b670db2015-07-09 13:25:02 -0700485 kNotEqual_StencilFunc,
486 0xffff,
487 0x0000,
488 0xffff);
489
cdaltoncdd79072015-10-05 15:37:35 -0700490 *pipelineBuilder->stencil() = kStencilPass;
joshualitt7b670db2015-07-09 13:25:02 -0700491
cdalton3bd909a2015-10-05 14:57:20 -0700492 SkMatrix drawMatrix(viewMatrix);
cdaltoncdd79072015-10-05 15:37:35 -0700493 drawMatrix.preTranslate(x, y);
cdalton3bd909a2015-10-05 14:57:20 -0700494 drawMatrix.preScale(fTextRatio, fTextRatio);
495
cdaltoncdd79072015-10-05 15:37:35 -0700496 SkMatrix& localMatrix = fLocalMatrixTemplate;
497 localMatrix.setTranslateX(x);
498 localMatrix.setTranslateY(y);
499
500 dc->drawPathsFromRange(pipelineBuilder, drawMatrix, localMatrix, color, fDraw,
cdalton3bd909a2015-10-05 14:57:20 -0700501 GrPathRendering::kWinding_FillType);
kkinnunenc6cb56f2014-06-24 00:12:27 -0700502 }
503
cdalton02015e52015-10-05 15:28:20 -0700504 if (fFallbackTextBlob) {
cdalton3bd909a2015-10-05 14:57:20 -0700505 SkPaint fallbackSkPaint(originalSkPaint);
cdalton7d5c9502015-10-03 13:28:35 -0700506 fStroke.applyToPaint(&fallbackSkPaint);
507 if (!fStroke.isFillStyle()) {
508 fallbackSkPaint.setStrokeWidth(fStroke.getWidth() * fTextRatio);
cdalton20b373c2014-12-01 08:38:55 -0800509 }
cdalton20b373c2014-12-01 08:38:55 -0800510
cdaltoncdd79072015-10-05 15:37:35 -0700511 fallbackTextContext->drawTextBlob(dc, pipelineBuilder->getRenderTarget(),
512 pipelineBuilder->clip(), fallbackSkPaint, viewMatrix,
513 fFallbackTextBlob, x, y, nullptr, clipBounds);
cdalton20b373c2014-12-01 08:38:55 -0800514 }
kkinnunenc6cb56f2014-06-24 00:12:27 -0700515}
cdalton02015e52015-10-05 15:28:20 -0700516
cdaltoncdd79072015-10-05 15:37:35 -0700517int GrStencilAndCoverTextContext::TextRun::cpuMemorySize() const {
518 int size = sizeof(TextRun) + fTotalGlyphCount * (sizeof(uint16_t) + 2 * sizeof(float));
519 if (fDraw) {
520 size += sizeof(GrPathRangeDraw);
521 }
522 if (fFallbackTextBlob) {
523 size += sizeof(SkTextBlob);
524 }
525 return size;
526}
527
cdalton02015e52015-10-05 15:28:20 -0700528////////////////////////////////////////////////////////////////////////////////////////////////////
529
530void GrStencilAndCoverTextContext::FallbackBlobBuilder::init(const SkPaint& font,
531 SkScalar textRatio) {
532 SkASSERT(!this->isInitialized());
533 fBuilder.reset(new SkTextBlobBuilder);
534 fFont = font;
535 fFont.setTextAlign(SkPaint::kLeft_Align); // The glyph positions will already account for align.
536 fFont.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
537 // No need for subpixel positioning with bitmap glyphs. TODO: revisit if non-bitmap color glyphs
538 // show up and https://code.google.com/p/skia/issues/detail?id=4408 gets resolved.
539 fFont.setSubpixelText(false);
540 fFont.setTextSize(fFont.getTextSize() * textRatio);
541 fBuffIdx = 0;
542}
543
544void GrStencilAndCoverTextContext::FallbackBlobBuilder::appendGlyph(uint16_t glyphId,
545 const SkPoint& pos) {
546 SkASSERT(this->isInitialized());
547 if (fBuffIdx >= kWriteBufferSize) {
548 this->flush();
549 }
550 fGlyphIds[fBuffIdx] = glyphId;
551 fPositions[fBuffIdx] = pos;
552 fBuffIdx++;
553}
554
555void GrStencilAndCoverTextContext::FallbackBlobBuilder::flush() {
556 SkASSERT(this->isInitialized());
557 SkASSERT(fBuffIdx <= kWriteBufferSize);
558 if (!fBuffIdx) {
559 return;
560 }
561 // This will automatically merge with previous runs since we use the same font.
562 const SkTextBlobBuilder::RunBuffer& buff = fBuilder->allocRunPos(fFont, fBuffIdx);
563 memcpy(buff.glyphs, fGlyphIds, fBuffIdx * sizeof(uint16_t));
564 memcpy(buff.pos, fPositions[0].asScalars(), fBuffIdx * 2 * sizeof(SkScalar));
565 fBuffIdx = 0;
566}
567
568const SkTextBlob* GrStencilAndCoverTextContext::FallbackBlobBuilder::buildIfInitialized() {
569 if (!this->isInitialized()) {
570 return nullptr;
571 }
572 this->flush();
573 return fBuilder->build();
574}