• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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