• 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 "src/core/SkGlyphRunPainter.h"
9 
10 #if SK_SUPPORT_GPU
11 #include "include/private/GrRecordingContext.h"
12 #include "src/gpu/GrCaps.h"
13 #include "src/gpu/GrColorSpaceInfo.h"
14 #include "src/gpu/GrContextPriv.h"
15 #include "src/gpu/GrRecordingContextPriv.h"
16 #include "src/gpu/GrRenderTargetContext.h"
17 #include "src/gpu/SkGr.h"
18 #include "src/gpu/text/GrTextBlobCache.h"
19 #include "src/gpu/text/GrTextContext.h"
20 #endif
21 
22 #include "include/core/SkColorFilter.h"
23 #include "include/core/SkMaskFilter.h"
24 #include "include/core/SkPathEffect.h"
25 #include "include/private/SkTDArray.h"
26 #include "src/core/SkDevice.h"
27 #include "src/core/SkDistanceFieldGen.h"
28 #include "src/core/SkDraw.h"
29 #include "src/core/SkFontPriv.h"
30 #include "src/core/SkPaintPriv.h"
31 #include "src/core/SkRasterClip.h"
32 #include "src/core/SkStrike.h"
33 #include "src/core/SkStrikeCache.h"
34 #include "src/core/SkStrikeInterface.h"
35 #include "src/core/SkStrikeSpec.h"
36 #include "src/core/SkTraceEvent.h"
37 
38 #include <limits.h>
39 
40 // -- SkGlyphCacheCommon ---------------------------------------------------------------------------
PixelRounding(bool isSubpixel,SkAxisAlignment axisAlignment)41 SkVector SkStrikeCommon::PixelRounding(bool isSubpixel, SkAxisAlignment axisAlignment) {
42     if (!isSubpixel) {
43         return {SK_ScalarHalf, SK_ScalarHalf};
44     } else {
45         static constexpr SkScalar kSubpixelRounding = SkFixedToScalar(SkGlyph::kSubpixelRound);
46         switch (axisAlignment) {
47             case kX_SkAxisAlignment:
48                 return {kSubpixelRounding, SK_ScalarHalf};
49             case kY_SkAxisAlignment:
50                 return {SK_ScalarHalf, kSubpixelRounding};
51             case kNone_SkAxisAlignment:
52                 return {kSubpixelRounding, kSubpixelRounding};
53         }
54     }
55 
56     // Some compilers need this.
57     return {0, 0};
58 }
59 
60 // -- SkGlyphRunListPainter ------------------------------------------------------------------------
SkGlyphRunListPainter(const SkSurfaceProps & props,SkColorType colorType,SkScalerContextFlags flags,SkStrikeCacheInterface * strikeCache)61 SkGlyphRunListPainter::SkGlyphRunListPainter(const SkSurfaceProps& props,
62                                              SkColorType colorType,
63                                              SkScalerContextFlags flags,
64                                              SkStrikeCacheInterface* strikeCache)
65         : fDeviceProps{props}
66         ,  fBitmapFallbackProps{SkSurfaceProps{props.flags(), kUnknown_SkPixelGeometry}}
67         ,  fColorType{colorType}, fScalerContextFlags{flags}
68         ,  fStrikeCache{strikeCache} {}
69 
70 // TODO: unify with code in GrTextContext.cpp
compute_scaler_context_flags(const SkColorSpace * cs)71 static SkScalerContextFlags compute_scaler_context_flags(const SkColorSpace* cs) {
72     // If we're doing linear blending, then we can disable the gamma hacks.
73     // Otherwise, leave them on. In either case, we still want the contrast boost:
74     // TODO: Can we be even smarter about mask gamma based on the dest transfer function?
75     if (cs && cs->gammaIsLinear()) {
76         return SkScalerContextFlags::kBoostContrast;
77     } else {
78         return SkScalerContextFlags::kFakeGammaAndBoostContrast;
79     }
80 }
81 
SkGlyphRunListPainter(const SkSurfaceProps & props,SkColorType colorType,SkColorSpace * cs,SkStrikeCacheInterface * strikeCache)82 SkGlyphRunListPainter::SkGlyphRunListPainter(const SkSurfaceProps& props,
83                                              SkColorType colorType,
84                                              SkColorSpace* cs,
85                                              SkStrikeCacheInterface* strikeCache)
86         : SkGlyphRunListPainter(props, colorType, compute_scaler_context_flags(cs), strikeCache) {}
87 
88 #if SK_SUPPORT_GPU
SkGlyphRunListPainter(const SkSurfaceProps & props,const GrColorSpaceInfo & csi)89 SkGlyphRunListPainter::SkGlyphRunListPainter(const SkSurfaceProps& props,
90                                              const GrColorSpaceInfo& csi)
91         : SkGlyphRunListPainter(props,
92                                 kUnknown_SkColorType,
93                                 compute_scaler_context_flags(csi.colorSpace()),
94                                 SkStrikeCache::GlobalStrikeCache()) {}
95 
SkGlyphRunListPainter(const GrRenderTargetContext & rtc)96 SkGlyphRunListPainter::SkGlyphRunListPainter(const GrRenderTargetContext& rtc)
97         : SkGlyphRunListPainter{rtc.surfaceProps(), rtc.colorSpaceInfo()} {}
98 
99 #endif
100 
check_glyph_position(SkPoint position)101 static bool check_glyph_position(SkPoint position) {
102     // Prevent glyphs from being drawn outside of or straddling the edge of device space.
103     // Comparisons written a little weirdly so that NaN coordinates are treated safely.
104     auto gt = [](float a, int b) { return !(a <= (float)b); };
105     auto lt = [](float a, int b) { return !(a >= (float)b); };
106     return !(gt(position.fX, INT_MAX - (INT16_MAX + SkTo<int>(UINT16_MAX))) ||
107              lt(position.fX, INT_MIN - (INT16_MIN + 0 /*UINT16_MIN*/)) ||
108              gt(position.fY, INT_MAX - (INT16_MAX + SkTo<int>(UINT16_MAX))) ||
109              lt(position.fY, INT_MIN - (INT16_MIN + 0 /*UINT16_MIN*/)));
110 }
111 
DeviceSpacePackedGlyphIDs(SkStrikeInterface * strike,const SkMatrix & viewMatrix,const SkPoint & origin,int n,const SkGlyphID * glyphIDs,const SkPoint * positions,SkPoint * mappedPositions,SkPackedGlyphID * results)112 SkSpan<const SkPackedGlyphID> SkGlyphRunListPainter::DeviceSpacePackedGlyphIDs(
113         SkStrikeInterface* strike,
114         const SkMatrix& viewMatrix,
115         const SkPoint& origin,
116         int n,
117         const SkGlyphID* glyphIDs,
118         const SkPoint* positions,
119         SkPoint* mappedPositions,
120         SkPackedGlyphID* results) {
121     // Add rounding and origin.
122     SkMatrix matrix = viewMatrix;
123     matrix.preTranslate(origin.x(), origin.y());
124     SkPoint rounding = strike->rounding();
125     matrix.postTranslate(rounding.x(), rounding.y());
126     matrix.mapPoints(mappedPositions, positions, n);
127 
128     SkIPoint mask = strike->subpixelMask();
129 
130     for (int i = 0; i < n; i++) {
131         SkFixed subX = SkScalarToFixed(mappedPositions[i].x()) & mask.x(),
132                 subY = SkScalarToFixed(mappedPositions[i].y()) & mask.y();
133         results[i] = SkPackedGlyphID{glyphIDs[i], subX, subY};
134     }
135 
136     return SkSpan<const SkPackedGlyphID>{results, SkTo<size_t>(n)};
137 }
138 
SourceSpacePackedGlyphIDs(const SkPoint & origin,int n,const SkGlyphID * glyphIDs,const SkPoint * positions,SkPoint * mappedPositions,SkPackedGlyphID * results)139 SkSpan<const SkPackedGlyphID> SkGlyphRunListPainter::SourceSpacePackedGlyphIDs(
140         const SkPoint& origin,
141         int n,
142         const SkGlyphID* glyphIDs,
143         const SkPoint* positions,
144         SkPoint* mappedPositions,
145         SkPackedGlyphID* results) {
146 
147     SkMatrix::MakeTrans(origin.x(), origin.y()).mapPoints(
148             mappedPositions, positions, n);
149 
150     SkPackedGlyphID* cursor = results;
151     for (int i = 0; i < n; i++) {
152         *cursor++ = SkPackedGlyphID{glyphIDs[i]};
153     }
154 
155     return SkSpan<const SkPackedGlyphID>{results, SkTo<size_t>(n)};
156 }
157 
drawForBitmapDevice(const SkGlyphRunList & glyphRunList,const SkMatrix & deviceMatrix,const BitmapDevicePainter * bitmapDevice)158 void SkGlyphRunListPainter::drawForBitmapDevice(
159         const SkGlyphRunList& glyphRunList, const SkMatrix& deviceMatrix,
160         const BitmapDevicePainter* bitmapDevice) {
161     ScopedBuffers _ = this->ensureBuffers(glyphRunList);
162 
163     const SkPaint& runPaint = glyphRunList.paint();
164     // The bitmap blitters can only draw lcd text to a N32 bitmap in srcOver. Otherwise,
165     // convert the lcd text into A8 text. The props communicates this to the scaler.
166     auto& props = (kN32_SkColorType == fColorType && runPaint.isSrcOver())
167                   ? fDeviceProps
168                   : fBitmapFallbackProps;
169 
170     SkPoint origin = glyphRunList.origin();
171     for (auto& glyphRun : glyphRunList) {
172         const SkFont& runFont = glyphRun.font();
173         auto runSize = glyphRun.runSize();
174 
175         if (SkStrikeSpec::ShouldDrawAsPath(runPaint, runFont, deviceMatrix)) {
176 
177             SkStrikeSpec strikeSpec = SkStrikeSpec::MakePath(
178                     runFont, runPaint, props, fScalerContextFlags);
179 
180             auto strike = strikeSpec.findOrCreateExclusiveStrike();
181 
182             auto packedGlyphIDs = SourceSpacePackedGlyphIDs(
183                     origin,
184                     runSize,
185                     glyphRun.glyphsIDs().data(),
186                     glyphRun.positions().data(),
187                     fPositions,
188                     fPackedGlyphIDs);
189 
190             auto glyphPosSpan = strike->prepareForDrawingRemoveEmpty(
191                     packedGlyphIDs.data(),
192                     fPositions,
193                     glyphRun.runSize(),
194                     0,
195                     SkStrikeInterface::kBoundsOnly,
196                     fGlyphPos);
197 
198             SkTDArray<SkPathPos> pathsAndPositions;
199             pathsAndPositions.setReserve(glyphPosSpan.size());
200             for (const SkGlyphPos& glyphPos : glyphPosSpan) {
201                 const SkGlyph& glyph = *glyphPos.glyph;
202                 SkPoint position = glyphPos.position;
203                 if (check_glyph_position(position)
204                     && !glyph.isEmpty()
205                     && !glyph.isColor()
206                     && glyph.path() != nullptr)
207                 {
208                     // Only draw a path if it exists, and this is not a color glyph.
209                     pathsAndPositions.push_back(SkPathPos{glyph.path(), position});
210                 } else {
211                     // TODO: this is here to have chrome layout tests pass. Remove this when
212                     //  fallback for CPU works.
213                     const SkPath* path = strike->preparePath((SkGlyph*) &glyph);
214                     if (check_glyph_position(position) && !glyph.isEmpty() && path != nullptr) {
215                         pathsAndPositions.push_back(SkPathPos{path, position});
216                     }
217                 }
218             }
219 
220             // The paint we draw paths with must have the same anti-aliasing state as the runFont
221             // allowing the paths to have the same edging as the glyph masks.
222             SkPaint pathPaint = runPaint;
223             pathPaint.setAntiAlias(runFont.hasSomeAntiAliasing());
224 
225             bitmapDevice->paintPaths(
226                     SkSpan<const SkPathPos>{pathsAndPositions.begin(), pathsAndPositions.size()},
227                     strikeSpec.strikeToSourceRatio(), pathPaint);
228         } else {
229             SkStrikeSpec strikeSpec = SkStrikeSpec::MakeMask(
230                     runFont, runPaint, props, fScalerContextFlags, deviceMatrix);
231 
232             auto strike = strikeSpec.findOrCreateExclusiveStrike();
233 
234             auto packedGlyphIDs = DeviceSpacePackedGlyphIDs(
235                     strike.get(),
236                     deviceMatrix,
237                     origin,
238                     runSize,
239                     glyphRun.glyphsIDs().data(),
240                     glyphRun.positions().data(),
241                     fPositions,
242                     fPackedGlyphIDs);
243 
244             SkSpan<const SkGlyphPos> glyphPosSpan = strike->prepareForDrawingRemoveEmpty(
245                     packedGlyphIDs.data(),
246                     fPositions,
247                     glyphRun.runSize(),
248                     std::numeric_limits<int>::max(),
249                     SkStrikeInterface::kImageIfNeeded,
250                     fGlyphPos);
251 
252             SkTDArray<SkMask> masks;
253             masks.setReserve(glyphPosSpan.size());
254 
255             for (const SkGlyphPos& glyphPos : glyphPosSpan) {
256                 const SkGlyph& glyph = *glyphPos.glyph;
257                 SkPoint position = glyphPos.position;
258                 // The glyph could have dimensions (!isEmpty()), but still may have no bits if
259                 // the width is too wide. So check that there really is an image.
260                 if (check_glyph_position(position) && glyph.image() != nullptr) {
261                     masks.push_back(glyph.mask(position));
262                 }
263             }
264 
265             bitmapDevice->paintMasks(SkSpan<const SkMask>{masks.begin(), masks.size()}, runPaint);
266         }
267     }
268 }
269 
270 // Getting glyphs to the screen in a fallback situation can be complex. Here is the set of
271 // transformations that have to happen. Normally, they would all be accommodated by the font
272 // scaler, but the atlas has an upper limit to the glyphs it can handle. So the GPU is used to
273 // make up the difference from the smaller atlas size to the larger size needed by the final
274 // transform. Here are the transformations that are applied.
275 //
276 // final transform = [view matrix] * [text scale] * [text size]
277 //
278 // There are three cases:
279 // * Go Fast - view matrix is scale and translate, and all the glyphs are small enough
280 //   Just scale the positions, and have the glyph cache handle the view matrix transformation.
281 //   The text scale is 1.
282 // * It's complicated - view matrix is not scale and translate, and the glyphs are small enough
283 //   The glyph cache does not handle the view matrix, but stores the glyphs at the text size
284 //   specified by the run paint. The GPU handles the rotation, etc. specified by the view matrix.
285 //   The text scale is 1.
286 // * Too big - The glyphs are too big to fit in the atlas
287 //   Reduce the text size so the glyphs will fit in the atlas, but don't apply any
288 //   transformations from the view matrix. Calculate a text scale based on that reduction. This
289 //   scale factor is used to increase the size of the destination rectangles. The destination
290 //   rectangles are then scaled, rotated, etc. by the GPU using the view matrix.
processARGBFallback(SkScalar maxSourceGlyphDimension,const SkPaint & runPaint,const SkFont & runFont,const SkMatrix & viewMatrix,SkGlyphRunPainterInterface * process)291 void SkGlyphRunListPainter::processARGBFallback(SkScalar maxSourceGlyphDimension,
292                                                 const SkPaint& runPaint,
293                                                 const SkFont& runFont,
294                                                 const SkMatrix& viewMatrix,
295                                                 SkGlyphRunPainterInterface* process) {
296     SkASSERT(!fARGBGlyphsIDs.empty());
297 
298     // if maxSourceGlyphDimension then no pixels will change.
299     if (maxSourceGlyphDimension == 0) { return; }
300 
301     SkScalar maxScale = viewMatrix.getMaxScale();
302 
303     // This is a linear estimate of the longest dimension among all the glyph widths and heights.
304     SkScalar conservativeMaxGlyphDimension = maxSourceGlyphDimension * maxScale;
305 
306     // If the situation that the matrix is simple, and all the glyphs are small enough. Go fast!
307     // N.B. If the matrix has scale, that will be reflected in the strike through the viewMatrix
308     // in the useFastPath case.
309     bool useDeviceCache =
310             viewMatrix.isScaleTranslate()
311             && conservativeMaxGlyphDimension <= SkStrikeCommon::kSkSideTooBigForAtlas;
312 
313     // A scaled and translated transform is the common case, and is handled directly in fallback.
314     // Even if the transform is scale and translate, fallback must be careful to use glyphs that
315     // fit in the atlas. If a glyph will not fit in the atlas, then the general transform case is
316     // used to render the glyphs.
317     if (useDeviceCache) {
318         // Translate the positions to device space.
319         // TODO: this code is dubious
320         viewMatrix.mapPoints(fARGBPositions.data(), fARGBPositions.size());
321         for (SkPoint& point : fARGBPositions) {
322             point.fX =  SkScalarFloorToScalar(point.fX);
323             point.fY =  SkScalarFloorToScalar(point.fY);
324         }
325 
326         SkStrikeSpec strikeSpec = SkStrikeSpec::MakeMask(
327                 runFont, runPaint, fDeviceProps, fScalerContextFlags, viewMatrix);
328 
329         SkScopedStrike strike = strikeSpec.findOrCreateScopedStrike(fStrikeCache);
330 
331         SkPackedGlyphID* cursor = fPackedGlyphIDs;
332         for (auto glyphID : fARGBGlyphsIDs) {
333             *cursor++ = SkPackedGlyphID{glyphID};
334         }
335 
336         SkSpan<const SkGlyphPos> glyphPosSpan = strike->prepareForDrawingRemoveEmpty(
337                 fPackedGlyphIDs,
338                 fARGBPositions.data(),
339                 fARGBGlyphsIDs.size(),
340                 SkStrikeCommon::kSkSideTooBigForAtlas,
341                 SkStrikeInterface::kBoundsOnly,
342                 fGlyphPos);
343 
344         if (process) {
345             process->processDeviceFallback(glyphPosSpan, strikeSpec);
346         }
347 
348     } else {
349         // If the matrix is complicated or if scaling is used to fit the glyphs in the cache,
350         // then this case is used.
351 
352         SkStrikeSpec strikeSpec = SkStrikeSpec::MakeSourceFallback(
353                 runFont, runPaint, fDeviceProps, fScalerContextFlags, maxSourceGlyphDimension);
354 
355         SkScopedStrike strike = strikeSpec.findOrCreateScopedStrike(fStrikeCache);
356 
357         SkPackedGlyphID* cursor = fPackedGlyphIDs;
358         for (auto glyphID : fARGBGlyphsIDs) {
359             *cursor++ = SkPackedGlyphID{glyphID};
360         }
361 
362         auto glyphPosSpan = strike->prepareForDrawingRemoveEmpty(
363                 fPackedGlyphIDs,
364                 fARGBPositions.data(),
365                 fARGBGlyphsIDs.size(),
366                 SkStrikeCommon::kSkSideTooBigForAtlas,
367                 SkStrikeInterface::kBoundsOnly,
368                 fGlyphPos);
369 
370         if (process) {
371             process->processSourceFallback(
372                     glyphPosSpan,
373                     strikeSpec,
374                     viewMatrix.hasPerspective());
375         }
376     }
377 }
378 
379 #if SK_SUPPORT_GPU
processGlyphRunList(const SkGlyphRunList & glyphRunList,const SkMatrix & viewMatrix,const SkSurfaceProps & props,bool contextSupportsDistanceFieldText,const GrTextContext::Options & options,SkGlyphRunPainterInterface * process)380 void SkGlyphRunListPainter::processGlyphRunList(const SkGlyphRunList& glyphRunList,
381                                                 const SkMatrix& viewMatrix,
382                                                 const SkSurfaceProps& props,
383                                                 bool contextSupportsDistanceFieldText,
384                                                 const GrTextContext::Options& options,
385                                                 SkGlyphRunPainterInterface* process) {
386 
387     SkPoint origin = glyphRunList.origin();
388     const SkPaint& runPaint = glyphRunList.paint();
389 
390     for (const auto& glyphRun : glyphRunList) {
391         SkScalar maxFallbackDimension{-SK_ScalarInfinity};
392         ScopedBuffers _ = this->ensureBuffers(glyphRun);
393 
394         auto addFallback = [this, &maxFallbackDimension]
395                 (const SkGlyph& glyph, SkPoint sourcePosition) {
396             maxFallbackDimension = std::max(maxFallbackDimension,
397                                             SkIntToScalar(glyph.maxDimension()));
398             fARGBGlyphsIDs.push_back(glyph.getGlyphID());
399             fARGBPositions.push_back(sourcePosition);
400         };
401 
402         const SkFont& runFont = glyphRun.font();
403 
404         bool useSDFT = GrTextContext::CanDrawAsDistanceFields(
405                 runPaint, runFont, viewMatrix, props, contextSupportsDistanceFieldText, options);
406         if (process) {
407             process->startRun(glyphRun, useSDFT);
408         }
409 
410         if (useSDFT) {
411             SkScalar minScale, maxScale;
412             SkStrikeSpec strikeSpec;
413             std::tie(strikeSpec, minScale, maxScale) =
414                     SkStrikeSpec::MakeSDFT(
415                             runFont, runPaint,fDeviceProps, viewMatrix, options);
416 
417             SkScopedStrike strike = strikeSpec.findOrCreateScopedStrike(fStrikeCache);
418 
419             auto packedGlyphIDs = SourceSpacePackedGlyphIDs(
420                     origin,
421                     glyphRun.runSize(),
422                     glyphRun.glyphsIDs().data(),
423                     glyphRun.positions().data(),
424                     fPositions,
425                     fPackedGlyphIDs);
426 
427             SkSpan<const SkGlyphPos> glyphPosSpan = strike->prepareForDrawingRemoveEmpty(
428                     packedGlyphIDs.data(),
429                     fPositions,
430                     glyphRun.runSize(),
431                     SkStrikeCommon::kSkSideTooBigForAtlas,
432                     SkStrikeInterface::kBoundsOnly,
433                     fGlyphPos);
434 
435             size_t glyphsWithMaskCount = 0;
436             for (const SkGlyphPos& glyphPos : glyphPosSpan) {
437                 const SkGlyph& glyph = *glyphPos.glyph;
438                 SkPoint position = glyphPos.position;
439 
440                 // The SDF scaler context system ensures that a glyph is empty, kSDF_Format, or
441                 // kARGB32_Format. The following if statements use this assumption.
442                 SkASSERT(glyph.maskFormat() == SkMask::kSDF_Format || glyph.isColor());
443 
444                 if (glyph.maskFormat() == SkMask::kSDF_Format
445                            && glyph.maxDimension() <= SkStrikeCommon::kSkSideTooBigForAtlas) {
446                     // SDF mask will work.
447                     fGlyphPos[glyphsWithMaskCount++] = glyphPos;
448                 } else if (!glyph.isColor() && glyph.path() != nullptr) {
449                     // If not color but too big, use a path.
450                     fPaths.push_back(glyphPos);
451                 } else {
452                     // If no path, or it is color, then fallback.
453                     addFallback(glyph, position);
454                 }
455             }
456 
457             if (process) {
458                 bool hasWCoord =
459                         viewMatrix.hasPerspective() || options.fDistanceFieldVerticesAlwaysHaveW;
460 
461                 // processSourceSDFT must be called even if there are no glyphs to make sure runs
462                 // are set correctly.
463                 process->processSourceSDFT(
464                         SkSpan<const SkGlyphPos>{fGlyphPos, glyphsWithMaskCount},
465                         strikeSpec,
466                         runFont,
467                         minScale,
468                         maxScale,
469                         hasWCoord);
470 
471                 if (!fPaths.empty()) {
472                     process->processSourcePaths(
473                             SkMakeSpan(fPaths),
474                             strikeSpec);
475                 }
476             }
477 
478             // fGlyphPos will be reused here.
479             if (!fARGBGlyphsIDs.empty()) {
480                 this->processARGBFallback(maxFallbackDimension * strikeSpec.strikeToSourceRatio(),
481                                           runPaint, runFont, viewMatrix, process);
482             }
483         } else if (SkStrikeSpec::ShouldDrawAsPath(runPaint, runFont, viewMatrix)) {
484             SkStrikeSpec strikeSpec = SkStrikeSpec::MakePath(
485                             runFont, runPaint, fDeviceProps, fScalerContextFlags);
486 
487             SkScopedStrike strike = strikeSpec.findOrCreateScopedStrike(fStrikeCache);
488 
489             auto packedGlyphIDs = SourceSpacePackedGlyphIDs(
490                     origin,
491                     glyphRun.runSize(),
492                     glyphRun.glyphsIDs().data(),
493                     glyphRun.positions().data(),
494                     fPositions,
495                     fPackedGlyphIDs);
496 
497             SkSpan<const SkGlyphPos> glyphPosSpan = strike->prepareForDrawingRemoveEmpty(
498                     packedGlyphIDs.data(),
499                     fPositions,
500                     glyphRun.runSize(),
501                     0,
502                     SkStrikeInterface::kBoundsOnly,
503                     fGlyphPos);
504 
505             // As opposed to SDF and mask, path handling puts paths in fGlyphPos instead of fPaths.
506             size_t glyphsWithPathCount = 0;
507             for (const SkGlyphPos& glyphPos : glyphPosSpan) {
508                 const SkGlyph& glyph = *glyphPos.glyph;
509                 SkPoint position = glyphPos.position;
510                 if (!glyph.isColor() && glyph.path() != nullptr) {
511                     // Place paths in fGlyphPos
512                     fGlyphPos[glyphsWithPathCount++] = glyphPos;
513                 } else {
514                     addFallback(glyph, position);
515                 }
516             }
517 
518             if (process) {
519                 // processSourcePaths must be called even if there are no glyphs to make sure runs
520                 // are set correctly.
521                 process->processSourcePaths(
522                         SkSpan<const SkGlyphPos>{fGlyphPos, glyphsWithPathCount},
523                         strikeSpec);
524             }
525 
526             // fGlyphPos will be reused here.
527             if (!fARGBGlyphsIDs.empty()) {
528                 this->processARGBFallback(maxFallbackDimension * strikeSpec.strikeToSourceRatio(),
529                                           runPaint, runFont, viewMatrix, process);
530             }
531         } else {
532             SkStrikeSpec strikeSpec =
533                     SkStrikeSpec::MakeMask(runFont, runPaint,
534                             fDeviceProps, fScalerContextFlags, viewMatrix);
535 
536             SkScopedStrike strike = strikeSpec.findOrCreateScopedStrike(fStrikeCache);
537 
538             auto packedGlyphIDs = DeviceSpacePackedGlyphIDs(
539                     strike.get(),
540                     viewMatrix,
541                     origin,
542                     glyphRun.runSize(),
543                     glyphRun.glyphsIDs().data(),
544                     glyphRun.positions().data(),
545                     fPositions,
546                     fPackedGlyphIDs);
547 
548             // Lookup all the glyphs from the cache. Strip empty glyphs.
549             SkSpan<const SkGlyphPos> glyphPosSpan = strike->prepareForDrawingRemoveEmpty(
550                     packedGlyphIDs.data(),
551                     fPositions,
552                     glyphRun.runSize(),
553                     SkStrikeCommon::kSkSideTooBigForAtlas,
554                     SkStrikeInterface::kBoundsOnly,
555                     fGlyphPos);
556 
557             // Sort glyphs into the three bins: mask (fGlyphPos), path (fPaths), and fallback.
558             size_t glyphsWithMaskCount = 0;
559             for (const SkGlyphPos& glyphPos : glyphPosSpan) {
560                 const SkGlyph& glyph = *glyphPos.glyph;
561                 const SkPoint position = glyphPos.position;
562 
563                 // Does the glyph have work to do or is the code able to position the glyph?
564                 if (!SkScalarsAreFinite(position.x(), position.y())) {
565                     // Do nothing;
566                 } else if (glyph.maxDimension() <= SkStrikeCommon::kSkSideTooBigForAtlas) {
567                     fGlyphPos[glyphsWithMaskCount++] = glyphPos;
568                 } else if (!glyph.isColor() && glyph.path() != nullptr) {
569                     fPaths.push_back(glyphPos);
570                 } else {
571                     addFallback(glyph, origin + glyphRun.positions()[glyphPos.index]);
572                 }
573             }
574 
575             if (process) {
576                 // processDeviceMasks must be called even if there are no glyphs to make sure runs
577                 // are set correctly.
578                 process->processDeviceMasks(
579                         SkSpan<const SkGlyphPos>{fGlyphPos, glyphsWithMaskCount}, strikeSpec);
580                 if (!fPaths.empty()) {
581                     process->processDevicePaths(SkMakeSpan(fPaths));
582                 }
583             }
584 
585             // fGlyphPos will be reused here.
586             if (!fARGBGlyphsIDs.empty()) {
587                 this->processARGBFallback(maxFallbackDimension / viewMatrix.getMaxScale(),
588                                           runPaint, runFont, viewMatrix, process);
589             }
590         }  // Mask case
591     }  // For all glyph runs
592 }
593 #endif  // SK_SUPPORT_GPU
594 
ensureBuffers(const SkGlyphRunList & glyphRunList)595 auto SkGlyphRunListPainter::ensureBuffers(const SkGlyphRunList& glyphRunList) -> ScopedBuffers {
596     size_t size = 0;
597     for (const SkGlyphRun& run : glyphRunList) {
598         size = std::max(run.runSize(), size);
599     }
600     return ScopedBuffers(this, size);
601 }
602 
603 SkGlyphRunListPainter::ScopedBuffers
ensureBuffers(const SkGlyphRun & glyphRun)604 SkGlyphRunListPainter::ensureBuffers(const SkGlyphRun& glyphRun) {
605     return ScopedBuffers(this, glyphRun.runSize());
606 }
607 
608 #if SK_SUPPORT_GPU
609 // -- GrTextContext --------------------------------------------------------------------------------
generate_filtered_color(const SkPaint & paint,const GrColorSpaceInfo & colorSpaceInfo)610 SkPMColor4f generate_filtered_color(const SkPaint& paint, const GrColorSpaceInfo& colorSpaceInfo) {
611     SkColor4f filteredColor = paint.getColor4f();
612     if (auto* xform = colorSpaceInfo.colorSpaceXformFromSRGB()) {
613         filteredColor = xform->apply(filteredColor);
614     }
615     if (paint.getColorFilter() != nullptr) {
616         filteredColor = paint.getColorFilter()->filterColor4f(filteredColor,
617                                                               colorSpaceInfo.colorSpace());
618     }
619     return filteredColor.premul();
620 }
621 
drawGlyphRunList(GrRecordingContext * context,GrTextTarget * target,const GrClip & clip,const SkMatrix & viewMatrix,const SkSurfaceProps & props,const SkGlyphRunList & glyphRunList)622 void GrTextContext::drawGlyphRunList(
623         GrRecordingContext* context, GrTextTarget* target, const GrClip& clip,
624         const SkMatrix& viewMatrix, const SkSurfaceProps& props,
625         const SkGlyphRunList& glyphRunList) {
626     SkPoint origin = glyphRunList.origin();
627 
628     // Get the first paint to use as the key paint.
629     const SkPaint& listPaint = glyphRunList.paint();
630 
631     SkPMColor4f filteredColor = generate_filtered_color(listPaint, target->colorSpaceInfo());
632     GrColor color = generate_filtered_color(listPaint, target->colorSpaceInfo()).toBytes_RGBA();
633 
634     // If we have been abandoned, then don't draw
635     if (context->priv().abandoned()) {
636         return;
637     }
638 
639     SkMaskFilterBase::BlurRec blurRec;
640     // It might be worth caching these things, but its not clear at this time
641     // TODO for animated mask filters, this will fill up our cache.  We need a safeguard here
642     const SkMaskFilter* mf = listPaint.getMaskFilter();
643     bool canCache = glyphRunList.canCache() && !(listPaint.getPathEffect() ||
644                                                  (mf && !as_MFB(mf)->asABlur(&blurRec)));
645     SkScalerContextFlags scalerContextFlags = ComputeScalerContextFlags(target->colorSpaceInfo());
646 
647     auto grStrikeCache = context->priv().getGrStrikeCache();
648     GrTextBlobCache* textBlobCache = context->priv().getTextBlobCache();
649 
650     sk_sp<GrTextBlob> cacheBlob;
651     GrTextBlob::Key key;
652     if (canCache) {
653         bool hasLCD = glyphRunList.anyRunsLCD();
654 
655         // We canonicalize all non-lcd draws to use kUnknown_SkPixelGeometry
656         SkPixelGeometry pixelGeometry = hasLCD ? props.pixelGeometry() :
657                                         kUnknown_SkPixelGeometry;
658 
659         // TODO we want to figure out a way to be able to use the canonical color on LCD text,
660         // see the note on ComputeCanonicalColor above.  We pick a dummy value for LCD text to
661         // ensure we always match the same key
662         GrColor canonicalColor = hasLCD ? SK_ColorTRANSPARENT :
663                                  ComputeCanonicalColor(listPaint, hasLCD);
664 
665         key.fPixelGeometry = pixelGeometry;
666         key.fUniqueID = glyphRunList.uniqueID();
667         key.fStyle = listPaint.getStyle();
668         key.fHasBlur = SkToBool(mf);
669         key.fCanonicalColor = canonicalColor;
670         key.fScalerContextFlags = scalerContextFlags;
671         cacheBlob = textBlobCache->find(key);
672     }
673 
674     if (cacheBlob) {
675         if (cacheBlob->mustRegenerate(listPaint, glyphRunList.anyRunsSubpixelPositioned(),
676                                       blurRec, viewMatrix, origin.x(),origin.y())) {
677             // We have to remake the blob because changes may invalidate our masks.
678             // TODO we could probably get away reuse most of the time if the pointer is unique,
679             // but we'd have to clear the subrun information
680             textBlobCache->remove(cacheBlob.get());
681             cacheBlob = textBlobCache->makeCachedBlob(
682                     glyphRunList, key, blurRec, listPaint, color, grStrikeCache);
683             cacheBlob->generateFromGlyphRunList(
684                     *context->priv().caps()->shaderCaps(), fOptions,
685                     listPaint, scalerContextFlags, viewMatrix, props,
686                     glyphRunList, target->glyphPainter());
687         } else {
688             textBlobCache->makeMRU(cacheBlob.get());
689 
690             if (CACHE_SANITY_CHECK) {
691                 sk_sp<GrTextBlob> sanityBlob(textBlobCache->makeBlob(
692                         glyphRunList, color, grStrikeCache));
693                 sanityBlob->setupKey(key, blurRec, listPaint);
694                 cacheBlob->generateFromGlyphRunList(
695                         *context->priv().caps()->shaderCaps(), fOptions,
696                         listPaint, scalerContextFlags, viewMatrix, props, glyphRunList,
697                         target->glyphPainter());
698                 GrTextBlob::AssertEqual(*sanityBlob, *cacheBlob);
699             }
700         }
701     } else {
702         if (canCache) {
703             cacheBlob = textBlobCache->makeCachedBlob(
704                     glyphRunList, key, blurRec, listPaint, color, grStrikeCache);
705         } else {
706             cacheBlob = textBlobCache->makeBlob(glyphRunList, color, grStrikeCache);
707         }
708         cacheBlob->generateFromGlyphRunList(
709                 *context->priv().caps()->shaderCaps(), fOptions, listPaint,
710                 scalerContextFlags, viewMatrix, props, glyphRunList,
711                 target->glyphPainter());
712     }
713 
714     cacheBlob->flush(target, props, fDistanceAdjustTable.get(), listPaint, filteredColor,
715                      clip, viewMatrix, origin.x(), origin.y());
716 }
717 
appendGlyph(GrGlyph * glyph,SkRect dstRect)718 void GrTextBlob::SubRun::appendGlyph(GrGlyph* glyph, SkRect dstRect) {
719 
720     this->joinGlyphBounds(dstRect);
721 
722     GrTextBlob* blob = fRun->fBlob;
723 
724     bool hasW = this->hasWCoord();
725     // glyphs drawn in perspective must always have a w coord.
726     SkASSERT(hasW || !blob->fInitialViewMatrix.hasPerspective());
727     auto maskFormat = this->maskFormat();
728     size_t vertexStride = GetVertexStride(maskFormat, hasW);
729 
730     intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices + fVertexEndIndex);
731 
732     // We always write the third position component used by SDFs. If it is unused it gets
733     // overwritten. Similarly, we always write the color and the blob will later overwrite it
734     // with texture coords if it is unused.
735     size_t colorOffset = hasW ? sizeof(SkPoint3) : sizeof(SkPoint);
736     // V0
737     *reinterpret_cast<SkPoint3*>(vertex) = {dstRect.fLeft, dstRect.fTop, 1.f};
738     *reinterpret_cast<GrColor*>(vertex + colorOffset) = fColor;
739     vertex += vertexStride;
740 
741     // V1
742     *reinterpret_cast<SkPoint3*>(vertex) = {dstRect.fLeft, dstRect.fBottom, 1.f};
743     *reinterpret_cast<GrColor*>(vertex + colorOffset) = fColor;
744     vertex += vertexStride;
745 
746     // V2
747     *reinterpret_cast<SkPoint3*>(vertex) = {dstRect.fRight, dstRect.fTop, 1.f};
748     *reinterpret_cast<GrColor*>(vertex + colorOffset) = fColor;
749     vertex += vertexStride;
750 
751     // V3
752     *reinterpret_cast<SkPoint3*>(vertex) = {dstRect.fRight, dstRect.fBottom, 1.f};
753     *reinterpret_cast<GrColor*>(vertex + colorOffset) = fColor;
754 
755     fVertexEndIndex += vertexStride * kVerticesPerGlyph;
756     blob->fGlyphs[fGlyphEndIndex++] = glyph;
757 }
758 
switchSubRunIfNeededAndAppendGlyph(GrGlyph * glyph,const sk_sp<GrTextStrike> & strike,const SkRect & destRect,bool needsTransform)759 void GrTextBlob::Run::switchSubRunIfNeededAndAppendGlyph(GrGlyph* glyph,
760                                                          const sk_sp<GrTextStrike>& strike,
761                                                          const SkRect& destRect,
762                                                          bool needsTransform) {
763     GrMaskFormat format = glyph->fMaskFormat;
764 
765     SubRun* subRun = &fSubRunInfo.back();
766     if (fInitialized && subRun->maskFormat() != format) {
767         subRun = pushBackSubRun(fStrikeSpec, fColor);
768         subRun->setStrike(strike);
769     } else if (!fInitialized) {
770         subRun->setStrike(strike);
771     }
772 
773     fInitialized = true;
774     subRun->setMaskFormat(format);
775     subRun->setNeedsTransform(needsTransform);
776     subRun->appendGlyph(glyph, destRect);
777 }
778 
appendDeviceSpaceGlyph(const sk_sp<GrTextStrike> & strike,const SkGlyph & skGlyph,SkPoint origin)779 void GrTextBlob::Run::appendDeviceSpaceGlyph(const sk_sp<GrTextStrike>& strike,
780                                              const SkGlyph& skGlyph, SkPoint origin) {
781     if (GrGlyph* glyph = strike->getGlyph(skGlyph)) {
782 
783         SkRect glyphRect = glyph->destRect(origin);
784 
785         if (!glyphRect.isEmpty()) {
786             this->switchSubRunIfNeededAndAppendGlyph(glyph, strike, glyphRect, false);
787         }
788     }
789 }
790 
appendSourceSpaceGlyph(const sk_sp<GrTextStrike> & strike,const SkGlyph & skGlyph,SkPoint origin,SkScalar textScale)791 void GrTextBlob::Run::appendSourceSpaceGlyph(const sk_sp<GrTextStrike>& strike,
792                                              const SkGlyph& skGlyph,
793                                              SkPoint origin,
794                                              SkScalar textScale) {
795     if (GrGlyph* glyph = strike->getGlyph(skGlyph)) {
796 
797         SkRect glyphRect = glyph->destRect(origin, textScale);
798 
799         if (!glyphRect.isEmpty()) {
800             this->switchSubRunIfNeededAndAppendGlyph(glyph, strike, glyphRect, true);
801         }
802     }
803 }
804 
generateFromGlyphRunList(const GrShaderCaps & shaderCaps,const GrTextContext::Options & options,const SkPaint & paint,SkScalerContextFlags scalerContextFlags,const SkMatrix & viewMatrix,const SkSurfaceProps & props,const SkGlyphRunList & glyphRunList,SkGlyphRunListPainter * glyphPainter)805 void GrTextBlob::generateFromGlyphRunList(const GrShaderCaps& shaderCaps,
806                                           const GrTextContext::Options& options,
807                                           const SkPaint& paint,
808                                           SkScalerContextFlags scalerContextFlags,
809                                           const SkMatrix& viewMatrix,
810                                           const SkSurfaceProps& props,
811                                           const SkGlyphRunList& glyphRunList,
812                                           SkGlyphRunListPainter* glyphPainter) {
813     SkPoint origin = glyphRunList.origin();
814     const SkPaint& runPaint = glyphRunList.paint();
815     this->initReusableBlob(SkPaintPriv::ComputeLuminanceColor(runPaint), viewMatrix,
816                            origin.x(), origin.y());
817 
818     glyphPainter->processGlyphRunList(glyphRunList,
819                                       viewMatrix,
820                                       props,
821                                       shaderCaps.supportsDistanceFieldText(),
822                                       options,
823                                       this);
824 }
825 
currentRun()826 GrTextBlob::Run* GrTextBlob::currentRun() {
827     return &fRuns[fRunCount - 1];
828 }
829 
startRun(const SkGlyphRun & glyphRun,bool useSDFT)830 void GrTextBlob::startRun(const SkGlyphRun& glyphRun, bool useSDFT) {
831     if (useSDFT) {
832         this->setHasDistanceField();
833     }
834     Run* run = this->pushBackRun();
835     run->setRunFontAntiAlias(glyphRun.font().hasSomeAntiAliasing());
836 }
837 
processDeviceMasks(SkSpan<const SkGlyphPos> masks,const SkStrikeSpec & strikeSpec)838 void GrTextBlob::processDeviceMasks(SkSpan<const SkGlyphPos> masks,
839                                     const SkStrikeSpec& strikeSpec) {
840     Run* run = this->currentRun();
841     this->setHasBitmap();
842     run->setupFont(strikeSpec);
843     sk_sp<GrTextStrike> currStrike = strikeSpec.findOrCreateGrStrike(fStrikeCache);
844     for (const auto& mask : masks) {
845         SkPoint pt{SkScalarFloorToScalar(mask.position.fX),
846                    SkScalarFloorToScalar(mask.position.fY)};
847         run->appendDeviceSpaceGlyph(currStrike, *mask.glyph, pt);
848     }
849 }
850 
processSourcePaths(SkSpan<const SkGlyphPos> paths,const SkStrikeSpec & strikeSpec)851 void GrTextBlob::processSourcePaths(SkSpan<const SkGlyphPos> paths,
852                                     const SkStrikeSpec& strikeSpec) {
853     Run* run = this->currentRun();
854     this->setHasBitmap();
855     run->setupFont(strikeSpec);
856     for (const auto& path : paths) {
857         if (const SkPath* glyphPath = path.glyph->path()) {
858             run->appendPathGlyph(*glyphPath, path.position, strikeSpec.strikeToSourceRatio(),
859                                  false);
860         }
861     }
862 }
863 
processDevicePaths(SkSpan<const SkGlyphPos> paths)864 void GrTextBlob::processDevicePaths(SkSpan<const SkGlyphPos> paths) {
865     Run* run = this->currentRun();
866     this->setHasBitmap();
867     for (const auto& path : paths) {
868         SkPoint pt{SkScalarFloorToScalar(path.position.fX),
869                    SkScalarFloorToScalar(path.position.fY)};
870         // TODO: path should always be set. Remove when proven.
871         if (const SkPath* glyphPath = path.glyph->path()) {
872             run->appendPathGlyph(*glyphPath, pt, SK_Scalar1, true);
873         }
874     }
875 }
876 
processSourceSDFT(SkSpan<const SkGlyphPos> masks,const SkStrikeSpec & strikeSpec,const SkFont & runFont,SkScalar minScale,SkScalar maxScale,bool hasWCoord)877 void GrTextBlob::processSourceSDFT(SkSpan<const SkGlyphPos> masks,
878                                    const SkStrikeSpec& strikeSpec,
879                                    const SkFont& runFont,
880                                    SkScalar minScale,
881                                    SkScalar maxScale,
882                                    bool hasWCoord) {
883 
884     Run* run = this->currentRun();
885     run->setSubRunHasDistanceFields(
886             runFont.getEdging() == SkFont::Edging::kSubpixelAntiAlias,
887             runFont.hasSomeAntiAliasing(),
888             hasWCoord);
889     this->setMinAndMaxScale(minScale, maxScale);
890     run->setupFont(strikeSpec);
891     sk_sp<GrTextStrike> currStrike = strikeSpec.findOrCreateGrStrike(fStrikeCache);
892     for (const auto& mask : masks) {
893         run->appendSourceSpaceGlyph(
894                 currStrike, *mask.glyph, mask.position, strikeSpec.strikeToSourceRatio());
895     }
896 }
897 
processSourceFallback(SkSpan<const SkGlyphPos> masks,const SkStrikeSpec & strikeSpec,bool hasW)898 void GrTextBlob::processSourceFallback(SkSpan<const SkGlyphPos> masks,
899                                        const SkStrikeSpec& strikeSpec,
900                                        bool hasW) {
901     Run* run = this->currentRun();
902 
903     auto subRun = run->initARGBFallback();
904     sk_sp<GrTextStrike> grStrike = strikeSpec.findOrCreateGrStrike(fStrikeCache);
905     subRun->setStrike(grStrike);
906     subRun->setHasWCoord(hasW);
907 
908     this->setHasBitmap();
909     run->setupFont(strikeSpec);
910     for (const auto& mask : masks) {
911         run->appendSourceSpaceGlyph
912                 (grStrike, *mask.glyph, mask.position, strikeSpec.strikeToSourceRatio());
913     }
914 }
915 
processDeviceFallback(SkSpan<const SkGlyphPos> masks,const SkStrikeSpec & strikeSpec)916 void GrTextBlob::processDeviceFallback(SkSpan<const SkGlyphPos> masks,
917                                        const SkStrikeSpec& strikeSpec) {
918     Run* run = this->currentRun();
919     this->setHasBitmap();
920     sk_sp<GrTextStrike> grStrike = strikeSpec.findOrCreateGrStrike(fStrikeCache);
921     auto subRun = run->initARGBFallback();
922     run->setupFont(strikeSpec);
923     subRun->setStrike(grStrike);
924     for (const auto& mask : masks) {
925         run->appendDeviceSpaceGlyph(grStrike, *mask.glyph, mask.position);
926     }
927 }
928 
929 #if GR_TEST_UTILS
930 
931 #include "src/gpu/GrRecordingContextPriv.h"
932 #include "src/gpu/GrRenderTargetContext.h"
933 
createOp_TestingOnly(GrRecordingContext * context,GrTextContext * textContext,GrRenderTargetContext * rtc,const SkPaint & skPaint,const SkFont & font,const SkMatrix & viewMatrix,const char * text,int x,int y)934 std::unique_ptr<GrDrawOp> GrTextContext::createOp_TestingOnly(GrRecordingContext* context,
935                                                               GrTextContext* textContext,
936                                                               GrRenderTargetContext* rtc,
937                                                               const SkPaint& skPaint,
938                                                               const SkFont& font,
939                                                               const SkMatrix& viewMatrix,
940                                                               const char* text,
941                                                               int x,
942                                                               int y) {
943     auto direct = context->priv().asDirectContext();
944     if (!direct) {
945         return nullptr;
946     }
947 
948     auto strikeCache = direct->priv().getGrStrikeCache();
949 
950     static SkSurfaceProps surfaceProps(SkSurfaceProps::kLegacyFontHost_InitType);
951 
952     size_t textLen = (int)strlen(text);
953 
954     SkPMColor4f filteredColor = generate_filtered_color(skPaint, rtc->colorSpaceInfo());
955     GrColor color = filteredColor.toBytes_RGBA();
956 
957     auto origin = SkPoint::Make(x, y);
958     SkGlyphRunBuilder builder;
959     builder.drawTextUTF8(skPaint, font, text, textLen, origin);
960 
961     auto glyphRunList = builder.useGlyphRunList();
962     sk_sp<GrTextBlob> blob;
963     if (!glyphRunList.empty()) {
964         blob = direct->priv().getTextBlobCache()->makeBlob(glyphRunList, color, strikeCache);
965         // Use the text and textLen below, because we don't want to mess with the paint.
966         SkScalerContextFlags scalerContextFlags =
967                 ComputeScalerContextFlags(rtc->colorSpaceInfo());
968         blob->generateFromGlyphRunList(
969                 *context->priv().caps()->shaderCaps(), textContext->fOptions,
970                 skPaint, scalerContextFlags, viewMatrix, surfaceProps,
971                 glyphRunList, rtc->textTarget()->glyphPainter());
972     }
973 
974     return blob->test_makeOp(textLen, 0, 0, viewMatrix, x, y, skPaint, filteredColor, surfaceProps,
975                              textContext->dfAdjustTable(), rtc->textTarget());
976 }
977 
978 #endif  // GR_TEST_UTILS
979 #endif  // SK_SUPPORT_GPU
980 
ScopedBuffers(SkGlyphRunListPainter * painter,int size)981 SkGlyphRunListPainter::ScopedBuffers::ScopedBuffers(SkGlyphRunListPainter* painter, int size)
982         : fPainter{painter} {
983     SkASSERT(size >= 0);
984     if (fPainter->fMaxRunSize < size) {
985         fPainter->fMaxRunSize = size;
986 
987         fPainter->fPositions.reset(size);
988         fPainter->fPackedGlyphIDs.reset(size);
989         fPainter->fGlyphPos.reset(size);
990     }
991 }
992 
~ScopedBuffers()993 SkGlyphRunListPainter::ScopedBuffers::~ScopedBuffers() {
994     fPainter->fPaths.clear();
995     fPainter->fARGBGlyphsIDs.clear();
996     fPainter->fARGBPositions.clear();
997 
998     if (fPainter->fMaxRunSize > 200) {
999         fPainter->fMaxRunSize = 0;
1000         fPainter->fPositions.reset();
1001         fPainter->fPackedGlyphIDs.reset();
1002         fPainter->fGlyphPos.reset();
1003         fPainter->fPaths.shrink_to_fit();
1004         fPainter->fARGBGlyphsIDs.shrink_to_fit();
1005         fPainter->fARGBPositions.shrink_to_fit();
1006     }
1007 }
1008