Line data Source code
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 "GrContext.h"
11 : #include "GrPath.h"
12 : #include "GrPathRange.h"
13 : #include "GrPipelineBuilder.h"
14 : #include "GrRenderTargetContext.h"
15 : #include "GrResourceProvider.h"
16 : #include "GrTextUtils.h"
17 : #include "SkAutoKern.h"
18 : #include "SkDraw.h"
19 : #include "SkDrawFilter.h"
20 : #include "SkDrawProcs.h"
21 : #include "SkGlyphCache.h"
22 : #include "SkGr.h"
23 : #include "SkPath.h"
24 : #include "SkTextBlobRunIterator.h"
25 : #include "SkTextFormatParams.h"
26 : #include "SkTextMapStateProc.h"
27 :
28 : #include "ops/GrDrawPathOp.h"
29 :
30 0 : template<typename Key, typename Val> static void delete_hash_map_entry(const Key&, Val* val) {
31 0 : SkASSERT(*val);
32 0 : delete *val;
33 0 : }
34 :
35 0 : template<typename T> static void delete_hash_table_entry(T* val) {
36 0 : SkASSERT(*val);
37 0 : delete *val;
38 0 : }
39 :
40 0 : GrStencilAndCoverTextContext::GrStencilAndCoverTextContext(GrAtlasTextContext* fallbackTextContext)
41 : : fFallbackTextContext(fallbackTextContext)
42 0 : , fCacheSize(0) {
43 0 : }
44 :
45 : GrStencilAndCoverTextContext*
46 0 : GrStencilAndCoverTextContext::Create(GrAtlasTextContext* fallbackTextContext) {
47 0 : return new GrStencilAndCoverTextContext(fallbackTextContext);;
48 : }
49 :
50 0 : GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() {
51 0 : fBlobIdCache.foreach(delete_hash_map_entry<uint32_t, TextBlob*>);
52 0 : fBlobKeyCache.foreach(delete_hash_table_entry<TextBlob*>);
53 0 : }
54 :
55 0 : bool GrStencilAndCoverTextContext::internalCanDraw(const SkPaint& skPaint) {
56 0 : if (skPaint.getRasterizer()) {
57 0 : return false;
58 : }
59 0 : if (skPaint.getMaskFilter()) {
60 0 : return false;
61 : }
62 0 : if (SkPathEffect* pe = skPaint.getPathEffect()) {
63 0 : if (pe->asADash(nullptr) != SkPathEffect::kDash_DashType) {
64 0 : return false;
65 : }
66 : }
67 : // No hairlines. They would require new paths with customized strokes for every new draw matrix.
68 0 : return SkPaint::kStroke_Style != skPaint.getStyle() || 0 != skPaint.getStrokeWidth();
69 : }
70 :
71 0 : void GrStencilAndCoverTextContext::drawText(GrContext* context, GrRenderTargetContext* rtc,
72 : const GrClip& clip, const SkPaint& skPaint,
73 : const SkMatrix& viewMatrix, const SkSurfaceProps& props,
74 : const char text[], size_t byteLength, SkScalar x,
75 : SkScalar y, const SkIRect& clipBounds) {
76 0 : if (context->abandoned()) {
77 0 : return;
78 0 : } else if (this->canDraw(skPaint, viewMatrix)) {
79 0 : if (skPaint.getTextSize() > 0) {
80 0 : TextRun run(skPaint);
81 0 : run.setText(text, byteLength, x, y);
82 0 : run.draw(context, rtc, clip, viewMatrix, props, 0, 0, clipBounds, fFallbackTextContext,
83 0 : skPaint);
84 : }
85 0 : return;
86 0 : } else if (fFallbackTextContext->canDraw(skPaint, viewMatrix, props,
87 0 : *context->caps()->shaderCaps())) {
88 0 : fFallbackTextContext->drawText(context, rtc, clip, skPaint, viewMatrix, props, text,
89 0 : byteLength, x, y, clipBounds);
90 0 : return;
91 : }
92 :
93 : // fall back to drawing as a path
94 : GrTextUtils::DrawTextAsPath(context, rtc, clip, skPaint, viewMatrix, text, byteLength, x, y,
95 0 : clipBounds);
96 : }
97 :
98 0 : void GrStencilAndCoverTextContext::drawPosText(GrContext* context, GrRenderTargetContext* rtc,
99 : const GrClip& clip, const SkPaint& skPaint,
100 : const SkMatrix& viewMatrix,
101 : const SkSurfaceProps& props, const char text[],
102 : size_t byteLength, const SkScalar pos[],
103 : int scalarsPerPosition, const SkPoint& offset,
104 : const SkIRect& clipBounds) {
105 0 : if (context->abandoned()) {
106 0 : return;
107 0 : } else if (this->canDraw(skPaint, viewMatrix)) {
108 0 : if (skPaint.getTextSize() > 0) {
109 0 : TextRun run(skPaint);
110 0 : run.setPosText(text, byteLength, pos, scalarsPerPosition, offset);
111 0 : run.draw(context, rtc, clip, viewMatrix, props, 0, 0, clipBounds, fFallbackTextContext,
112 0 : skPaint);
113 : }
114 0 : return;
115 0 : } else if (fFallbackTextContext->canDraw(skPaint, viewMatrix, props,
116 0 : *context->caps()->shaderCaps())) {
117 0 : fFallbackTextContext->drawPosText(context, rtc, clip, skPaint, viewMatrix, props, text,
118 0 : byteLength, pos, scalarsPerPosition, offset, clipBounds);
119 0 : return;
120 : }
121 :
122 : // fall back to drawing as a path
123 : GrTextUtils::DrawPosTextAsPath(context, rtc, props, clip, skPaint, viewMatrix, text,
124 0 : byteLength, pos, scalarsPerPosition, offset, clipBounds);
125 : }
126 :
127 0 : void GrStencilAndCoverTextContext::uncachedDrawTextBlob(GrContext* context,
128 : GrRenderTargetContext* rtc,
129 : const GrClip& clip,
130 : const SkPaint& skPaint,
131 : const SkMatrix& viewMatrix,
132 : const SkSurfaceProps& props,
133 : const SkTextBlob* blob,
134 : SkScalar x, SkScalar y,
135 : SkDrawFilter* drawFilter,
136 : const SkIRect& clipBounds) {
137 0 : GrTextUtils::Paint paint(&skPaint);
138 0 : GrTextUtils::RunPaint runPaint(&paint, drawFilter, props);
139 0 : SkTextBlobRunIterator it(blob);
140 0 : for (;!it.done(); it.next()) {
141 0 : if (!runPaint.modifyForRun(it)) {
142 0 : continue;
143 : }
144 0 : size_t textLen = it.glyphCount() * sizeof(uint16_t);
145 0 : const SkPoint& offset = it.offset();
146 :
147 0 : switch (it.positioning()) {
148 : case SkTextBlob::kDefault_Positioning:
149 0 : this->drawText(context, rtc, clip, runPaint, viewMatrix, props,
150 0 : (const char*)it.glyphs(), textLen, x + offset.x(), y + offset.y(),
151 0 : clipBounds);
152 0 : break;
153 : case SkTextBlob::kHorizontal_Positioning:
154 0 : this->drawPosText(context, rtc, clip, runPaint, viewMatrix, props,
155 0 : (const char*)it.glyphs(), textLen, it.pos(), 1,
156 0 : SkPoint::Make(x, y + offset.y()), clipBounds);
157 0 : break;
158 : case SkTextBlob::kFull_Positioning:
159 0 : this->drawPosText(context, rtc, clip, runPaint, viewMatrix, props,
160 0 : (const char*)it.glyphs(), textLen, it.pos(), 2,
161 0 : SkPoint::Make(x, y), clipBounds);
162 0 : break;
163 : }
164 : }
165 0 : }
166 :
167 0 : void GrStencilAndCoverTextContext::drawTextBlob(GrContext* context, GrRenderTargetContext* rtc,
168 : const GrClip& clip, const SkPaint& skPaint,
169 : const SkMatrix& viewMatrix,
170 : const SkSurfaceProps& props,
171 : const SkTextBlob* skBlob, SkScalar x, SkScalar y,
172 : SkDrawFilter* drawFilter,
173 : const SkIRect& clipBounds) {
174 0 : if (context->abandoned()) {
175 0 : return;
176 : }
177 :
178 0 : if (!this->internalCanDraw(skPaint)) {
179 0 : fFallbackTextContext->drawTextBlob(context, rtc, clip, skPaint, viewMatrix, props, skBlob,
180 0 : x, y, drawFilter, clipBounds);
181 0 : return;
182 : }
183 :
184 0 : if (drawFilter || skPaint.getPathEffect()) {
185 : // This draw can't be cached.
186 : this->uncachedDrawTextBlob(context, rtc, clip, skPaint, viewMatrix, props, skBlob, x, y,
187 0 : drawFilter, clipBounds);
188 0 : return;
189 : }
190 :
191 0 : const TextBlob& blob = this->findOrCreateTextBlob(skBlob, skPaint);
192 :
193 0 : TextBlob::Iter iter(blob);
194 0 : for (TextRun *run = iter.get(), *nextRun; run; run = nextRun) {
195 0 : nextRun = iter.next();
196 0 : run->draw(context, rtc, clip, viewMatrix, props, x, y, clipBounds, fFallbackTextContext,
197 0 : skPaint);
198 0 : run->releaseGlyphCache();
199 : }
200 : }
201 :
202 0 : static inline int style_key_cnt(const GrStyle& style) {
203 0 : int cnt = GrStyle::KeySize(style, GrStyle::Apply::kPathEffectAndStrokeRec);
204 : // We should be able to make a key because we filtered out arbitrary path effects.
205 0 : SkASSERT(cnt > 0);
206 0 : return cnt;
207 : }
208 :
209 0 : static inline void write_style_key(uint32_t* dst, const GrStyle& style) {
210 : // Pass 1 for the scale since the GPU will apply the style not GrStyle::applyToPath().
211 0 : GrStyle::WriteKey(dst, style, GrStyle::Apply::kPathEffectAndStrokeRec, SK_Scalar1);
212 0 : }
213 :
214 : const GrStencilAndCoverTextContext::TextBlob&
215 0 : GrStencilAndCoverTextContext::findOrCreateTextBlob(const SkTextBlob* skBlob,
216 : const SkPaint& skPaint) {
217 : // The font-related parameters are baked into the text blob and will override this skPaint, so
218 : // the only remaining properties that can affect a TextBlob are the ones related to stroke.
219 0 : if (SkPaint::kFill_Style == skPaint.getStyle()) { // Fast path.
220 0 : if (TextBlob** found = fBlobIdCache.find(skBlob->uniqueID())) {
221 0 : fLRUList.remove(*found);
222 0 : fLRUList.addToTail(*found);
223 0 : return **found;
224 : }
225 0 : TextBlob* blob = new TextBlob(skBlob->uniqueID(), skBlob, skPaint);
226 0 : this->purgeToFit(*blob);
227 0 : fBlobIdCache.set(skBlob->uniqueID(), blob);
228 0 : fLRUList.addToTail(blob);
229 0 : fCacheSize += blob->cpuMemorySize();
230 0 : return *blob;
231 : } else {
232 0 : GrStyle style(skPaint);
233 0 : SkSTArray<4, uint32_t, true> key;
234 0 : key.reset(1 + style_key_cnt(style));
235 0 : key[0] = skBlob->uniqueID();
236 0 : write_style_key(&key[1], style);
237 0 : if (TextBlob** found = fBlobKeyCache.find(key)) {
238 0 : fLRUList.remove(*found);
239 0 : fLRUList.addToTail(*found);
240 0 : return **found;
241 : }
242 0 : TextBlob* blob = new TextBlob(key, skBlob, skPaint);
243 0 : this->purgeToFit(*blob);
244 0 : fBlobKeyCache.set(blob);
245 0 : fLRUList.addToTail(blob);
246 0 : fCacheSize += blob->cpuMemorySize();
247 0 : return *blob;
248 : }
249 : }
250 :
251 0 : void GrStencilAndCoverTextContext::purgeToFit(const TextBlob& blob) {
252 : static const size_t maxCacheSize = 4 * 1024 * 1024; // Allow up to 4 MB for caching text blobs.
253 :
254 0 : size_t maxSizeForNewBlob = maxCacheSize - blob.cpuMemorySize();
255 0 : while (fCacheSize && fCacheSize > maxSizeForNewBlob) {
256 0 : TextBlob* lru = fLRUList.head();
257 0 : if (1 == lru->key().count()) {
258 : // 1-length keys are unterstood to be the blob id.
259 0 : fBlobIdCache.remove(lru->key()[0]);
260 : } else {
261 0 : fBlobKeyCache.remove(lru->key());
262 : }
263 0 : fLRUList.remove(lru);
264 0 : fCacheSize -= lru->cpuMemorySize();
265 0 : delete lru;
266 : }
267 0 : }
268 :
269 : ////////////////////////////////////////////////////////////////////////////////////////////////////
270 :
271 0 : void GrStencilAndCoverTextContext::TextBlob::init(const SkTextBlob* skBlob,
272 : const SkPaint& skPaint) {
273 0 : fCpuMemorySize = sizeof(TextBlob);
274 0 : SkPaint runPaint(skPaint);
275 0 : for (SkTextBlobRunIterator iter(skBlob); !iter.done(); iter.next()) {
276 0 : iter.applyFontToPaint(&runPaint); // No need to re-seed the paint.
277 0 : if (runPaint.getTextSize() <= 0) {
278 0 : continue;
279 : }
280 0 : TextRun* run = this->addToTail(runPaint);
281 :
282 0 : const char* text = reinterpret_cast<const char*>(iter.glyphs());
283 0 : size_t byteLength = sizeof(uint16_t) * iter.glyphCount();
284 0 : const SkPoint& runOffset = iter.offset();
285 :
286 0 : switch (iter.positioning()) {
287 : case SkTextBlob::kDefault_Positioning:
288 0 : run->setText(text, byteLength, runOffset.fX, runOffset.fY);
289 0 : break;
290 : case SkTextBlob::kHorizontal_Positioning:
291 0 : run->setPosText(text, byteLength, iter.pos(), 1, SkPoint::Make(0, runOffset.fY));
292 0 : break;
293 : case SkTextBlob::kFull_Positioning:
294 0 : run->setPosText(text, byteLength, iter.pos(), 2, SkPoint::Make(0, 0));
295 0 : break;
296 : }
297 :
298 0 : fCpuMemorySize += run->computeSizeInCache();
299 : }
300 0 : }
301 :
302 : ////////////////////////////////////////////////////////////////////////////////////////////////////
303 :
304 0 : class GrStencilAndCoverTextContext::FallbackBlobBuilder {
305 : public:
306 0 : FallbackBlobBuilder() : fBuffIdx(0), fCount(0) {}
307 :
308 0 : bool isInitialized() const { return fBuilder != nullptr; }
309 :
310 : void init(const SkPaint& font, SkScalar textRatio);
311 :
312 : void appendGlyph(uint16_t glyphId, const SkPoint& pos);
313 :
314 : sk_sp<SkTextBlob> makeIfNeeded(int* count);
315 :
316 : private:
317 : enum { kWriteBufferSize = 1024 };
318 :
319 : void flush();
320 :
321 : std::unique_ptr<SkTextBlobBuilder> fBuilder;
322 : SkPaint fFont;
323 : int fBuffIdx;
324 : int fCount;
325 : uint16_t fGlyphIds[kWriteBufferSize];
326 : SkPoint fPositions[kWriteBufferSize];
327 : };
328 :
329 : ////////////////////////////////////////////////////////////////////////////////////////////////////
330 :
331 0 : GrStencilAndCoverTextContext::TextRun::TextRun(const SkPaint& fontAndStroke)
332 : : fStyle(fontAndStroke)
333 : , fFont(fontAndStroke)
334 : , fTotalGlyphCount(0)
335 : , fFallbackGlyphCount(0)
336 : , fDetachedGlyphCache(nullptr)
337 0 : , fLastDrawnGlyphsID(SK_InvalidUniqueID) {
338 0 : SkASSERT(fFont.getTextSize() > 0);
339 0 : SkASSERT(!fStyle.hasNonDashPathEffect()); // Arbitrary path effects not supported.
340 0 : SkASSERT(!fStyle.isSimpleHairline()); // Hairlines are not supported.
341 :
342 : // Setting to "fill" ensures that no strokes get baked into font outlines. (We use the GPU path
343 : // rendering API for stroking).
344 0 : fFont.setStyle(SkPaint::kFill_Style);
345 :
346 0 : if (fFont.isFakeBoldText() && fStyle.isSimpleFill()) {
347 0 : const SkStrokeRec& stroke = fStyle.strokeRec();
348 : // Instead of letting fake bold get baked into the glyph outlines, do it with GPU stroke.
349 0 : SkScalar fakeBoldScale = SkScalarInterpFunc(fFont.getTextSize(),
350 : kStdFakeBoldInterpKeys,
351 : kStdFakeBoldInterpValues,
352 0 : kStdFakeBoldInterpLength);
353 0 : SkScalar extra = fFont.getTextSize() * fakeBoldScale;
354 :
355 0 : SkStrokeRec strokeRec(SkStrokeRec::kFill_InitStyle);
356 0 : strokeRec.setStrokeStyle(stroke.needToApply() ? stroke.getWidth() + extra : extra,
357 0 : true /*strokeAndFill*/);
358 0 : fStyle = GrStyle(strokeRec, fStyle.refPathEffect());
359 0 : fFont.setFakeBoldText(false);
360 : }
361 :
362 0 : if (!fFont.getPathEffect() && !fStyle.isDashed()) {
363 0 : const SkStrokeRec& stroke = fStyle.strokeRec();
364 : // We can draw the glyphs from canonically sized paths.
365 0 : fTextRatio = fFont.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
366 0 : fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fFont.getTextSize();
367 :
368 : // Compensate for the glyphs being scaled by fTextRatio.
369 0 : if (!fStyle.isSimpleFill()) {
370 0 : SkStrokeRec strokeRec(SkStrokeRec::kFill_InitStyle);
371 0 : strokeRec.setStrokeStyle(stroke.getWidth() / fTextRatio,
372 0 : SkStrokeRec::kStrokeAndFill_Style == stroke.getStyle());
373 0 : fStyle = GrStyle(strokeRec, fStyle.refPathEffect());
374 : }
375 :
376 0 : fFont.setLinearText(true);
377 0 : fFont.setLCDRenderText(false);
378 0 : fFont.setAutohinted(false);
379 0 : fFont.setHinting(SkPaint::kNo_Hinting);
380 0 : fFont.setSubpixelText(true);
381 0 : fFont.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
382 :
383 0 : fUsingRawGlyphPaths = SK_Scalar1 == fFont.getTextScaleX() &&
384 0 : 0 == fFont.getTextSkewX() &&
385 0 : !fFont.isFakeBoldText() &&
386 0 : !fFont.isVerticalText();
387 : } else {
388 0 : fTextRatio = fTextInverseRatio = 1.0f;
389 0 : fUsingRawGlyphPaths = false;
390 : }
391 :
392 : // Generate the key that will be used to cache the GPU glyph path objects.
393 0 : if (fUsingRawGlyphPaths && fStyle.isSimpleFill()) {
394 0 : static const GrUniqueKey::Domain kRawFillPathGlyphDomain = GrUniqueKey::GenerateDomain();
395 :
396 0 : const SkTypeface* typeface = fFont.getTypeface();
397 0 : GrUniqueKey::Builder builder(&fGlyphPathsKey, kRawFillPathGlyphDomain, 1);
398 0 : reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
399 : } else {
400 0 : static const GrUniqueKey::Domain kPathGlyphDomain = GrUniqueKey::GenerateDomain();
401 :
402 0 : int styleDataCount = GrStyle::KeySize(fStyle, GrStyle::Apply::kPathEffectAndStrokeRec);
403 : // Key should be valid since we opted out of drawing arbitrary path effects.
404 0 : SkASSERT(styleDataCount >= 0);
405 0 : if (fUsingRawGlyphPaths) {
406 0 : const SkTypeface* typeface = fFont.getTypeface();
407 0 : GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain, 2 + styleDataCount);
408 0 : reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
409 0 : reinterpret_cast<uint32_t&>(builder[1]) = styleDataCount;
410 0 : if (styleDataCount) {
411 0 : write_style_key(&builder[2], fStyle);
412 : }
413 : } else {
414 0 : SkGlyphCache* glyphCache = this->getGlyphCache();
415 0 : const SkTypeface* typeface = glyphCache->getScalerContext()->getTypeface();
416 0 : const SkDescriptor* desc = &glyphCache->getDescriptor();
417 0 : int descDataCount = (desc->getLength() + 3) / 4;
418 : GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain,
419 0 : 2 + styleDataCount + descDataCount);
420 0 : reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0;
421 0 : reinterpret_cast<uint32_t&>(builder[1]) = styleDataCount | (descDataCount << 16);
422 0 : if (styleDataCount) {
423 0 : write_style_key(&builder[2], fStyle);
424 : }
425 0 : memcpy(&builder[2 + styleDataCount], desc, desc->getLength());
426 : }
427 : }
428 0 : }
429 :
430 0 : GrStencilAndCoverTextContext::TextRun::~TextRun() {
431 0 : this->releaseGlyphCache();
432 0 : }
433 :
434 0 : void GrStencilAndCoverTextContext::TextRun::setText(const char text[], size_t byteLength,
435 : SkScalar x, SkScalar y) {
436 0 : SkASSERT(byteLength == 0 || text != nullptr);
437 :
438 0 : SkGlyphCache* glyphCache = this->getGlyphCache();
439 0 : SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(fFont.getTextEncoding(),
440 0 : fFont.isDevKernText(),
441 0 : true);
442 :
443 0 : fTotalGlyphCount = fFont.countText(text, byteLength);
444 0 : fInstanceData.reset(InstanceData::Alloc(GrPathRendering::kTranslate_PathTransformType,
445 0 : fTotalGlyphCount));
446 :
447 0 : const char* stop = text + byteLength;
448 :
449 : // Measure first if needed.
450 0 : if (fFont.getTextAlign() != SkPaint::kLeft_Align) {
451 0 : SkScalar stopX = 0;
452 0 : SkScalar stopY = 0;
453 :
454 0 : const char* textPtr = text;
455 0 : while (textPtr < stop) {
456 : // We don't need x, y here, since all subpixel variants will have the
457 : // same advance.
458 0 : const SkGlyph& glyph = glyphCacheProc(glyphCache, &textPtr);
459 :
460 0 : stopX += SkFloatToScalar(glyph.fAdvanceX);
461 0 : stopY += SkFloatToScalar(glyph.fAdvanceY);
462 : }
463 0 : SkASSERT(textPtr == stop);
464 :
465 0 : SkScalar alignX = stopX * fTextRatio;
466 0 : SkScalar alignY = stopY * fTextRatio;
467 :
468 0 : if (fFont.getTextAlign() == SkPaint::kCenter_Align) {
469 0 : alignX = SkScalarHalf(alignX);
470 0 : alignY = SkScalarHalf(alignY);
471 : }
472 :
473 0 : x -= alignX;
474 0 : y -= alignY;
475 : }
476 :
477 0 : SkAutoKern autokern;
478 :
479 0 : FallbackBlobBuilder fallback;
480 0 : while (text < stop) {
481 0 : const SkGlyph& glyph = glyphCacheProc(glyphCache, &text);
482 0 : x += autokern.adjust(glyph) * fTextRatio;
483 0 : if (glyph.fWidth) {
484 0 : this->appendGlyph(glyph, SkPoint::Make(x, y), &fallback);
485 : }
486 :
487 0 : x += SkFloatToScalar(glyph.fAdvanceX) * fTextRatio;
488 0 : y += SkFloatToScalar(glyph.fAdvanceY) * fTextRatio;
489 : }
490 :
491 0 : fFallbackTextBlob = fallback.makeIfNeeded(&fFallbackGlyphCount);
492 0 : }
493 :
494 0 : void GrStencilAndCoverTextContext::TextRun::setPosText(const char text[], size_t byteLength,
495 : const SkScalar pos[], int scalarsPerPosition,
496 : const SkPoint& offset) {
497 0 : SkASSERT(byteLength == 0 || text != nullptr);
498 0 : SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
499 :
500 0 : SkGlyphCache* glyphCache = this->getGlyphCache();
501 0 : SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(fFont.getTextEncoding(),
502 0 : fFont.isDevKernText(),
503 0 : true);
504 :
505 0 : fTotalGlyphCount = fFont.countText(text, byteLength);
506 0 : fInstanceData.reset(InstanceData::Alloc(GrPathRendering::kTranslate_PathTransformType,
507 0 : fTotalGlyphCount));
508 :
509 0 : const char* stop = text + byteLength;
510 :
511 0 : SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
512 0 : SkTextAlignProc alignProc(fFont.getTextAlign());
513 0 : FallbackBlobBuilder fallback;
514 0 : while (text < stop) {
515 0 : const SkGlyph& glyph = glyphCacheProc(glyphCache, &text);
516 0 : if (glyph.fWidth) {
517 : SkPoint tmsLoc;
518 0 : tmsProc(pos, &tmsLoc);
519 : SkPoint loc;
520 0 : alignProc(tmsLoc, glyph, &loc);
521 :
522 0 : this->appendGlyph(glyph, loc, &fallback);
523 : }
524 0 : pos += scalarsPerPosition;
525 : }
526 :
527 0 : fFallbackTextBlob = fallback.makeIfNeeded(&fFallbackGlyphCount);
528 0 : }
529 :
530 0 : GrPathRange* GrStencilAndCoverTextContext::TextRun::createGlyphs(
531 : GrResourceProvider* resourceProvider) const {
532 : GrPathRange* glyphs = static_cast<GrPathRange*>(
533 0 : resourceProvider->findAndRefResourceByUniqueKey(fGlyphPathsKey));
534 0 : if (nullptr == glyphs) {
535 0 : if (fUsingRawGlyphPaths) {
536 0 : SkScalerContextEffects noeffects;
537 0 : glyphs = resourceProvider->createGlyphs(fFont.getTypeface(), noeffects,
538 0 : nullptr, fStyle);
539 : } else {
540 0 : SkGlyphCache* cache = this->getGlyphCache();
541 0 : glyphs = resourceProvider->createGlyphs(cache->getScalerContext()->getTypeface(),
542 0 : cache->getScalerContext()->getEffects(),
543 0 : &cache->getDescriptor(),
544 0 : fStyle);
545 : }
546 0 : resourceProvider->assignUniqueKeyToResource(fGlyphPathsKey, glyphs);
547 : }
548 0 : return glyphs;
549 : }
550 :
551 0 : inline void GrStencilAndCoverTextContext::TextRun::appendGlyph(const SkGlyph& glyph,
552 : const SkPoint& pos,
553 : FallbackBlobBuilder* fallback) {
554 : // Stick the glyphs we can't draw into the fallback text blob.
555 0 : if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
556 0 : if (!fallback->isInitialized()) {
557 0 : fallback->init(fFont, fTextRatio);
558 : }
559 0 : fallback->appendGlyph(glyph.getGlyphID(), pos);
560 : } else {
561 0 : fInstanceData->append(glyph.getGlyphID(), fTextInverseRatio * pos.x(),
562 0 : fTextInverseRatio * pos.y());
563 : }
564 0 : }
565 :
566 0 : void GrStencilAndCoverTextContext::TextRun::draw(GrContext* ctx,
567 : GrRenderTargetContext* renderTargetContext,
568 : const GrClip& clip, const SkMatrix& viewMatrix,
569 : const SkSurfaceProps& props, SkScalar x,
570 : SkScalar y, const SkIRect& clipBounds,
571 : GrAtlasTextContext* fallbackTextContext,
572 : const SkPaint& originalSkPaint) const {
573 0 : SkASSERT(fInstanceData);
574 :
575 0 : if (fInstanceData->count()) {
576 : static constexpr GrUserStencilSettings kCoverPass(
577 : GrUserStencilSettings::StaticInit<
578 : 0x0000,
579 : GrUserStencilTest::kNotEqual, // Stencil pass accounts for clip.
580 : 0xffff,
581 : GrUserStencilOp::kZero,
582 : GrUserStencilOp::kKeep,
583 : 0xffff>()
584 : );
585 :
586 0 : sk_sp<GrPathRange> glyphs(this->createGlyphs(ctx->resourceProvider()));
587 0 : if (fLastDrawnGlyphsID != glyphs->uniqueID()) {
588 : // Either this is the first draw or the glyphs object was purged since last draw.
589 0 : glyphs->loadPathsIfNeeded(fInstanceData->indices(), fInstanceData->count());
590 0 : fLastDrawnGlyphsID = glyphs->uniqueID();
591 : }
592 :
593 0 : GrPaint grPaint;
594 0 : if (!SkPaintToGrPaint(ctx, renderTargetContext, originalSkPaint, viewMatrix, &grPaint)) {
595 0 : return;
596 : }
597 :
598 : // Don't compute a bounding box. For dst copy texture, we'll opt instead for it to just copy
599 : // the entire dst. Realistically this is a moot point, because any context that supports
600 : // NV_path_rendering will also support NV_blend_equation_advanced.
601 : // For clipping we'll just skip any optimizations based on the bounds. This does, however,
602 : // hurt GrOp combining.
603 : const SkRect bounds = SkRect::MakeIWH(renderTargetContext->width(),
604 0 : renderTargetContext->height());
605 :
606 : // The run's "font" overrides the anti-aliasing of the passed in SkPaint!
607 : GrAAType aaType;
608 0 : if (this->aa() == GrAA::kYes) {
609 0 : SkASSERT(renderTargetContext->isStencilBufferMultisampled());
610 0 : aaType = renderTargetContext->isUnifiedMultisampled() ? GrAAType::kMSAA
611 : : GrAAType::kMixedSamples;
612 : } else {
613 0 : aaType = GrAAType::kNone;
614 : }
615 :
616 : std::unique_ptr<GrDrawOp> op = GrDrawPathRangeOp::Make(
617 0 : viewMatrix, fTextRatio, fTextInverseRatio * x, fTextInverseRatio * y,
618 0 : std::move(grPaint), GrPathRendering::kWinding_FillType, aaType, glyphs.get(),
619 0 : fInstanceData.get(), bounds);
620 :
621 0 : renderTargetContext->addDrawOp(clip, std::move(op));
622 : }
623 :
624 0 : if (fFallbackTextBlob) {
625 0 : SkPaint fallbackSkPaint(originalSkPaint);
626 0 : fStyle.strokeRec().applyToPaint(&fallbackSkPaint);
627 0 : if (!fStyle.isSimpleFill()) {
628 0 : fallbackSkPaint.setStrokeWidth(fStyle.strokeRec().getWidth() * fTextRatio);
629 : }
630 :
631 : fallbackTextContext->drawTextBlob(ctx, renderTargetContext, clip, fallbackSkPaint,
632 0 : viewMatrix, props, fFallbackTextBlob.get(), x, y, nullptr,
633 0 : clipBounds);
634 : }
635 : }
636 :
637 0 : SkGlyphCache* GrStencilAndCoverTextContext::TextRun::getGlyphCache() const {
638 0 : if (!fDetachedGlyphCache) {
639 0 : fDetachedGlyphCache = fFont.detachCache(nullptr, SkPaint::kNone_ScalerContextFlags,
640 : nullptr);
641 : }
642 0 : return fDetachedGlyphCache;
643 : }
644 :
645 :
646 0 : void GrStencilAndCoverTextContext::TextRun::releaseGlyphCache() const {
647 0 : if (fDetachedGlyphCache) {
648 0 : SkGlyphCache::AttachCache(fDetachedGlyphCache);
649 0 : fDetachedGlyphCache = nullptr;
650 : }
651 0 : }
652 :
653 0 : size_t GrStencilAndCoverTextContext::TextRun::computeSizeInCache() const {
654 0 : size_t size = sizeof(TextRun) + fGlyphPathsKey.size();
655 : // The instance data always reserves enough space for every glyph.
656 0 : size += (fTotalGlyphCount + fFallbackGlyphCount) * (sizeof(uint16_t) + 2 * sizeof(float));
657 0 : if (fInstanceData) {
658 0 : size += sizeof(InstanceData);
659 : }
660 0 : if (fFallbackTextBlob) {
661 0 : size += sizeof(SkTextBlob);
662 : }
663 0 : return size;
664 : }
665 :
666 : ////////////////////////////////////////////////////////////////////////////////////////////////////
667 :
668 0 : void GrStencilAndCoverTextContext::FallbackBlobBuilder::init(const SkPaint& font,
669 : SkScalar textRatio) {
670 0 : SkASSERT(!this->isInitialized());
671 0 : fBuilder.reset(new SkTextBlobBuilder);
672 0 : fFont = font;
673 0 : fFont.setTextAlign(SkPaint::kLeft_Align); // The glyph positions will already account for align.
674 0 : fFont.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
675 : // No need for subpixel positioning with bitmap glyphs. TODO: revisit if non-bitmap color glyphs
676 : // show up and https://code.google.com/p/skia/issues/detail?id=4408 gets resolved.
677 0 : fFont.setSubpixelText(false);
678 0 : fFont.setTextSize(fFont.getTextSize() * textRatio);
679 0 : fBuffIdx = 0;
680 0 : }
681 :
682 0 : void GrStencilAndCoverTextContext::FallbackBlobBuilder::appendGlyph(uint16_t glyphId,
683 : const SkPoint& pos) {
684 0 : SkASSERT(this->isInitialized());
685 0 : if (fBuffIdx >= kWriteBufferSize) {
686 0 : this->flush();
687 : }
688 0 : fGlyphIds[fBuffIdx] = glyphId;
689 0 : fPositions[fBuffIdx] = pos;
690 0 : fBuffIdx++;
691 0 : fCount++;
692 0 : }
693 :
694 0 : void GrStencilAndCoverTextContext::FallbackBlobBuilder::flush() {
695 0 : SkASSERT(this->isInitialized());
696 0 : SkASSERT(fBuffIdx <= kWriteBufferSize);
697 0 : if (!fBuffIdx) {
698 0 : return;
699 : }
700 : // This will automatically merge with previous runs since we use the same font.
701 0 : const SkTextBlobBuilder::RunBuffer& buff = fBuilder->allocRunPos(fFont, fBuffIdx);
702 0 : memcpy(buff.glyphs, fGlyphIds, fBuffIdx * sizeof(uint16_t));
703 0 : memcpy(buff.pos, fPositions[0].asScalars(), fBuffIdx * 2 * sizeof(SkScalar));
704 0 : fBuffIdx = 0;
705 : }
706 :
707 0 : sk_sp<SkTextBlob> GrStencilAndCoverTextContext::FallbackBlobBuilder::makeIfNeeded(int *count) {
708 0 : *count = fCount;
709 0 : if (fCount) {
710 0 : this->flush();
711 0 : return fBuilder->make();
712 : }
713 0 : return nullptr;
714 : }
|