OLD | NEW |
| (Empty) |
1 /* | |
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" | |
9 #include "GrAtlasTextContext.h" | |
10 #include "GrDrawContext.h" | |
11 #include "GrDrawTarget.h" | |
12 #include "GrPath.h" | |
13 #include "GrPathRange.h" | |
14 #include "GrResourceProvider.h" | |
15 #include "SkAutoKern.h" | |
16 #include "SkDraw.h" | |
17 #include "SkDrawProcs.h" | |
18 #include "SkGlyphCache.h" | |
19 #include "SkGpuDevice.h" | |
20 #include "SkGrPriv.h" | |
21 #include "SkPath.h" | |
22 #include "SkTextBlobRunIterator.h" | |
23 #include "SkTextMapStateProc.h" | |
24 #include "SkTextFormatParams.h" | |
25 | |
26 #include "batches/GrDrawPathBatch.h" | |
27 | |
28 template<typename Key, typename Val> static void delete_hash_map_entry(const Key
&, Val* val) { | |
29 SkASSERT(*val); | |
30 delete *val; | |
31 } | |
32 | |
33 template<typename T> static void delete_hash_table_entry(T* val) { | |
34 SkASSERT(*val); | |
35 delete *val; | |
36 } | |
37 | |
38 GrStencilAndCoverTextContext::GrStencilAndCoverTextContext(GrContext* context, | |
39 const SkSurfaceProps&
surfaceProps) | |
40 : INHERITED(context, surfaceProps), | |
41 fCacheSize(0) { | |
42 } | |
43 | |
44 GrStencilAndCoverTextContext* | |
45 GrStencilAndCoverTextContext::Create(GrContext* context, const SkSurfaceProps& s
urfaceProps) { | |
46 GrStencilAndCoverTextContext* textContext = | |
47 new GrStencilAndCoverTextContext(context, surfaceProps); | |
48 textContext->fFallbackTextContext = GrAtlasTextContext::Create(context, surf
aceProps); | |
49 | |
50 return textContext; | |
51 } | |
52 | |
53 GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() { | |
54 fBlobIdCache.foreach(delete_hash_map_entry<uint32_t, TextBlob*>); | |
55 fBlobKeyCache.foreach(delete_hash_table_entry<TextBlob*>); | |
56 } | |
57 | |
58 bool GrStencilAndCoverTextContext::internalCanDraw(const SkPaint& skPaint) { | |
59 if (skPaint.getRasterizer()) { | |
60 return false; | |
61 } | |
62 if (skPaint.getMaskFilter()) { | |
63 return false; | |
64 } | |
65 if (SkPathEffect* pe = skPaint.getPathEffect()) { | |
66 if (pe->asADash(nullptr) != SkPathEffect::kDash_DashType) { | |
67 return false; | |
68 } | |
69 } | |
70 // No hairlines. They would require new paths with customized strokes for ev
ery new draw matrix. | |
71 return SkPaint::kStroke_Style != skPaint.getStyle() || 0 != skPaint.getStrok
eWidth(); | |
72 } | |
73 | |
74 void GrStencilAndCoverTextContext::onDrawText(GrDrawContext* dc, | |
75 const GrClip& clip, | |
76 const GrPaint& paint, | |
77 const SkPaint& skPaint, | |
78 const SkMatrix& viewMatrix, | |
79 const char text[], | |
80 size_t byteLength, | |
81 SkScalar x, SkScalar y, | |
82 const SkIRect& clipBounds) { | |
83 TextRun run(skPaint); | |
84 GrPipelineBuilder pipelineBuilder(paint, dc->accessRenderTarget(), clip); | |
85 run.setText(text, byteLength, x, y); | |
86 run.draw(fContext, dc, &pipelineBuilder, paint.getColor(), viewMatrix, 0, 0,
clipBounds, | |
87 fFallbackTextContext, skPaint); | |
88 } | |
89 | |
90 void GrStencilAndCoverTextContext::onDrawPosText(GrDrawContext* dc, | |
91 const GrClip& clip, | |
92 const GrPaint& paint, | |
93 const SkPaint& skPaint, | |
94 const SkMatrix& viewMatrix, | |
95 const char text[], | |
96 size_t byteLength, | |
97 const SkScalar pos[], | |
98 int scalarsPerPosition, | |
99 const SkPoint& offset, | |
100 const SkIRect& clipBounds) { | |
101 TextRun run(skPaint); | |
102 GrPipelineBuilder pipelineBuilder(paint, dc->accessRenderTarget(), clip); | |
103 run.setPosText(text, byteLength, pos, scalarsPerPosition, offset); | |
104 run.draw(fContext, dc, &pipelineBuilder, paint.getColor(), viewMatrix, 0, 0,
clipBounds, | |
105 fFallbackTextContext, skPaint); | |
106 } | |
107 | |
108 void GrStencilAndCoverTextContext::drawTextBlob(GrDrawContext* dc, | |
109 const GrClip& clip, const SkPain
t& skPaint, | |
110 const SkMatrix& viewMatrix, | |
111 const SkTextBlob* skBlob, SkScal
ar x, SkScalar y, | |
112 SkDrawFilter* drawFilter, | |
113 const SkIRect& clipBounds) { | |
114 if (!this->internalCanDraw(skPaint)) { | |
115 fFallbackTextContext->drawTextBlob(dc, clip, skPaint, viewMatrix, skBlob
, x, y, | |
116 drawFilter, clipBounds); | |
117 return; | |
118 } | |
119 | |
120 if (drawFilter || skPaint.getPathEffect()) { | |
121 // This draw can't be cached. | |
122 INHERITED::drawTextBlob(dc, clip, skPaint, viewMatrix, skBlob, x, y, dra
wFilter, | |
123 clipBounds); | |
124 return; | |
125 } | |
126 | |
127 if (fContext->abandoned()) { | |
128 return; | |
129 } | |
130 | |
131 GrPaint paint; | |
132 if (!SkPaintToGrPaint(fContext, skPaint, viewMatrix, &paint)) { | |
133 return; | |
134 } | |
135 | |
136 const TextBlob& blob = this->findOrCreateTextBlob(skBlob, skPaint); | |
137 GrPipelineBuilder pipelineBuilder(paint, dc->accessRenderTarget(), clip); | |
138 | |
139 TextBlob::Iter iter(blob); | |
140 for (TextRun* run = iter.get(); run; run = iter.next()) { | |
141 run->draw(fContext, dc, &pipelineBuilder, paint.getColor(), viewMatrix,
x, y, clipBounds, | |
142 fFallbackTextContext, skPaint); | |
143 run->releaseGlyphCache(); | |
144 } | |
145 } | |
146 | |
147 const GrStencilAndCoverTextContext::TextBlob& | |
148 GrStencilAndCoverTextContext::findOrCreateTextBlob(const SkTextBlob* skBlob, | |
149 const SkPaint& skPaint) { | |
150 // The font-related parameters are baked into the text blob and will overrid
e this skPaint, so | |
151 // the only remaining properties that can affect a TextBlob are the ones rel
ated to stroke. | |
152 if (SkPaint::kFill_Style == skPaint.getStyle()) { // Fast path. | |
153 if (TextBlob** found = fBlobIdCache.find(skBlob->uniqueID())) { | |
154 fLRUList.remove(*found); | |
155 fLRUList.addToTail(*found); | |
156 return **found; | |
157 } | |
158 TextBlob* blob = new TextBlob(skBlob->uniqueID(), skBlob, skPaint); | |
159 this->purgeToFit(*blob); | |
160 fBlobIdCache.set(skBlob->uniqueID(), blob); | |
161 fLRUList.addToTail(blob); | |
162 fCacheSize += blob->cpuMemorySize(); | |
163 return *blob; | |
164 } else { | |
165 GrStrokeInfo stroke(skPaint); | |
166 SkSTArray<4, uint32_t, true> key; | |
167 key.reset(1 + stroke.computeUniqueKeyFragmentData32Cnt()); | |
168 key[0] = skBlob->uniqueID(); | |
169 stroke.asUniqueKeyFragment(&key[1]); | |
170 if (TextBlob** found = fBlobKeyCache.find(key)) { | |
171 fLRUList.remove(*found); | |
172 fLRUList.addToTail(*found); | |
173 return **found; | |
174 } | |
175 TextBlob* blob = new TextBlob(key, skBlob, skPaint); | |
176 this->purgeToFit(*blob); | |
177 fBlobKeyCache.set(blob); | |
178 fLRUList.addToTail(blob); | |
179 fCacheSize += blob->cpuMemorySize(); | |
180 return *blob; | |
181 } | |
182 } | |
183 | |
184 void GrStencilAndCoverTextContext::purgeToFit(const TextBlob& blob) { | |
185 static const size_t maxCacheSize = 4 * 1024 * 1024; // Allow up to 4 MB for
caching text blobs. | |
186 | |
187 size_t maxSizeForNewBlob = maxCacheSize - blob.cpuMemorySize(); | |
188 while (fCacheSize && fCacheSize > maxSizeForNewBlob) { | |
189 TextBlob* lru = fLRUList.head(); | |
190 if (1 == lru->key().count()) { | |
191 // 1-length keys are unterstood to be the blob id. | |
192 fBlobIdCache.remove(lru->key()[0]); | |
193 } else { | |
194 fBlobKeyCache.remove(lru->key()); | |
195 } | |
196 fLRUList.remove(lru); | |
197 fCacheSize -= lru->cpuMemorySize(); | |
198 delete lru; | |
199 } | |
200 } | |
201 | |
202 ////////////////////////////////////////////////////////////////////////////////
//////////////////// | |
203 | |
204 void GrStencilAndCoverTextContext::TextBlob::init(const SkTextBlob* skBlob, | |
205 const SkPaint& skPaint) { | |
206 fCpuMemorySize = sizeof(TextBlob); | |
207 SkPaint runPaint(skPaint); | |
208 for (SkTextBlobRunIterator iter(skBlob); !iter.done(); iter.next()) { | |
209 iter.applyFontToPaint(&runPaint); // No need to re-seed the paint. | |
210 TextRun* run = this->addToTail(runPaint); | |
211 | |
212 const char* text = reinterpret_cast<const char*>(iter.glyphs()); | |
213 size_t byteLength = sizeof(uint16_t) * iter.glyphCount(); | |
214 const SkPoint& runOffset = iter.offset(); | |
215 | |
216 switch (iter.positioning()) { | |
217 case SkTextBlob::kDefault_Positioning: | |
218 run->setText(text, byteLength, runOffset.fX, runOffset.fY); | |
219 break; | |
220 case SkTextBlob::kHorizontal_Positioning: | |
221 run->setPosText(text, byteLength, iter.pos(), 1, SkPoint::Make(0
, runOffset.fY)); | |
222 break; | |
223 case SkTextBlob::kFull_Positioning: | |
224 run->setPosText(text, byteLength, iter.pos(), 2, SkPoint::Make(0
, 0)); | |
225 break; | |
226 } | |
227 | |
228 fCpuMemorySize += run->computeSizeInCache(); | |
229 } | |
230 } | |
231 | |
232 ////////////////////////////////////////////////////////////////////////////////
//////////////////// | |
233 | |
234 class GrStencilAndCoverTextContext::FallbackBlobBuilder { | |
235 public: | |
236 FallbackBlobBuilder() : fBuffIdx(0), fCount(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* buildIfNeeded(int* count); | |
245 | |
246 private: | |
247 enum { kWriteBufferSize = 1024 }; | |
248 | |
249 void flush(); | |
250 | |
251 SkAutoTDelete<SkTextBlobBuilder> fBuilder; | |
252 SkPaint fFont; | |
253 int fBuffIdx; | |
254 int fCount; | |
255 uint16_t fGlyphIds[kWriteBufferSize]; | |
256 SkPoint fPositions[kWriteBufferSize]; | |
257 }; | |
258 | |
259 ////////////////////////////////////////////////////////////////////////////////
//////////////////// | |
260 | |
261 GrStencilAndCoverTextContext::TextRun::TextRun(const SkPaint& fontAndStroke) | |
262 : fStroke(fontAndStroke), | |
263 fFont(fontAndStroke), | |
264 fTotalGlyphCount(0), | |
265 fFallbackGlyphCount(0), | |
266 fDetachedGlyphCache(nullptr), | |
267 fLastDrawnGlyphsID(SK_InvalidUniqueID) { | |
268 SkASSERT(!fStroke.isHairlineStyle()); // Hairlines are not supported. | |
269 | |
270 // Setting to "fill" ensures that no strokes get baked into font outlines. (
We use the GPU path | |
271 // rendering API for stroking). | |
272 fFont.setStyle(SkPaint::kFill_Style); | |
273 | |
274 if (fFont.isFakeBoldText() && SkStrokeRec::kStroke_Style != fStroke.getStyle
()) { | |
275 // Instead of letting fake bold get baked into the glyph outlines, do it
with GPU stroke. | |
276 SkScalar fakeBoldScale = SkScalarInterpFunc(fFont.getTextSize(), | |
277 kStdFakeBoldInterpKeys, | |
278 kStdFakeBoldInterpValues, | |
279 kStdFakeBoldInterpLength); | |
280 SkScalar extra = SkScalarMul(fFont.getTextSize(), fakeBoldScale); | |
281 fStroke.setStrokeStyle(fStroke.needToApply() ? fStroke.getWidth() + extr
a : extra, | |
282 true /*strokeAndFill*/); | |
283 | |
284 fFont.setFakeBoldText(false); | |
285 } | |
286 | |
287 if (!fFont.getPathEffect() && !fStroke.isDashed()) { | |
288 // We can draw the glyphs from canonically sized paths. | |
289 fTextRatio = fFont.getTextSize() / SkPaint::kCanonicalTextSizeForPaths; | |
290 fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fFont.getTextS
ize(); | |
291 | |
292 // Compensate for the glyphs being scaled by fTextRatio. | |
293 if (!fStroke.isFillStyle()) { | |
294 fStroke.setStrokeStyle(fStroke.getWidth() / fTextRatio, | |
295 SkStrokeRec::kStrokeAndFill_Style == fStroke.
getStyle()); | |
296 } | |
297 | |
298 fFont.setLinearText(true); | |
299 fFont.setLCDRenderText(false); | |
300 fFont.setAutohinted(false); | |
301 fFont.setHinting(SkPaint::kNo_Hinting); | |
302 fFont.setSubpixelText(true); | |
303 fFont.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths)); | |
304 | |
305 fUsingRawGlyphPaths = SK_Scalar1 == fFont.getTextScaleX() && | |
306 0 == fFont.getTextSkewX() && | |
307 !fFont.isFakeBoldText() && | |
308 !fFont.isVerticalText(); | |
309 } else { | |
310 fTextRatio = fTextInverseRatio = 1.0f; | |
311 fUsingRawGlyphPaths = false; | |
312 } | |
313 | |
314 // Generate the key that will be used to cache the GPU glyph path objects. | |
315 if (fUsingRawGlyphPaths && fStroke.isFillStyle()) { | |
316 static const GrUniqueKey::Domain kRawFillPathGlyphDomain = GrUniqueKey::
GenerateDomain(); | |
317 | |
318 const SkTypeface* typeface = fFont.getTypeface(); | |
319 GrUniqueKey::Builder builder(&fGlyphPathsKey, kRawFillPathGlyphDomain, 1
); | |
320 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID(
) : 0; | |
321 } else { | |
322 static const GrUniqueKey::Domain kPathGlyphDomain = GrUniqueKey::Generat
eDomain(); | |
323 | |
324 int strokeDataCount = fStroke.computeUniqueKeyFragmentData32Cnt(); | |
325 if (fUsingRawGlyphPaths) { | |
326 const SkTypeface* typeface = fFont.getTypeface(); | |
327 GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain, 2 +
strokeDataCount); | |
328 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqu
eID() : 0; | |
329 reinterpret_cast<uint32_t&>(builder[1]) = strokeDataCount; | |
330 fStroke.asUniqueKeyFragment(&builder[2]); | |
331 } else { | |
332 SkGlyphCache* glyphCache = this->getGlyphCache(); | |
333 const SkTypeface* typeface = glyphCache->getScalerContext()->getType
face(); | |
334 const SkDescriptor* desc = &glyphCache->getDescriptor(); | |
335 int descDataCount = (desc->getLength() + 3) / 4; | |
336 GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain, | |
337 2 + strokeDataCount + descDataCount); | |
338 reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqu
eID() : 0; | |
339 reinterpret_cast<uint32_t&>(builder[1]) = strokeDataCount | (descDat
aCount << 16); | |
340 fStroke.asUniqueKeyFragment(&builder[2]); | |
341 memcpy(&builder[2 + strokeDataCount], desc, desc->getLength()); | |
342 } | |
343 } | |
344 } | |
345 | |
346 GrStencilAndCoverTextContext::TextRun::~TextRun() { | |
347 this->releaseGlyphCache(); | |
348 } | |
349 | |
350 void GrStencilAndCoverTextContext::TextRun::setText(const char text[], size_t by
teLength, | |
351 SkScalar x, SkScalar y) { | |
352 SkASSERT(byteLength == 0 || text != nullptr); | |
353 | |
354 SkGlyphCache* glyphCache = this->getGlyphCache(); | |
355 SkDrawCacheProc glyphCacheProc = fFont.getDrawCacheProc(); | |
356 | |
357 fTotalGlyphCount = fFont.countText(text, byteLength); | |
358 fInstanceData.reset(InstanceData::Alloc(GrPathRendering::kTranslate_PathTran
sformType, | |
359 fTotalGlyphCount)); | |
360 | |
361 const char* stop = text + byteLength; | |
362 | |
363 // Measure first if needed. | |
364 if (fFont.getTextAlign() != SkPaint::kLeft_Align) { | |
365 SkFixed stopX = 0; | |
366 SkFixed stopY = 0; | |
367 | |
368 const char* textPtr = text; | |
369 while (textPtr < stop) { | |
370 // We don't need x, y here, since all subpixel variants will have th
e | |
371 // same advance. | |
372 const SkGlyph& glyph = glyphCacheProc(glyphCache, &textPtr, 0, 0); | |
373 | |
374 stopX += glyph.fAdvanceX; | |
375 stopY += glyph.fAdvanceY; | |
376 } | |
377 SkASSERT(textPtr == stop); | |
378 | |
379 SkScalar alignX = SkFixedToScalar(stopX) * fTextRatio; | |
380 SkScalar alignY = SkFixedToScalar(stopY) * fTextRatio; | |
381 | |
382 if (fFont.getTextAlign() == SkPaint::kCenter_Align) { | |
383 alignX = SkScalarHalf(alignX); | |
384 alignY = SkScalarHalf(alignY); | |
385 } | |
386 | |
387 x -= alignX; | |
388 y -= alignY; | |
389 } | |
390 | |
391 SkAutoKern autokern; | |
392 | |
393 SkFixed fixedSizeRatio = SkScalarToFixed(fTextRatio); | |
394 | |
395 SkFixed fx = SkScalarToFixed(x); | |
396 SkFixed fy = SkScalarToFixed(y); | |
397 FallbackBlobBuilder fallback; | |
398 while (text < stop) { | |
399 const SkGlyph& glyph = glyphCacheProc(glyphCache, &text, 0, 0); | |
400 fx += SkFixedMul(autokern.adjust(glyph), fixedSizeRatio); | |
401 if (glyph.fWidth) { | |
402 this->appendGlyph(glyph, SkPoint::Make(SkFixedToScalar(fx), SkFixedT
oScalar(fy)), | |
403 &fallback); | |
404 } | |
405 | |
406 fx += SkFixedMul(glyph.fAdvanceX, fixedSizeRatio); | |
407 fy += SkFixedMul(glyph.fAdvanceY, fixedSizeRatio); | |
408 } | |
409 | |
410 fFallbackTextBlob.reset(fallback.buildIfNeeded(&fFallbackGlyphCount)); | |
411 } | |
412 | |
413 void GrStencilAndCoverTextContext::TextRun::setPosText(const char text[], size_t
byteLength, | |
414 const SkScalar pos[], int
scalarsPerPosition, | |
415 const SkPoint& offset) { | |
416 SkASSERT(byteLength == 0 || text != nullptr); | |
417 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition); | |
418 | |
419 SkGlyphCache* glyphCache = this->getGlyphCache(); | |
420 SkDrawCacheProc glyphCacheProc = fFont.getDrawCacheProc(); | |
421 | |
422 fTotalGlyphCount = fFont.countText(text, byteLength); | |
423 fInstanceData.reset(InstanceData::Alloc(GrPathRendering::kTranslate_PathTran
sformType, | |
424 fTotalGlyphCount)); | |
425 | |
426 const char* stop = text + byteLength; | |
427 | |
428 SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition); | |
429 SkTextAlignProc alignProc(fFont.getTextAlign()); | |
430 FallbackBlobBuilder fallback; | |
431 while (text < stop) { | |
432 const SkGlyph& glyph = glyphCacheProc(glyphCache, &text, 0, 0); | |
433 if (glyph.fWidth) { | |
434 SkPoint tmsLoc; | |
435 tmsProc(pos, &tmsLoc); | |
436 SkPoint loc; | |
437 alignProc(tmsLoc, glyph, &loc); | |
438 | |
439 this->appendGlyph(glyph, loc, &fallback); | |
440 } | |
441 pos += scalarsPerPosition; | |
442 } | |
443 | |
444 fFallbackTextBlob.reset(fallback.buildIfNeeded(&fFallbackGlyphCount)); | |
445 } | |
446 | |
447 GrPathRange* GrStencilAndCoverTextContext::TextRun::createGlyphs(GrContext* ctx)
const { | |
448 GrPathRange* glyphs = static_cast<GrPathRange*>( | |
449 ctx->resourceProvider()->findAndRefResourceByUniqueKey(fGlyphPathsKe
y)); | |
450 if (nullptr == glyphs) { | |
451 if (fUsingRawGlyphPaths) { | |
452 glyphs = ctx->resourceProvider()->createGlyphs(fFont.getTypeface(),
nullptr, fStroke); | |
453 } else { | |
454 SkGlyphCache* cache = this->getGlyphCache(); | |
455 glyphs = ctx->resourceProvider()->createGlyphs(cache->getScalerConte
xt()->getTypeface(), | |
456 &cache->getDescriptor
(), | |
457 fStroke); | |
458 } | |
459 ctx->resourceProvider()->assignUniqueKeyToResource(fGlyphPathsKey, glyph
s); | |
460 } | |
461 return glyphs; | |
462 } | |
463 | |
464 inline void GrStencilAndCoverTextContext::TextRun::appendGlyph(const SkGlyph& gl
yph, | |
465 const SkPoint& po
s, | |
466 FallbackBlobBuild
er* fallback) { | |
467 // Stick the glyphs we can't draw into the fallback text blob. | |
468 if (SkMask::kARGB32_Format == glyph.fMaskFormat) { | |
469 if (!fallback->isInitialized()) { | |
470 fallback->init(fFont, fTextRatio); | |
471 } | |
472 fallback->appendGlyph(glyph.getGlyphID(), pos); | |
473 } else { | |
474 fInstanceData->append(glyph.getGlyphID(), fTextInverseRatio * pos.x(), | |
475 fTextInverseRatio * pos.y()); | |
476 } | |
477 } | |
478 | |
479 void GrStencilAndCoverTextContext::TextRun::draw(GrContext* ctx, | |
480 GrDrawContext* dc, | |
481 GrPipelineBuilder* pipelineBuil
der, | |
482 GrColor color, | |
483 const SkMatrix& viewMatrix, | |
484 SkScalar x, SkScalar y, | |
485 const SkIRect& clipBounds, | |
486 GrTextContext* fallbackTextCont
ext, | |
487 const SkPaint& originalSkPaint)
const { | |
488 SkASSERT(fInstanceData); | |
489 SkASSERT(dc->accessRenderTarget()->isStencilBufferMultisampled() || !fFont.i
sAntiAlias()); | |
490 | |
491 if (fInstanceData->count()) { | |
492 pipelineBuilder->setState(GrPipelineBuilder::kHWAntialias_Flag, fFont.is
AntiAlias()); | |
493 | |
494 GR_STATIC_CONST_SAME_STENCIL(kStencilPass, | |
495 kZero_StencilOp, | |
496 kKeep_StencilOp, | |
497 kNotEqual_StencilFunc, | |
498 0xffff, | |
499 0x0000, | |
500 0xffff); | |
501 | |
502 *pipelineBuilder->stencil() = kStencilPass; | |
503 | |
504 SkAutoTUnref<GrPathRange> glyphs(this->createGlyphs(ctx)); | |
505 if (fLastDrawnGlyphsID != glyphs->getUniqueID()) { | |
506 // Either this is the first draw or the glyphs object was purged sin
ce last draw. | |
507 glyphs->loadPathsIfNeeded(fInstanceData->indices(), fInstanceData->c
ount()); | |
508 fLastDrawnGlyphsID = glyphs->getUniqueID(); | |
509 } | |
510 | |
511 // Don't compute a bounding box. For dst copy texture, we'll opt instead
for it to just copy | |
512 // the entire dst. Realistically this is a moot point, because any conte
xt that supports | |
513 // NV_path_rendering will also support NV_blend_equation_advanced. | |
514 // For clipping we'll just skip any optimizations based on the bounds. T
his does, however, | |
515 // hurt batching. | |
516 SkRect bounds = SkRect::MakeIWH(pipelineBuilder->getRenderTarget()->widt
h(), | |
517 pipelineBuilder->getRenderTarget()->heig
ht()); | |
518 | |
519 SkAutoTUnref<GrDrawPathBatchBase> batch( | |
520 GrDrawPathRangeBatch::Create(viewMatrix, fTextRatio, fTextInverseRat
io * x, | |
521 fTextInverseRatio * y, color, | |
522 GrPathRendering::kWinding_FillType, gly
phs, fInstanceData, | |
523 bounds)); | |
524 | |
525 dc->drawPathBatch(*pipelineBuilder, batch); | |
526 } | |
527 | |
528 if (fFallbackTextBlob) { | |
529 SkPaint fallbackSkPaint(originalSkPaint); | |
530 fStroke.applyToPaint(&fallbackSkPaint); | |
531 if (!fStroke.isFillStyle()) { | |
532 fallbackSkPaint.setStrokeWidth(fStroke.getWidth() * fTextRatio); | |
533 } | |
534 | |
535 fallbackTextContext->drawTextBlob(dc, pipelineBuilder->clip(), fallbackS
kPaint, viewMatrix, | |
536 fFallbackTextBlob, x, y, nullptr, clip
Bounds); | |
537 } | |
538 } | |
539 | |
540 SkGlyphCache* GrStencilAndCoverTextContext::TextRun::getGlyphCache() const { | |
541 if (!fDetachedGlyphCache) { | |
542 fDetachedGlyphCache = fFont.detachCache(nullptr, nullptr, true /*ignoreG
amma*/); | |
543 } | |
544 return fDetachedGlyphCache; | |
545 } | |
546 | |
547 | |
548 void GrStencilAndCoverTextContext::TextRun::releaseGlyphCache() const { | |
549 if (fDetachedGlyphCache) { | |
550 SkGlyphCache::AttachCache(fDetachedGlyphCache); | |
551 fDetachedGlyphCache = nullptr; | |
552 } | |
553 } | |
554 | |
555 size_t GrStencilAndCoverTextContext::TextRun::computeSizeInCache() const { | |
556 size_t size = sizeof(TextRun) + fGlyphPathsKey.size(); | |
557 // The instance data always reserves enough space for every glyph. | |
558 size += (fTotalGlyphCount + fFallbackGlyphCount) * (sizeof(uint16_t) + 2 * s
izeof(float)); | |
559 if (fInstanceData) { | |
560 size += sizeof(InstanceData); | |
561 } | |
562 if (fFallbackTextBlob) { | |
563 size += sizeof(SkTextBlob); | |
564 } | |
565 return size; | |
566 } | |
567 | |
568 ////////////////////////////////////////////////////////////////////////////////
//////////////////// | |
569 | |
570 void GrStencilAndCoverTextContext::FallbackBlobBuilder::init(const SkPaint& font
, | |
571 SkScalar textRatio)
{ | |
572 SkASSERT(!this->isInitialized()); | |
573 fBuilder.reset(new SkTextBlobBuilder); | |
574 fFont = font; | |
575 fFont.setTextAlign(SkPaint::kLeft_Align); // The glyph positions will alread
y account for align. | |
576 fFont.setTextEncoding(SkPaint::kGlyphID_TextEncoding); | |
577 // No need for subpixel positioning with bitmap glyphs. TODO: revisit if non
-bitmap color glyphs | |
578 // show up and https://code.google.com/p/skia/issues/detail?id=4408 gets res
olved. | |
579 fFont.setSubpixelText(false); | |
580 fFont.setTextSize(fFont.getTextSize() * textRatio); | |
581 fBuffIdx = 0; | |
582 } | |
583 | |
584 void GrStencilAndCoverTextContext::FallbackBlobBuilder::appendGlyph(uint16_t gly
phId, | |
585 const SkPoin
t& pos) { | |
586 SkASSERT(this->isInitialized()); | |
587 if (fBuffIdx >= kWriteBufferSize) { | |
588 this->flush(); | |
589 } | |
590 fGlyphIds[fBuffIdx] = glyphId; | |
591 fPositions[fBuffIdx] = pos; | |
592 fBuffIdx++; | |
593 fCount++; | |
594 } | |
595 | |
596 void GrStencilAndCoverTextContext::FallbackBlobBuilder::flush() { | |
597 SkASSERT(this->isInitialized()); | |
598 SkASSERT(fBuffIdx <= kWriteBufferSize); | |
599 if (!fBuffIdx) { | |
600 return; | |
601 } | |
602 // This will automatically merge with previous runs since we use the same fo
nt. | |
603 const SkTextBlobBuilder::RunBuffer& buff = fBuilder->allocRunPos(fFont, fBuf
fIdx); | |
604 memcpy(buff.glyphs, fGlyphIds, fBuffIdx * sizeof(uint16_t)); | |
605 memcpy(buff.pos, fPositions[0].asScalars(), fBuffIdx * 2 * sizeof(SkScalar))
; | |
606 fBuffIdx = 0; | |
607 } | |
608 | |
609 const SkTextBlob* GrStencilAndCoverTextContext::FallbackBlobBuilder::buildIfNeed
ed(int *count) { | |
610 *count = fCount; | |
611 if (fCount) { | |
612 this->flush(); | |
613 return fBuilder->build(); | |
614 } | |
615 return nullptr; | |
616 } | |
OLD | NEW |