1 /*
2 * Copyright 2018 The Android Open Source Project
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 "SkGlyphRunPainter.h"
9
10 #if SK_SUPPORT_GPU
11 #include "GrColorSpaceInfo.h"
12 #include "GrRenderTargetContext.h"
13 #include "SkGr.h"
14 #include "text/GrTextBlobCache.h"
15 #include "text/GrTextContext.h"
16 #endif
17
18 #include "SkColorFilter.h"
19 #include "SkDevice.h"
20 #include "SkDistanceFieldGen.h"
21 #include "SkDraw.h"
22 #include "SkFontPriv.h"
23 #include "SkMaskFilter.h"
24 #include "SkPaintPriv.h"
25 #include "SkPathEffect.h"
26 #include "SkRasterClip.h"
27 #include "SkRemoteGlyphCacheImpl.h"
28 #include "SkStrike.h"
29 #include "SkStrikeCache.h"
30 #include "SkTDArray.h"
31 #include "SkTraceEvent.h"
32
33 // -- SkGlyphCacheCommon ---------------------------------------------------------------------------
34
PixelRounding(bool isSubpixel,SkAxisAlignment axisAlignment)35 SkVector SkStrikeCommon::PixelRounding(bool isSubpixel, SkAxisAlignment axisAlignment) {
36 if (!isSubpixel) {
37 return {SK_ScalarHalf, SK_ScalarHalf};
38 } else {
39 static constexpr SkScalar kSubpixelRounding = SkFixedToScalar(SkGlyph::kSubpixelRound);
40 switch (axisAlignment) {
41 case kX_SkAxisAlignment:
42 return {kSubpixelRounding, SK_ScalarHalf};
43 case kY_SkAxisAlignment:
44 return {SK_ScalarHalf, kSubpixelRounding};
45 case kNone_SkAxisAlignment:
46 return {kSubpixelRounding, kSubpixelRounding};
47 }
48 }
49
50 // Some compilers need this.
51 return {0, 0};
52 }
53
SubpixelLookup(SkAxisAlignment axisAlignment,SkPoint position)54 SkIPoint SkStrikeCommon::SubpixelLookup(SkAxisAlignment axisAlignment, SkPoint position) {
55 // TODO: SkScalarFraction uses truncf to calculate the fraction. This should be floorf.
56 SkFixed lookupX = SkScalarToFixed(SkScalarFraction(position.x())),
57 lookupY = SkScalarToFixed(SkScalarFraction(position.y()));
58
59 // Snap to a given axis if alignment is requested.
60 if (axisAlignment == kX_SkAxisAlignment) {
61 lookupY = 0;
62 } else if (axisAlignment == kY_SkAxisAlignment) {
63 lookupX = 0;
64 }
65
66 return {lookupX, lookupY};
67 }
68
GlyphTooBigForAtlas(const SkGlyph & glyph)69 bool SkStrikeCommon::GlyphTooBigForAtlas(const SkGlyph& glyph) {
70 return glyph.fWidth > kSkSideTooBigForAtlas || glyph.fHeight > kSkSideTooBigForAtlas;
71 }
72
73 // -- SkGlyphRunListPainter ------------------------------------------------------------------------
SkGlyphRunListPainter(const SkSurfaceProps & props,SkColorType colorType,SkScalerContextFlags flags)74 SkGlyphRunListPainter::SkGlyphRunListPainter(
75 const SkSurfaceProps& props, SkColorType colorType, SkScalerContextFlags flags)
76 : fDeviceProps{props}
77 , fBitmapFallbackProps{SkSurfaceProps{props.flags(), kUnknown_SkPixelGeometry}}
78 , fColorType{colorType}
79 , fScalerContextFlags{flags} {}
80
81 #if SK_SUPPORT_GPU
82
83 // TODO: unify with code in GrTextContext.cpp
compute_scaler_context_flags(const GrColorSpaceInfo & colorSpaceInfo)84 static SkScalerContextFlags compute_scaler_context_flags(
85 const GrColorSpaceInfo& colorSpaceInfo) {
86 // If we're doing linear blending, then we can disable the gamma hacks.
87 // Otherwise, leave them on. In either case, we still want the contrast boost:
88 // TODO: Can we be even smarter about mask gamma based on the dest transfer function?
89 if (colorSpaceInfo.isLinearlyBlended()) {
90 return SkScalerContextFlags::kBoostContrast;
91 } else {
92 return SkScalerContextFlags::kFakeGammaAndBoostContrast;
93 }
94 }
95
SkGlyphRunListPainter(const SkSurfaceProps & props,const GrColorSpaceInfo & csi)96 SkGlyphRunListPainter::SkGlyphRunListPainter(
97 const SkSurfaceProps& props, const GrColorSpaceInfo& csi)
98 : SkGlyphRunListPainter(props, kUnknown_SkColorType, compute_scaler_context_flags(csi)) {}
99
SkGlyphRunListPainter(const GrRenderTargetContext & rtc)100 SkGlyphRunListPainter::SkGlyphRunListPainter(const GrRenderTargetContext& rtc)
101 : SkGlyphRunListPainter{rtc.surfaceProps(), rtc.colorSpaceInfo()} {}
102
103 #endif
104
ShouldDrawAsPath(const SkPaint & paint,const SkFont & font,const SkMatrix & matrix)105 bool SkGlyphRunListPainter::ShouldDrawAsPath(
106 const SkPaint& paint, const SkFont& font, const SkMatrix& matrix) {
107 // hairline glyphs are fast enough so we don't need to cache them
108 if (SkPaint::kStroke_Style == paint.getStyle() && 0 == paint.getStrokeWidth()) {
109 return true;
110 }
111
112 // we don't cache perspective
113 if (matrix.hasPerspective()) {
114 return true;
115 }
116
117 return SkFontPriv::TooBigToUseCache(matrix, SkFontPriv::MakeTextMatrix(font), 1024);
118 }
119
check_glyph_position(SkPoint position)120 static bool check_glyph_position(SkPoint position) {
121 // Prevent glyphs from being drawn outside of or straddling the edge of device space.
122 // Comparisons written a little weirdly so that NaN coordinates are treated safely.
123 auto gt = [](float a, int b) { return !(a <= (float)b); };
124 auto lt = [](float a, int b) { return !(a >= (float)b); };
125 return !(gt(position.fX, INT_MAX - (INT16_MAX + SkTo<int>(UINT16_MAX))) ||
126 lt(position.fX, INT_MIN - (INT16_MIN + 0 /*UINT16_MIN*/)) ||
127 gt(position.fY, INT_MAX - (INT16_MAX + SkTo<int>(UINT16_MAX))) ||
128 lt(position.fY, INT_MIN - (INT16_MIN + 0 /*UINT16_MIN*/)));
129 }
130
create_mask(const SkGlyph & glyph,SkPoint position,const void * image)131 static SkMask create_mask(const SkGlyph& glyph, SkPoint position, const void* image) {
132 SkMask mask;
133 int left = SkScalarFloorToInt(position.fX);
134 int top = SkScalarFloorToInt(position.fY);
135
136 left += glyph.fLeft;
137 top += glyph.fTop;
138
139 int right = left + glyph.fWidth;
140 int bottom = top + glyph.fHeight;
141
142 mask.fBounds.set(left, top, right, bottom);
143 SkASSERT(!mask.fBounds.isEmpty());
144
145 mask.fImage = (uint8_t*)image;
146 mask.fRowBytes = glyph.rowBytes();
147 mask.fFormat = static_cast<SkMask::Format>(glyph.fMaskFormat);
148
149 return mask;
150 }
151
drawForBitmapDevice(const SkGlyphRunList & glyphRunList,const SkMatrix & deviceMatrix,const BitmapDevicePainter * bitmapDevice)152 void SkGlyphRunListPainter::drawForBitmapDevice(
153 const SkGlyphRunList& glyphRunList, const SkMatrix& deviceMatrix,
154 const BitmapDevicePainter* bitmapDevice) {
155 ScopedBuffers _ = this->ensureBuffers(glyphRunList);
156
157 const SkPaint& runPaint = glyphRunList.paint();
158 // The bitmap blitters can only draw lcd text to a N32 bitmap in srcOver. Otherwise,
159 // convert the lcd text into A8 text. The props communicates this to the scaler.
160 auto& props = (kN32_SkColorType == fColorType && runPaint.isSrcOver())
161 ? fDeviceProps
162 : fBitmapFallbackProps;
163
164 SkPoint origin = glyphRunList.origin();
165 for (auto& glyphRun : glyphRunList) {
166 const SkFont& runFont = glyphRun.font();
167 auto runSize = glyphRun.runSize();
168
169 if (ShouldDrawAsPath(runPaint, runFont, deviceMatrix)) {
170 SkMatrix::MakeTrans(origin.x(), origin.y()).mapPoints(
171 fPositions, glyphRun.positions().data(), runSize);
172 // setup our std pathPaint, in hopes of getting hits in the cache
173 SkPaint pathPaint(runPaint);
174 SkFont pathFont{runFont};
175 SkScalar textScale = pathFont.setupForAsPaths(&pathPaint);
176
177 auto pathCache = SkStrikeCache::FindOrCreateStrikeExclusive(
178 pathFont, pathPaint, props,
179 fScalerContextFlags, SkMatrix::I());
180
181 SkTDArray<PathAndPos> pathsAndPositions;
182 pathsAndPositions.setReserve(runSize);
183 SkPoint* positionCursor = fPositions;
184 for (auto glyphID : glyphRun.glyphsIDs()) {
185 SkPoint position = *positionCursor++;
186 if (check_glyph_position(position)) {
187 const SkGlyph& glyph = pathCache->getGlyphMetrics(glyphID, {0, 0});
188 if (!glyph.isEmpty()) {
189 const SkPath* path = pathCache->findPath(glyph);
190 if (path != nullptr) {
191 pathsAndPositions.push_back(PathAndPos{path, position});
192 }
193 }
194 }
195 }
196
197 // The paint we draw paths with must have the same anti-aliasing state as the runFont
198 // allowing the paths to have the same edging as the glyph masks.
199 pathPaint = runPaint;
200 pathPaint.setAntiAlias(runFont.hasSomeAntiAliasing());
201
202 bitmapDevice->paintPaths(
203 SkSpan<const PathAndPos>{pathsAndPositions.begin(), pathsAndPositions.size()},
204 textScale, pathPaint);
205 } else {
206 auto cache = SkStrikeCache::FindOrCreateStrikeExclusive(
207 runFont, runPaint, props,
208 fScalerContextFlags, deviceMatrix);
209
210 // Add rounding and origin.
211 SkMatrix matrix = deviceMatrix;
212 matrix.preTranslate(origin.x(), origin.y());
213 SkPoint rounding = cache->rounding();
214 matrix.postTranslate(rounding.x(), rounding.y());
215 matrix.mapPoints(fPositions, glyphRun.positions().data(), runSize);
216
217 SkTDArray<SkMask> masks;
218 masks.setReserve(runSize);
219 const SkPoint* positionCursor = fPositions;
220 for (auto glyphID : glyphRun.glyphsIDs()) {
221 auto position = *positionCursor++;
222 if (check_glyph_position(position)) {
223 const SkGlyph& glyph = cache->getGlyphMetrics(glyphID, position);
224 const void* image;
225 if (!glyph.isEmpty() && (image = cache->findImage(glyph))) {
226 masks.push_back(create_mask(glyph, position, image));
227 }
228 }
229 }
230 bitmapDevice->paintMasks(SkSpan<const SkMask>{masks.begin(), masks.size()}, runPaint);
231 }
232 }
233 }
234
235 // Getting glyphs to the screen in a fallback situation can be complex. Here is the set of
236 // transformations that have to happen. Normally, they would all be accommodated by the font
237 // scaler, but the atlas has an upper limit to the glyphs it can handle. So the GPU is used to
238 // make up the difference from the smaller atlas size to the larger size needed by the final
239 // transform. Here are the transformations that are applied.
240 //
241 // final transform = [view matrix] * [text scale] * [text size]
242 //
243 // There are three cases:
244 // * Go Fast - view matrix is scale and translate, and all the glyphs are small enough
245 // Just scale the positions, and have the glyph cache handle the view matrix transformation.
246 // The text scale is 1.
247 // * It's complicated - view matrix is not scale and translate, and the glyphs are small enough
248 // The glyph cache does not handle the view matrix, but stores the glyphs at the text size
249 // specified by the run paint. The GPU handles the rotation, etc. specified by the view matrix.
250 // The text scale is 1.
251 // * Too big - The glyphs are too big to fit in the atlas
252 // Reduce the text size so the glyphs will fit in the atlas, but don't apply any
253 // transformations from the view matrix. Calculate a text scale based on that reduction. This
254 // scale factor is used to increase the size of the destination rectangles. The destination
255 // rectangles are then scaled, rotated, etc. by the GPU using the view matrix.
processARGBFallback(SkScalar maxGlyphDimension,const SkPaint & runPaint,const SkFont & runFont,const SkMatrix & viewMatrix,SkScalar textScale,ARGBFallback argbFallback)256 void SkGlyphRunListPainter::processARGBFallback(
257 SkScalar maxGlyphDimension, const SkPaint& runPaint, const SkFont& runFont,
258 const SkMatrix& viewMatrix, SkScalar textScale, ARGBFallback argbFallback) {
259 SkASSERT(!fARGBGlyphsIDs.empty());
260
261 SkScalar maxScale = viewMatrix.getMaxScale();
262
263 // This is a conservative estimate of the longest dimension among all the glyph widths and
264 // heights.
265 SkScalar conservativeMaxGlyphDimension = maxGlyphDimension * textScale * maxScale;
266
267 // If the situation that the matrix is simple, and all the glyphs are small enough. Go fast!
268 bool useFastPath =
269 viewMatrix.isScaleTranslate() && conservativeMaxGlyphDimension <= maxGlyphDimension;
270
271 auto glyphIDs = SkSpan<const SkGlyphID>{fARGBGlyphsIDs};
272
273 // A scaled and translated transform is the common case, and is handled directly in fallback.
274 // Even if the transform is scale and translate, fallback must be careful to use glyphs that
275 // fit in the atlas. If a glyph will not fit in the atlas, then the general transform case is
276 // used to render the glyphs.
277 if (useFastPath) {
278 // Translate the positions to device space.
279 viewMatrix.mapPoints(fARGBPositions.data(), fARGBPositions.size());
280 for (SkPoint& point : fARGBPositions) {
281 point.fX = SkScalarFloorToScalar(point.fX);
282 point.fY = SkScalarFloorToScalar(point.fY);
283 }
284
285 auto positions = SkSpan<const SkPoint>{fARGBPositions};
286 argbFallback(runPaint,
287 runFont,
288 glyphIDs,
289 positions,
290 SK_Scalar1,
291 viewMatrix,
292 kTransformDone);
293
294 } else {
295 // If the matrix is complicated or if scaling is used to fit the glyphs in the cache,
296 // then this case is used.
297
298 // Subtract 2 to account for the bilerp pad around the glyph
299 SkScalar maxAtlasDimension = SkStrikeCommon::kSkSideTooBigForAtlas - 2;
300
301 SkScalar runFontTextSize = runFont.getSize();
302
303 // Scale the text size down so the long side of all the glyphs will fit in the atlas.
304 SkScalar reducedTextSize =
305 (maxAtlasDimension / conservativeMaxGlyphDimension) * runFontTextSize;
306
307 // If there's a glyph in the font that's particularly large, it's possible
308 // that fScaledFallbackTextSize may end up minimizing too much. We'd rather skip
309 // that glyph than make the others blurry, so we set a minimum size of half the
310 // maximum text size to avoid this case.
311 SkScalar fallbackTextSize =
312 SkScalarFloorToScalar(std::max(reducedTextSize, 0.5f * runFontTextSize));
313
314 // Don't allow the text size to get too big. This will also improve glyph cache hit rate
315 // for larger text sizes.
316 fallbackTextSize = std::min(fallbackTextSize, 256.0f);
317
318 SkFont fallbackFont{runFont};
319 fallbackFont.setSize(fallbackTextSize);
320 SkScalar fallbackTextScale = runFontTextSize / fallbackTextSize;
321 auto positions = SkSpan<const SkPoint>{fARGBPositions};
322 argbFallback(runPaint,
323 fallbackFont,
324 glyphIDs,
325 positions,
326 fallbackTextScale,
327 SkMatrix::I(),
328 kDoTransform);
329 }
330 }
331
332 // Beware! The following code will end up holding two glyph caches at the same time, but they
333 // will not be the same cache (which would cause two separate caches to be created).
334 template <typename PerEmptyT, typename PerPathT>
drawGlyphRunAsPathWithARGBFallback(SkStrikeInterface * pathCache,const SkGlyphRun & glyphRun,SkPoint origin,const SkPaint & runPaint,const SkMatrix & viewMatrix,SkScalar textScale,PerEmptyT && perEmpty,PerPathT && perPath,ARGBFallback && argbFallback)335 void SkGlyphRunListPainter::drawGlyphRunAsPathWithARGBFallback(
336 SkStrikeInterface* pathCache, const SkGlyphRun& glyphRun,
337 SkPoint origin, const SkPaint& runPaint, const SkMatrix& viewMatrix, SkScalar textScale,
338 PerEmptyT&& perEmpty, PerPathT&& perPath, ARGBFallback&& argbFallback) {
339 fARGBGlyphsIDs.clear();
340 fARGBPositions.clear();
341 SkScalar maxFallbackDimension{-SK_ScalarInfinity};
342
343 const SkPoint* positionCursor = glyphRun.positions().data();
344 for (auto glyphID : glyphRun.glyphsIDs()) {
345 SkPoint glyphPos = origin + *positionCursor++;
346 const SkGlyph& glyph = pathCache->getGlyphMetrics(glyphID, {0, 0});
347 if (glyph.isEmpty()) {
348 perEmpty(glyph, glyphPos);
349 } else if (glyph.fMaskFormat != SkMask::kARGB32_Format) {
350 if (pathCache->hasPath(glyph)) {
351 perPath(glyph, glyphPos);
352 } else {
353 perEmpty(glyph, glyphPos);
354 }
355 } else {
356 SkScalar largestDimension = std::max(glyph.fWidth, glyph.fHeight);
357 maxFallbackDimension = std::max(maxFallbackDimension, largestDimension);
358 fARGBGlyphsIDs.push_back(glyphID);
359 fARGBPositions.push_back(glyphPos);
360 }
361 }
362
363 if (!fARGBGlyphsIDs.empty()) {
364 this->processARGBFallback(
365 maxFallbackDimension, runPaint, glyphRun.font(), viewMatrix, textScale,
366 std::move(argbFallback));
367
368 }
369 }
370
371 template <typename EmptiesT, typename MasksT, typename PathsT>
drawGlyphRunAsBMPWithPathFallback(SkStrikeInterface * cache,const SkGlyphRun & glyphRun,SkPoint origin,const SkMatrix & deviceMatrix,EmptiesT && processEmpties,MasksT && processMasks,PathsT && processPaths)372 void SkGlyphRunListPainter::drawGlyphRunAsBMPWithPathFallback(
373 SkStrikeInterface* cache, const SkGlyphRun& glyphRun,
374 SkPoint origin, const SkMatrix& deviceMatrix,
375 EmptiesT&& processEmpties, MasksT&& processMasks, PathsT&& processPaths) {
376 ScopedBuffers _ = this->ensureBuffers(glyphRun);
377
378 int glyphCount = 0;
379 // Four empty glyphs are expected; one for each horizontal subpixel position.
380 SkSTArray<4, const SkGlyph*> emptyGlyphs;
381
382 SkMatrix mapping = deviceMatrix;
383 mapping.preTranslate(origin.x(), origin.y());
384 SkVector rounding = cache->rounding();
385 mapping.postTranslate(rounding.x(), rounding.y());
386 mapping.mapPoints(fPositions, glyphRun.positions().data(), glyphRun.runSize());
387
388 const SkPoint* posCursor = fPositions;
389 for (auto glyphID : glyphRun.glyphsIDs()) {
390 SkPoint mappedPt = *posCursor++;
391
392 if (std::any_of(emptyGlyphs.begin(), emptyGlyphs.end(),
393 [glyphID](const SkGlyph* g) { return g->getGlyphID() == glyphID; })) {
394 continue;
395 }
396
397 if (SkScalarsAreFinite(mappedPt.x(), mappedPt.y())) {
398 const SkGlyph& glyph = cache->getGlyphMetrics(glyphID, mappedPt);
399 if (glyph.isEmpty()) {
400 emptyGlyphs.push_back(&glyph);
401 } else if (SkStrikeCommon::GlyphTooBigForAtlas(glyph)) {
402 if (cache->hasPath(glyph)) {
403 fPaths.push_back({&glyph, mappedPt});
404 } else {
405 // This happens when a bitmap-only font is forced to scale very large. This
406 // doesn't happen in practice.
407 emptyGlyphs.push_back(&glyph);
408 }
409 } else {
410 if (cache->hasImage(glyph)) {
411 fMasks[glyphCount++] = {&glyph, mappedPt};
412 } else {
413 // In practice, this never happens.
414 emptyGlyphs.push_back(&glyph);
415 }
416 }
417 }
418 }
419
420 if (!emptyGlyphs.empty()) {
421 processEmpties(SkSpan<const SkGlyph*>{emptyGlyphs.data(), emptyGlyphs.size()});
422 }
423 if (glyphCount > 0) {
424 mapping.mapPoints(fPositions, glyphCount);
425 processMasks(SkSpan<const GlyphAndPos>{fMasks, SkTo<size_t>(glyphCount)});
426 }
427 if (!fPaths.empty()) {
428 processPaths(SkSpan<const GlyphAndPos>{fPaths});
429 }
430 }
431
432 template <typename PerEmptyT, typename PerSDFT, typename PerPathT>
drawGlyphRunAsSDFWithARGBFallback(SkStrikeInterface * cache,const SkGlyphRun & glyphRun,SkPoint origin,const SkPaint & runPaint,const SkMatrix & viewMatrix,SkScalar textScale,PerEmptyT && perEmpty,PerSDFT && perSDF,PerPathT && perPath,ARGBFallback && argbFallback)433 void SkGlyphRunListPainter::drawGlyphRunAsSDFWithARGBFallback(
434 SkStrikeInterface* cache, const SkGlyphRun& glyphRun,
435 SkPoint origin, const SkPaint& runPaint, const SkMatrix& viewMatrix, SkScalar textScale,
436 PerEmptyT&& perEmpty, PerSDFT&& perSDF, PerPathT&& perPath, ARGBFallback&& argbFallback) {
437 fARGBGlyphsIDs.clear();
438 fARGBPositions.clear();
439 SkScalar maxFallbackDimension{-SK_ScalarInfinity};
440
441 const SkPoint* positionCursor = glyphRun.positions().data();
442 for (auto glyphID : glyphRun.glyphsIDs()) {
443 const SkGlyph& glyph = cache->getGlyphMetrics(glyphID, {0, 0});
444 SkPoint glyphPos = origin + *positionCursor++;
445 if (glyph.isEmpty()) {
446 perEmpty(glyph, glyphPos);
447 } else if (glyph.fMaskFormat == SkMask::kSDF_Format) {
448 if (!SkStrikeCommon::GlyphTooBigForAtlas(glyph)) {
449 // TODO: this check is probably not needed. Remove when proven.
450 if (cache->hasImage(glyph)) {
451 perSDF(glyph, glyphPos);
452 } else {
453 perEmpty(glyph, glyphPos);
454 }
455 } else {
456 if (cache->hasPath(glyph)) {
457 perPath(glyph, glyphPos);
458 } else {
459 perEmpty(glyph, glyphPos);
460 }
461 }
462 } else {
463 SkASSERT(glyph.fMaskFormat == SkMask::kARGB32_Format);
464 SkScalar largestDimension = std::max(glyph.fWidth, glyph.fHeight);
465 maxFallbackDimension = std::max(maxFallbackDimension, largestDimension);
466 fARGBGlyphsIDs.push_back(glyphID);
467 fARGBPositions.push_back(glyphPos);
468 }
469 }
470
471 if (!fARGBGlyphsIDs.empty()) {
472 this->processARGBFallback(
473 maxFallbackDimension, runPaint, glyphRun.font(), viewMatrix, textScale,
474 std::move(argbFallback));
475 }
476 }
477
478 SkGlyphRunListPainter::ScopedBuffers
ensureBuffers(const SkGlyphRunList & glyphRunList)479 SkGlyphRunListPainter::ensureBuffers(const SkGlyphRunList& glyphRunList) {
480 size_t size = 0;
481 for (const SkGlyphRun& run : glyphRunList) {
482 size = std::max(run.runSize(), size);
483 }
484 return ScopedBuffers(this, size);
485 }
486
487 SkGlyphRunListPainter::ScopedBuffers
ensureBuffers(const SkGlyphRun & glyphRun)488 SkGlyphRunListPainter::ensureBuffers(const SkGlyphRun& glyphRun) {
489 return ScopedBuffers(this, glyphRun.runSize());
490 }
491
492 #if SK_SUPPORT_GPU
493 // -- GrTextContext --------------------------------------------------------------------------------
generate_filtered_color(const SkPaint & paint,const GrColorSpaceInfo & colorSpaceInfo)494 SkPMColor4f generate_filtered_color(const SkPaint& paint, const GrColorSpaceInfo& colorSpaceInfo) {
495 SkColor4f filteredColor = paint.getColor4f();
496 if (auto* xform = colorSpaceInfo.colorSpaceXformFromSRGB()) {
497 filteredColor = xform->apply(filteredColor);
498 }
499 if (paint.getColorFilter() != nullptr) {
500 filteredColor = paint.getColorFilter()->filterColor4f(filteredColor,
501 colorSpaceInfo.colorSpace());
502 }
503 return filteredColor.premul();
504 }
505
drawGlyphRunList(GrContext * context,GrTextTarget * target,const GrClip & clip,const SkMatrix & viewMatrix,const SkSurfaceProps & props,const SkGlyphRunList & glyphRunList)506 void GrTextContext::drawGlyphRunList(
507 GrContext* context, GrTextTarget* target, const GrClip& clip,
508 const SkMatrix& viewMatrix, const SkSurfaceProps& props,
509 const SkGlyphRunList& glyphRunList) {
510 SkPoint origin = glyphRunList.origin();
511
512 // Get the first paint to use as the key paint.
513 const SkPaint& listPaint = glyphRunList.paint();
514
515 SkPMColor4f filteredColor = generate_filtered_color(listPaint, target->colorSpaceInfo());
516 GrColor color = generate_filtered_color(listPaint, target->colorSpaceInfo()).toBytes_RGBA();
517
518 // If we have been abandoned, then don't draw
519 if (context->abandoned()) {
520 return;
521 }
522
523 SkMaskFilterBase::BlurRec blurRec;
524 // It might be worth caching these things, but its not clear at this time
525 // TODO for animated mask filters, this will fill up our cache. We need a safeguard here
526 const SkMaskFilter* mf = listPaint.getMaskFilter();
527 bool canCache = glyphRunList.canCache() && !(listPaint.getPathEffect() ||
528 (mf && !as_MFB(mf)->asABlur(&blurRec)));
529 SkScalerContextFlags scalerContextFlags = ComputeScalerContextFlags(target->colorSpaceInfo());
530
531 auto glyphCache = context->contextPriv().getGlyphCache();
532 GrTextBlobCache* textBlobCache = context->contextPriv().getTextBlobCache();
533
534 sk_sp<GrTextBlob> cacheBlob;
535 GrTextBlob::Key key;
536 if (canCache) {
537 bool hasLCD = glyphRunList.anyRunsLCD();
538
539 // We canonicalize all non-lcd draws to use kUnknown_SkPixelGeometry
540 SkPixelGeometry pixelGeometry = hasLCD ? props.pixelGeometry() :
541 kUnknown_SkPixelGeometry;
542
543 // TODO we want to figure out a way to be able to use the canonical color on LCD text,
544 // see the note on ComputeCanonicalColor above. We pick a dummy value for LCD text to
545 // ensure we always match the same key
546 GrColor canonicalColor = hasLCD ? SK_ColorTRANSPARENT :
547 ComputeCanonicalColor(listPaint, hasLCD);
548
549 key.fPixelGeometry = pixelGeometry;
550 key.fUniqueID = glyphRunList.uniqueID();
551 key.fStyle = listPaint.getStyle();
552 key.fHasBlur = SkToBool(mf);
553 key.fCanonicalColor = canonicalColor;
554 key.fScalerContextFlags = scalerContextFlags;
555 cacheBlob = textBlobCache->find(key);
556 }
557
558 if (cacheBlob) {
559 if (cacheBlob->mustRegenerate(listPaint, glyphRunList.anyRunsSubpixelPositioned(),
560 blurRec, viewMatrix, origin.x(),origin.y())) {
561 // We have to remake the blob because changes may invalidate our masks.
562 // TODO we could probably get away reuse most of the time if the pointer is unique,
563 // but we'd have to clear the subrun information
564 textBlobCache->remove(cacheBlob.get());
565 cacheBlob = textBlobCache->makeCachedBlob(glyphRunList, key, blurRec, listPaint, color);
566 cacheBlob->generateFromGlyphRunList(
567 glyphCache, *context->contextPriv().caps()->shaderCaps(), fOptions,
568 listPaint, scalerContextFlags, viewMatrix, props,
569 glyphRunList, target->glyphPainter());
570 } else {
571 textBlobCache->makeMRU(cacheBlob.get());
572
573 if (CACHE_SANITY_CHECK) {
574 sk_sp<GrTextBlob> sanityBlob(textBlobCache->makeBlob(glyphRunList, color));
575 sanityBlob->setupKey(key, blurRec, listPaint);
576 cacheBlob->generateFromGlyphRunList(
577 glyphCache, *context->contextPriv().caps()->shaderCaps(), fOptions,
578 listPaint, scalerContextFlags, viewMatrix, props, glyphRunList,
579 target->glyphPainter());
580 GrTextBlob::AssertEqual(*sanityBlob, *cacheBlob);
581 }
582 }
583 } else {
584 if (canCache) {
585 cacheBlob = textBlobCache->makeCachedBlob(glyphRunList, key, blurRec, listPaint, color);
586 } else {
587 cacheBlob = textBlobCache->makeBlob(glyphRunList, color);
588 }
589 cacheBlob->generateFromGlyphRunList(
590 glyphCache, *context->contextPriv().caps()->shaderCaps(), fOptions, listPaint,
591 scalerContextFlags, viewMatrix, props, glyphRunList,
592 target->glyphPainter());
593 }
594
595 cacheBlob->flush(target, props, fDistanceAdjustTable.get(), listPaint, filteredColor,
596 clip, viewMatrix, origin.x(), origin.y());
597 }
598
appendGlyph(GrGlyph * glyph,SkRect dstRect)599 void GrTextBlob::SubRun::appendGlyph(GrGlyph* glyph, SkRect dstRect) {
600
601 this->joinGlyphBounds(dstRect);
602
603 GrTextBlob* blob = fRun->fBlob;
604
605 bool hasW = this->hasWCoord();
606 // glyphs drawn in perspective must always have a w coord.
607 SkASSERT(hasW || !blob->fInitialViewMatrix.hasPerspective());
608 auto maskFormat = this->maskFormat();
609 size_t vertexStride = GetVertexStride(maskFormat, hasW);
610
611 intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices + fVertexEndIndex);
612
613 // We always write the third position component used by SDFs. If it is unused it gets
614 // overwritten. Similarly, we always write the color and the blob will later overwrite it
615 // with texture coords if it is unused.
616 size_t colorOffset = hasW ? sizeof(SkPoint3) : sizeof(SkPoint);
617 // V0
618 *reinterpret_cast<SkPoint3*>(vertex) = {dstRect.fLeft, dstRect.fTop, 1.f};
619 *reinterpret_cast<GrColor*>(vertex + colorOffset) = fColor;
620 vertex += vertexStride;
621
622 // V1
623 *reinterpret_cast<SkPoint3*>(vertex) = {dstRect.fLeft, dstRect.fBottom, 1.f};
624 *reinterpret_cast<GrColor*>(vertex + colorOffset) = fColor;
625 vertex += vertexStride;
626
627 // V2
628 *reinterpret_cast<SkPoint3*>(vertex) = {dstRect.fRight, dstRect.fTop, 1.f};
629 *reinterpret_cast<GrColor*>(vertex + colorOffset) = fColor;
630 vertex += vertexStride;
631
632 // V3
633 *reinterpret_cast<SkPoint3*>(vertex) = {dstRect.fRight, dstRect.fBottom, 1.f};
634 *reinterpret_cast<GrColor*>(vertex + colorOffset) = fColor;
635
636 fVertexEndIndex += vertexStride * kVerticesPerGlyph;
637 blob->fGlyphs[fGlyphEndIndex++] = glyph;
638 }
639
switchSubRunIfNeededAndAppendGlyph(GrGlyph * glyph,const sk_sp<GrTextStrike> & strike,const SkRect & destRect,bool needsTransform)640 void GrTextBlob::Run::switchSubRunIfNeededAndAppendGlyph(GrGlyph* glyph,
641 const sk_sp<GrTextStrike>& strike,
642 const SkRect& destRect,
643 bool needsTransform) {
644 GrMaskFormat format = glyph->fMaskFormat;
645
646 SubRun* subRun = &fSubRunInfo.back();
647 if (fInitialized && subRun->maskFormat() != format) {
648 subRun = pushBackSubRun(fDescriptor, fColor);
649 subRun->setStrike(strike);
650 } else if (!fInitialized) {
651 subRun->setStrike(strike);
652 }
653
654 fInitialized = true;
655 subRun->setMaskFormat(format);
656 subRun->setNeedsTransform(needsTransform);
657 subRun->appendGlyph(glyph, destRect);
658 }
659
appendDeviceSpaceGlyph(const sk_sp<GrTextStrike> & strike,const SkGlyph & skGlyph,SkPoint origin)660 void GrTextBlob::Run::appendDeviceSpaceGlyph(const sk_sp<GrTextStrike>& strike,
661 const SkGlyph& skGlyph, SkPoint origin) {
662 if (GrGlyph* glyph = strike->getGlyph(skGlyph)) {
663
664 SkRect glyphRect = glyph->destRect(origin);
665
666 if (!glyphRect.isEmpty()) {
667 this->switchSubRunIfNeededAndAppendGlyph(glyph, strike, glyphRect, false);
668 }
669 }
670 }
671
appendSourceSpaceGlyph(const sk_sp<GrTextStrike> & strike,const SkGlyph & skGlyph,SkPoint origin,SkScalar textScale)672 void GrTextBlob::Run::appendSourceSpaceGlyph(const sk_sp<GrTextStrike>& strike,
673 const SkGlyph& skGlyph,
674 SkPoint origin,
675 SkScalar textScale) {
676 if (GrGlyph* glyph = strike->getGlyph(skGlyph)) {
677
678 SkRect glyphRect = glyph->destRect(origin, textScale);
679
680 if (!glyphRect.isEmpty()) {
681 this->switchSubRunIfNeededAndAppendGlyph(glyph, strike, glyphRect, true);
682 }
683 }
684 }
685
generateFromGlyphRunList(GrStrikeCache * glyphCache,const GrShaderCaps & shaderCaps,const GrTextContext::Options & options,const SkPaint & paint,SkScalerContextFlags scalerContextFlags,const SkMatrix & viewMatrix,const SkSurfaceProps & props,const SkGlyphRunList & glyphRunList,SkGlyphRunListPainter * glyphPainter)686 void GrTextBlob::generateFromGlyphRunList(GrStrikeCache* glyphCache,
687 const GrShaderCaps& shaderCaps,
688 const GrTextContext::Options& options,
689 const SkPaint& paint,
690 SkScalerContextFlags scalerContextFlags,
691 const SkMatrix& viewMatrix,
692 const SkSurfaceProps& props,
693 const SkGlyphRunList& glyphRunList,
694 SkGlyphRunListPainter* glyphPainter) {
695 struct ARGBFallbackHelper {
696 void operator()(const SkPaint& fallbackPaint, const SkFont& fallbackFont,
697 SkSpan<const SkGlyphID> glyphIDs,
698 SkSpan<const SkPoint> positions, SkScalar textScale,
699 const SkMatrix& glyphCacheMatrix,
700 SkGlyphRunListPainter::NeedsTransform needsTransform) const {
701 fBlob->setHasBitmap();
702 fRun->setSubRunHasW(glyphCacheMatrix.hasPerspective());
703 auto subRun = fRun->initARGBFallback();
704 SkExclusiveStrikePtr fallbackCache = SkStrikeCache::FindOrCreateStrikeExclusive(
705 fallbackFont, fallbackPaint, fProps, fScalerContextFlags, glyphCacheMatrix);
706 sk_sp<GrTextStrike> strike = fGrStrikeCache->getStrike(fallbackCache.get());
707 fRun->setupFont(fallbackPaint, fallbackFont, fallbackCache->getDescriptor());
708
709 SkASSERT(strike != nullptr);
710 subRun->setStrike(strike);
711 const SkPoint* glyphPos = positions.data();
712 if (needsTransform == SkGlyphRunListPainter::kTransformDone) {
713 for (auto glyphID : glyphIDs) {
714 const SkGlyph& glyph = fallbackCache->getGlyphIDMetrics(glyphID);
715 fRun->appendDeviceSpaceGlyph(strike, glyph, *glyphPos++);
716 }
717 } else {
718 for (auto glyphID : glyphIDs) {
719 const SkGlyph& glyph = fallbackCache->getGlyphIDMetrics(glyphID);
720 fRun->appendSourceSpaceGlyph(strike, glyph, *glyphPos++, textScale);
721 }
722 }
723 }
724
725 GrTextBlob* const fBlob;
726 GrTextBlob::Run* fRun;
727 const SkSurfaceProps& fProps;
728 const SkScalerContextFlags fScalerContextFlags;
729 GrStrikeCache* const fGrStrikeCache;
730 };
731
732 SkPoint origin = glyphRunList.origin();
733 const SkPaint& runPaint = glyphRunList.paint();
734 this->initReusableBlob(SkPaintPriv::ComputeLuminanceColor(runPaint), viewMatrix,
735 origin.x(), origin.y());
736
737 for (const auto& glyphRun : glyphRunList) {
738 const SkFont& runFont = glyphRun.font();
739
740 Run* run = this->pushBackRun();
741
742 run->setRunFontAntiAlias(runFont.hasSomeAntiAliasing());
743
744 if (GrTextContext::CanDrawAsDistanceFields(runPaint, runFont, viewMatrix, props,
745 shaderCaps.supportsDistanceFieldText(), options)) {
746 bool hasWCoord = viewMatrix.hasPerspective()
747 || options.fDistanceFieldVerticesAlwaysHaveW;
748
749 // Setup distance field runPaint and text ratio
750 SkScalar textScale;
751 SkPaint distanceFieldPaint{runPaint};
752 SkFont distanceFieldFont{runFont};
753 SkScalerContextFlags flags;
754 GrTextContext::InitDistanceFieldPaint(runFont.getSize(),
755 viewMatrix,
756 options,
757 this,
758 &distanceFieldPaint,
759 &distanceFieldFont,
760 &textScale,
761 &flags);
762 this->setHasDistanceField();
763 run->setSubRunHasDistanceFields(
764 runFont.getEdging() == SkFont::Edging::kSubpixelAntiAlias,
765 runFont.hasSomeAntiAliasing(),
766 hasWCoord);
767
768 {
769 SkExclusiveStrikePtr cache =SkStrikeCache::FindOrCreateStrikeExclusive(
770 distanceFieldFont, distanceFieldPaint, props, flags, SkMatrix::I());
771 sk_sp<GrTextStrike> currStrike = glyphCache->getStrike(cache.get());
772 run->setupFont(distanceFieldPaint, distanceFieldFont, cache->getDescriptor());
773
774 auto perEmpty = [](const SkGlyph&, SkPoint) {};
775
776 auto perSDF =
777 [run, &currStrike, textScale]
778 (const SkGlyph& glyph, SkPoint position) {
779 run->appendSourceSpaceGlyph(currStrike, glyph, position, textScale);
780 };
781
782 auto perPath =
783 [run, textScale]
784 (const SkGlyph& glyph, SkPoint position) {
785 // TODO: path should always be set. Remove when proven.
786 if (const SkPath* glyphPath = glyph.path()) {
787 run->appendPathGlyph(*glyphPath, position, textScale, false);
788 }
789 };
790
791 ARGBFallbackHelper argbFallback{this, run, props, scalerContextFlags,
792 glyphCache};
793
794 glyphPainter->drawGlyphRunAsSDFWithARGBFallback(
795 cache.get(), glyphRun, origin, runPaint, viewMatrix, textScale,
796 std::move(perEmpty), std::move(perSDF), std::move(perPath),
797 std::move(argbFallback));
798 }
799
800 } else if (SkGlyphRunListPainter::ShouldDrawAsPath(runPaint, runFont, viewMatrix)) {
801 // The glyphs are big, so use paths to draw them.
802
803 // Ensure the blob is set for bitmaptext
804 this->setHasBitmap();
805
806 // setup our std runPaint, in hopes of getting hits in the cache
807 SkPaint pathPaint{runPaint};
808 SkFont pathFont{runFont};
809 SkScalar textScale = pathFont.setupForAsPaths(&pathPaint);
810
811 auto pathCache = SkStrikeCache::FindOrCreateStrikeExclusive(
812 pathFont, pathPaint, props,
813 scalerContextFlags, SkMatrix::I());
814
815 auto perEmpty = [](const SkGlyph&, SkPoint) {};
816
817 // Given a glyph that is not ARGB, draw it.
818 auto perPath = [textScale, run]
819 (const SkGlyph& glyph, SkPoint position) {
820 // TODO: path should always be set. Remove when proven.
821 if (const SkPath* glyphPath = glyph.path()) {
822 run->appendPathGlyph(*glyphPath, position, textScale, false);
823 }
824 };
825
826 ARGBFallbackHelper argbFallback{this, run, props, scalerContextFlags, glyphCache};
827
828 glyphPainter->drawGlyphRunAsPathWithARGBFallback(
829 pathCache.get(), glyphRun, origin, runPaint, viewMatrix, textScale,
830 std::move(perEmpty), std::move(perPath), std::move(argbFallback));
831 } else {
832 // Ensure the blob is set for bitmaptext
833 this->setHasBitmap();
834
835 auto cache = SkStrikeCache::FindOrCreateStrikeExclusive(
836 runFont, runPaint, props, scalerContextFlags, viewMatrix);
837 run->setupFont(runPaint, runFont, cache->getDescriptor());
838
839 auto processEmpties = [](SkSpan<const SkGlyph*>glyphs) {};
840
841 auto processMasks =
842 [run, cache{cache.get()}, glyphCache]
843 (SkSpan<const SkGlyphRunListPainter::GlyphAndPos> masks) {
844 sk_sp<GrTextStrike> currStrike = glyphCache->getStrike(cache);
845 for (const auto& mask : masks) {
846 SkPoint pt{SkScalarFloorToScalar(mask.position.fX),
847 SkScalarFloorToScalar(mask.position.fY)};
848 run->appendDeviceSpaceGlyph(currStrike, *mask.glyph, pt);
849 }
850 };
851
852 auto processPaths =
853 [run]
854 (SkSpan<const SkGlyphRunListPainter::GlyphAndPos> paths) {
855 for (const auto& path : paths) {
856 SkPoint pt{SkScalarFloorToScalar(path.position.fX),
857 SkScalarFloorToScalar(path.position.fY)};
858 // TODO: path should always be set. Remove when proven.
859 if (const SkPath* glyphPath = path.glyph->path()) {
860 run->appendPathGlyph(*glyphPath, pt, SK_Scalar1, true);
861 }
862 }
863 };
864
865 glyphPainter->drawGlyphRunAsBMPWithPathFallback(
866 cache.get(), glyphRun, origin, viewMatrix,
867 std::move(processEmpties), std::move(processMasks), std::move(processPaths));
868 }
869 }
870 }
871
872 #if GR_TEST_UTILS
873
874 #include "GrRenderTargetContext.h"
875
createOp_TestingOnly(GrContext * context,GrTextContext * textContext,GrRenderTargetContext * rtc,const SkPaint & skPaint,const SkFont & font,const SkMatrix & viewMatrix,const char * text,int x,int y)876 std::unique_ptr<GrDrawOp> GrTextContext::createOp_TestingOnly(GrContext* context,
877 GrTextContext* textContext,
878 GrRenderTargetContext* rtc,
879 const SkPaint& skPaint,
880 const SkFont& font,
881 const SkMatrix& viewMatrix,
882 const char* text,
883 int x,
884 int y) {
885 auto glyphCache = context->contextPriv().getGlyphCache();
886
887 static SkSurfaceProps surfaceProps(SkSurfaceProps::kLegacyFontHost_InitType);
888
889 size_t textLen = (int)strlen(text);
890
891 SkPMColor4f filteredColor = generate_filtered_color(skPaint, rtc->colorSpaceInfo());
892 GrColor color = filteredColor.toBytes_RGBA();
893
894 auto origin = SkPoint::Make(x, y);
895 SkGlyphRunBuilder builder;
896 builder.drawTextUTF8(skPaint, font, text, textLen, origin);
897
898 auto glyphRunList = builder.useGlyphRunList();
899 sk_sp<GrTextBlob> blob;
900 if (!glyphRunList.empty()) {
901 blob = context->contextPriv().getTextBlobCache()->makeBlob(glyphRunList, color);
902 // Use the text and textLen below, because we don't want to mess with the paint.
903 SkScalerContextFlags scalerContextFlags =
904 ComputeScalerContextFlags(rtc->colorSpaceInfo());
905 blob->generateFromGlyphRunList(
906 glyphCache, *context->contextPriv().caps()->shaderCaps(), textContext->fOptions,
907 skPaint, scalerContextFlags, viewMatrix, surfaceProps,
908 glyphRunList, rtc->textTarget()->glyphPainter());
909 }
910
911 return blob->test_makeOp(textLen, 0, 0, viewMatrix, x, y, skPaint, filteredColor, surfaceProps,
912 textContext->dfAdjustTable(), rtc->textTarget());
913 }
914
915 #endif // GR_TEST_UTILS
916 #endif // SK_SUPPORT_GPU
917
918 // -- SkTextBlobCacheDiffCanvas::TrackLayerDevice --------------------------------------------------
919
processGlyphRun(const SkPoint & origin,const SkGlyphRun & glyphRun,const SkPaint & runPaint)920 void SkTextBlobCacheDiffCanvas::TrackLayerDevice::processGlyphRun(
921 const SkPoint& origin, const SkGlyphRun& glyphRun, const SkPaint& runPaint) {
922 TRACE_EVENT0("skia", "SkTextBlobCacheDiffCanvas::processGlyphRun");
923
924 const SkMatrix& runMatrix = this->ctm();
925
926 // If the matrix has perspective, we fall back to using distance field text or paths.
927 #if SK_SUPPORT_GPU
928 if (this->maybeProcessGlyphRunForDFT(glyphRun, runMatrix, origin, runPaint)) {
929 return;
930 } else
931 #endif
932 if (SkGlyphRunListPainter::ShouldDrawAsPath(runPaint, glyphRun.font(), runMatrix)) {
933 this->processGlyphRunForPaths(glyphRun, runMatrix, origin, runPaint);
934 } else {
935 this->processGlyphRunForMask(glyphRun, runMatrix, origin, runPaint);
936 }
937 }
938
processGlyphRunForMask(const SkGlyphRun & glyphRun,const SkMatrix & runMatrix,SkPoint origin,const SkPaint & runPaint)939 void SkTextBlobCacheDiffCanvas::TrackLayerDevice::processGlyphRunForMask(
940 const SkGlyphRun& glyphRun, const SkMatrix& runMatrix,
941 SkPoint origin, const SkPaint& runPaint) {
942 TRACE_EVENT0("skia", "SkTextBlobCacheDiffCanvas::processGlyphRunForMask");
943
944 SkScalerContextEffects effects;
945 auto* glyphCacheState = fStrikeServer->getOrCreateCache(
946 runPaint, glyphRun.font(), this->surfaceProps(), runMatrix,
947 SkScalerContextFlags::kFakeGammaAndBoostContrast, &effects);
948 SkASSERT(glyphCacheState);
949
950 auto processEmpties = [glyphCacheState] (SkSpan<const SkGlyph*>glyphs) {
951 for (const SkGlyph* glyph : glyphs) {
952 glyphCacheState->addGlyph(glyph->getPackedID(), false);
953 }
954 };
955
956 auto processMasks = [glyphCacheState]
957 (SkSpan<const SkGlyphRunListPainter::GlyphAndPos> masks) {
958 for (const auto& mask : masks) {
959 glyphCacheState->addGlyph(mask.glyph->getPackedID(), false);
960 }
961 };
962
963 // Glyphs which are too large for the atlas still request images when computing the bounds
964 // for the glyph, which is why its necessary to send both. See related code in
965 // get_packed_glyph_bounds in GrStrikeCache.cpp and crbug.com/510931.
966 auto processPaths = [glyphCacheState]
967 (SkSpan<const SkGlyphRunListPainter::GlyphAndPos> paths) {
968 for (const auto& path : paths) {
969 SkPackedGlyphID glyphID = path.glyph->getPackedID();
970 glyphCacheState->addGlyph(glyphID, true);
971 glyphCacheState->addGlyph(glyphID, false);
972 }
973 };
974
975 fPainter.drawGlyphRunAsBMPWithPathFallback(
976 glyphCacheState, glyphRun, origin, runMatrix,
977 std::move(processEmpties), std::move(processMasks), std::move(processPaths));
978 }
979
980 struct ARGBHelper {
operator ()ARGBHelper981 void operator()(const SkPaint& fallbackPaint,
982 const SkFont& fallbackFont,
983 SkSpan<const SkGlyphID> glyphIDs,
984 SkSpan<const SkPoint> positions,
985 SkScalar textScale,
986 const SkMatrix& glyphCacheMatrix,
987 SkGlyphRunListPainter::NeedsTransform needsTransform) {
988 TRACE_EVENT0("skia", "argbFallback");
989
990 SkScalerContextEffects effects;
991 auto* fallbackCache =
992 fStrikeServer->getOrCreateCache(
993 fallbackPaint, fallbackFont, fSurfaceProps, fFallbackMatrix,
994 SkScalerContextFlags::kFakeGammaAndBoostContrast, &effects);
995
996 for (auto glyphID : glyphIDs) {
997 fallbackCache->addGlyph(SkPackedGlyphID(glyphID, 0, 0), false);
998 }
999 }
1000
1001 const SkMatrix& fFallbackMatrix;
1002 const SkSurfaceProps& fSurfaceProps;
1003 SkStrikeServer* const fStrikeServer;
1004 };
1005
SetupForPath(SkPaint * paint,SkFont * font)1006 SkScalar SkTextBlobCacheDiffCanvas::SetupForPath(SkPaint* paint, SkFont* font) {
1007 return font->setupForAsPaths(paint);
1008 }
1009
processGlyphRunForPaths(const SkGlyphRun & glyphRun,const SkMatrix & runMatrix,SkPoint origin,const SkPaint & runPaint)1010 void SkTextBlobCacheDiffCanvas::TrackLayerDevice::processGlyphRunForPaths(
1011 const SkGlyphRun& glyphRun, const SkMatrix& runMatrix,
1012 SkPoint origin, const SkPaint& runPaint) {
1013 TRACE_EVENT0("skia", "SkTextBlobCacheDiffCanvas::processGlyphRunForPaths");
1014
1015 SkPaint pathPaint{runPaint};
1016 SkFont pathFont{glyphRun.font()};
1017 SkScalar textScale = SetupForPath(&pathPaint, &pathFont);
1018
1019 SkScalerContextEffects effects;
1020 auto* glyphCacheState = fStrikeServer->getOrCreateCache(
1021 pathPaint, pathFont, this->surfaceProps(), SkMatrix::I(),
1022 SkScalerContextFlags::kFakeGammaAndBoostContrast, &effects);
1023
1024 auto perEmpty = [glyphCacheState] (const SkGlyph& glyph, SkPoint mappedPt) {
1025 glyphCacheState->addGlyph(glyph.getPackedID(), false);
1026 };
1027
1028 auto perPath = [glyphCacheState](const SkGlyph& glyph, SkPoint position) {
1029 const bool asPath = true;
1030 glyphCacheState->addGlyph(glyph.getGlyphID(), asPath);
1031 };
1032
1033 ARGBHelper argbFallback{runMatrix, surfaceProps(), fStrikeServer};
1034
1035 fPainter.drawGlyphRunAsPathWithARGBFallback(
1036 glyphCacheState, glyphRun, origin, runPaint, runMatrix, textScale,
1037 std::move(perEmpty), std::move(perPath), std::move(argbFallback));
1038 }
1039
1040 #if SK_SUPPORT_GPU
maybeProcessGlyphRunForDFT(const SkGlyphRun & glyphRun,const SkMatrix & runMatrix,SkPoint origin,const SkPaint & runPaint)1041 bool SkTextBlobCacheDiffCanvas::TrackLayerDevice::maybeProcessGlyphRunForDFT(
1042 const SkGlyphRun& glyphRun, const SkMatrix& runMatrix,
1043 SkPoint origin, const SkPaint& runPaint) {
1044 TRACE_EVENT0("skia", "SkTextBlobCacheDiffCanvas::maybeProcessGlyphRunForDFT");
1045
1046 const SkFont& runFont = glyphRun.font();
1047
1048 GrTextContext::Options options;
1049 options.fMinDistanceFieldFontSize = fSettings.fMinDistanceFieldFontSize;
1050 options.fMaxDistanceFieldFontSize = fSettings.fMaxDistanceFieldFontSize;
1051 GrTextContext::SanitizeOptions(&options);
1052 if (!GrTextContext::CanDrawAsDistanceFields(runPaint, runFont,
1053 runMatrix, this->surfaceProps(),
1054 fSettings.fContextSupportsDistanceFieldText,
1055 options)) {
1056 return false;
1057 }
1058
1059 SkScalar textRatio;
1060 SkPaint dfPaint{runPaint};
1061 SkFont dfFont{runFont};
1062 SkScalerContextFlags flags;
1063 GrTextContext::InitDistanceFieldPaint(runFont.getSize(),
1064 runMatrix,
1065 options,
1066 nullptr,
1067 &dfPaint,
1068 &dfFont,
1069 &textRatio,
1070 &flags);
1071 SkScalerContextEffects effects;
1072 auto* sdfCache = fStrikeServer->getOrCreateCache(dfPaint, dfFont, this->surfaceProps(),
1073 SkMatrix::I(), flags, &effects);
1074
1075 ARGBHelper argbFallback{runMatrix, surfaceProps(), fStrikeServer};
1076
1077 auto perEmpty = [sdfCache] (const SkGlyph& glyph, SkPoint mappedPt) {
1078 sdfCache->addGlyph(glyph.getPackedID(), false);
1079 };
1080
1081 auto perSDF = [sdfCache] (const SkGlyph& glyph, SkPoint position) {
1082 const bool asPath = false;
1083 sdfCache->addGlyph(glyph.getGlyphID(), asPath);
1084 };
1085
1086 auto perPath = [sdfCache] (const SkGlyph& glyph, SkPoint position) {
1087 const bool asPath = true;
1088 sdfCache->addGlyph(glyph.getGlyphID(), asPath);
1089 };
1090
1091 fPainter.drawGlyphRunAsSDFWithARGBFallback(
1092 sdfCache, glyphRun, origin, runPaint, runMatrix, textRatio,
1093 std::move(perEmpty), std::move(perSDF), std::move(perPath),
1094 std::move(argbFallback));
1095
1096 return true;
1097 }
1098 #endif
1099
ScopedBuffers(SkGlyphRunListPainter * painter,int size)1100 SkGlyphRunListPainter::ScopedBuffers::ScopedBuffers(SkGlyphRunListPainter* painter, int size)
1101 : fPainter{painter} {
1102 SkASSERT(size >= 0);
1103 if (fPainter->fMaxRunSize < size) {
1104 fPainter->fMaxRunSize = size;
1105
1106 fPainter->fPositions.reset(size);
1107 fPainter->fMasks.reset(size);
1108 }
1109 }
1110
~ScopedBuffers()1111 SkGlyphRunListPainter::ScopedBuffers::~ScopedBuffers() {
1112 fPainter->fPaths.clear();
1113 fPainter->fARGBGlyphsIDs.clear();
1114 fPainter->fARGBPositions.clear();
1115
1116 if (fPainter->fMaxRunSize > 200) {
1117 fPainter->fMaxRunSize = 0;
1118 fPainter->fPositions.reset();
1119 fPainter->fMasks.reset();
1120 fPainter->fPaths.shrink_to_fit();
1121 fPainter->fARGBGlyphsIDs.shrink_to_fit();
1122 fPainter->fARGBPositions.shrink_to_fit();
1123 }
1124 }
1125