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