• 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/gpu/GrRecordingContext.h"
12 #include "src/gpu/GrCaps.h"
13 #include "src/gpu/GrColorInfo.h"
14 #include "src/gpu/GrDirectContextPriv.h"
15 #include "src/gpu/GrRecordingContextPriv.h"
16 #include "src/gpu/SkGr.h"
17 #include "src/gpu/text/GrSDFTControl.h"
18 #include "src/gpu/text/GrTextBlobRedrawCoordinator.h"
19 #include "src/gpu/v1/SurfaceDrawContext_v1.h"
20 #endif // SK_SUPPORT_GPU
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/SkEnumerate.h"
30 #include "src/core/SkFontPriv.h"
31 #include "src/core/SkRasterClip.h"
32 #include "src/core/SkScalerCache.h"
33 #include "src/core/SkStrikeCache.h"
34 #include "src/core/SkStrikeForGPU.h"
35 #include "src/core/SkStrikeSpec.h"
36 #include "src/core/SkTraceEvent.h"
37 
38 #include <cinttypes>
39 #include <climits>
40 
41 // -- SkGlyphRunListPainter ------------------------------------------------------------------------
SkGlyphRunListPainter(const SkSurfaceProps & props,SkColorType colorType,SkScalerContextFlags flags,SkStrikeForGPUCacheInterface * strikeCache)42 SkGlyphRunListPainter::SkGlyphRunListPainter(const SkSurfaceProps& props,
43                                              SkColorType colorType,
44                                              SkScalerContextFlags flags,
45                                              SkStrikeForGPUCacheInterface* strikeCache)
46         : fDeviceProps{props}
47         ,  fBitmapFallbackProps{SkSurfaceProps{props.flags(), kUnknown_SkPixelGeometry}}
48         ,  fColorType{colorType}, fScalerContextFlags{flags}
49         ,  fStrikeCache{strikeCache} {}
50 
51 // TODO: unify with code in GrSDFTControl.cpp
compute_scaler_context_flags(const SkColorSpace * cs)52 static SkScalerContextFlags compute_scaler_context_flags(const SkColorSpace* cs) {
53     // If we're doing linear blending, then we can disable the gamma hacks.
54     // Otherwise, leave them on. In either case, we still want the contrast boost:
55     // TODO: Can we be even smarter about mask gamma based on the dest transfer function?
56     if (cs && cs->gammaIsLinear()) {
57         return SkScalerContextFlags::kBoostContrast;
58     } else {
59         return SkScalerContextFlags::kFakeGammaAndBoostContrast;
60     }
61 }
62 
SkGlyphRunListPainter(const SkSurfaceProps & props,SkColorType colorType,SkColorSpace * cs,SkStrikeForGPUCacheInterface * strikeCache)63 SkGlyphRunListPainter::SkGlyphRunListPainter(const SkSurfaceProps& props,
64                                              SkColorType colorType,
65                                              SkColorSpace* cs,
66                                              SkStrikeForGPUCacheInterface* strikeCache)
67         : SkGlyphRunListPainter(props, colorType, compute_scaler_context_flags(cs), strikeCache) {}
68 
69 #if SK_SUPPORT_GPU
SkGlyphRunListPainter(const SkSurfaceProps & props,const GrColorInfo & csi)70 SkGlyphRunListPainter::SkGlyphRunListPainter(const SkSurfaceProps& props, const GrColorInfo& csi)
71         : SkGlyphRunListPainter(props,
72                                 kUnknown_SkColorType,
73                                 compute_scaler_context_flags(csi.colorSpace()),
74                                 SkStrikeCache::GlobalStrikeCache()) {}
75 
SkGlyphRunListPainter(const skgpu::v1::SurfaceDrawContext & sdc)76 SkGlyphRunListPainter::SkGlyphRunListPainter(const skgpu::v1::SurfaceDrawContext& sdc)
77         : SkGlyphRunListPainter{sdc.surfaceProps(), sdc.colorInfo()} {}
78 
79 #endif // SK_SUPPORT_GPU
80 
drawForBitmapDevice(SkCanvas * canvas,const BitmapDevicePainter * bitmapDevice,const SkGlyphRunList & glyphRunList,const SkPaint & paint,const SkMatrix & deviceMatrix)81 void SkGlyphRunListPainter::drawForBitmapDevice(
82         SkCanvas* canvas, const BitmapDevicePainter* bitmapDevice,
83         const SkGlyphRunList& glyphRunList, const SkPaint& paint, const SkMatrix& deviceMatrix) {
84     ScopedBuffers _ = this->ensureBuffers(glyphRunList);
85 
86     // TODO: fStrikeCache is only used for GPU, and some compilers complain about it during the no
87     //  gpu build. Remove when SkGlyphRunListPainter is split into GPU and CPU version.
88     (void)fStrikeCache;
89 
90     // The bitmap blitters can only draw lcd text to a N32 bitmap in srcOver. Otherwise,
91     // convert the lcd text into A8 text. The props communicates this to the scaler.
92     auto& props = (kN32_SkColorType == fColorType && paint.isSrcOver())
93                   ? fDeviceProps
94                   : fBitmapFallbackProps;
95 
96     SkPoint drawOrigin = glyphRunList.origin();
97     for (auto& glyphRun : glyphRunList) {
98         const SkFont& runFont = glyphRun.font();
99 
100         fRejected.setSource(glyphRun.source());
101 
102         if (SkStrikeSpec::ShouldDrawAsPath(paint, runFont, deviceMatrix)) {
103 
104             auto [strikeSpec, strikeToSourceScale] =
105                     SkStrikeSpec::MakePath(runFont, paint, props, fScalerContextFlags);
106 
107             auto strike = strikeSpec.findOrCreateStrike();
108 
109             fAccepted.startSource(fRejected.source());
110             strike->prepareForPathDrawing(&fAccepted, &fRejected);
111             fRejected.flipRejectsToSource();
112 
113             // The paint we draw paths with must have the same anti-aliasing state as the runFont
114             // allowing the paths to have the same edging as the glyph masks.
115             SkPaint pathPaint = paint;
116             pathPaint.setAntiAlias(runFont.hasSomeAntiAliasing());
117 
118             const bool stroking = pathPaint.getStyle() != SkPaint::kFill_Style;
119             const bool hairline = pathPaint.getStrokeWidth() == 0;
120             const bool needsExactCTM = pathPaint.getShader()
121                                     || pathPaint.getPathEffect()
122                                     || pathPaint.getMaskFilter()
123                                     || (stroking && !hairline);
124             if (!needsExactCTM) {
125                 for (auto [variant, pos] : fAccepted.accepted()) {
126                     const SkPath* path = variant.path();
127                     SkMatrix m;
128                     SkPoint translate = drawOrigin + pos;
129                     m.setScaleTranslate(strikeToSourceScale, strikeToSourceScale,
130                                         translate.x(), translate.y());
131                     SkAutoCanvasRestore acr(canvas, true);
132                     canvas->concat(m);
133                     canvas->drawPath(*path, pathPaint);
134                 }
135             } else {
136                for (auto [variant, pos] : fAccepted.accepted()) {
137                     const SkPath* path = variant.path();
138                     SkMatrix m;
139                     SkPoint translate = drawOrigin + pos;
140                     m.setScaleTranslate(strikeToSourceScale, strikeToSourceScale,
141                                         translate.x(), translate.y());
142 
143                     SkPath deviceOutline;
144                     path->transform(m, &deviceOutline);
145                     deviceOutline.setIsVolatile(true);
146                     canvas->drawPath(deviceOutline, pathPaint);
147                 }
148             }
149 
150             if (!fRejected.source().empty()) {
151                 fAccepted.startSource(fRejected.source());
152                 strike->prepareForDrawableDrawing(&fAccepted, &fRejected);
153                 fRejected.flipRejectsToSource();
154 
155                 for (auto [variant, pos] : fAccepted.accepted()) {
156                     SkDrawable* drawable = variant.drawable();
157                     SkMatrix m;
158                     SkPoint translate = drawOrigin + pos;
159                     m.setScaleTranslate(strikeToSourceScale, strikeToSourceScale,
160                                         translate.x(), translate.y());
161                     SkAutoCanvasRestore acr(canvas, false);
162                     SkRect drawableBounds = drawable->getBounds();
163                     m.mapRect(&drawableBounds);
164                     canvas->saveLayer(&drawableBounds, &paint);
165                     drawable->draw(canvas, &m);
166                 }
167             }
168         }
169         if (!fRejected.source().empty() && !deviceMatrix.hasPerspective()) {
170             SkStrikeSpec strikeSpec = SkStrikeSpec::MakeMask(
171                     runFont, paint, props, fScalerContextFlags, deviceMatrix);
172 
173             auto strike = strikeSpec.findOrCreateStrike();
174 
175             fAccepted.startBitmapDevice(
176                     fRejected.source(), drawOrigin, deviceMatrix, strike->roundingSpec());
177             strike->prepareForDrawingMasksCPU(&fAccepted);
178             fRejected.flipRejectsToSource();
179             bitmapDevice->paintMasks(&fAccepted, paint);
180         }
181         if (!fRejected.source().empty()) {
182             SkMatrix runMatrix = deviceMatrix;
183             runMatrix.preTranslate(drawOrigin.x(), drawOrigin.y());
184             std::vector<SkPoint> sourcePositions;
185 
186             // Create a strike is source space to calculate scale information.
187             SkStrikeSpec scaleStrikeSpec = SkStrikeSpec::MakeMask(
188                     runFont, paint, props, fScalerContextFlags, SkMatrix::I());
189             SkBulkGlyphMetrics metrics{scaleStrikeSpec};
190 
191             auto glyphIDs = fRejected.source().get<0>();
192             auto positions = fRejected.source().get<1>();
193             SkSpan<const SkGlyph*> glyphs = metrics.glyphs(glyphIDs);
194             SkScalar maxScale = SK_ScalarMin;
195 
196             // Calculate the scale that makes the longest edge 1:1 with its side in the cache.
197             for (auto [glyph, pos] : SkMakeZip(glyphs, positions)) {
198                 SkPoint corners[4];
199                 SkPoint srcPos = pos + drawOrigin;
200                 // Store off the positions in device space to position the glyphs during drawing.
201                 sourcePositions.push_back(srcPos);
202                 SkRect rect = glyph->rect();
203                 rect.makeOffset(srcPos);
204                 runMatrix.mapRectToQuad(corners, rect);
205                 // left top -> right top
206                 SkScalar scale = (corners[1] - corners[0]).length() / rect.width();
207                 maxScale = std::max(maxScale, scale);
208                 // right top -> right bottom
209                 scale = (corners[2] - corners[1]).length() / rect.height();
210                 maxScale = std::max(maxScale, scale);
211                 // right bottom -> left bottom
212                 scale = (corners[3] - corners[2]).length() / rect.width();
213                 maxScale = std::max(maxScale, scale);
214                 // left bottom -> left top
215                 scale = (corners[0] - corners[3]).length() / rect.height();
216                 maxScale = std::max(maxScale, scale);
217             }
218 
219             if (maxScale * runFont.getSize() > 256) {
220                 maxScale = 256.0f / runFont.getSize();
221             }
222 
223             SkMatrix cacheScale = SkMatrix::Scale(maxScale, maxScale);
224             SkStrikeSpec strikeSpec = SkStrikeSpec::MakeMask(
225                     runFont, paint, props, fScalerContextFlags, cacheScale);
226 
227             auto strike = strikeSpec.findOrCreateStrike();
228 
229             // Figure out all the positions and packed glyphIDs based on the device matrix.
230             fAccepted.startBitmapDevice(
231                     fRejected.source(), drawOrigin, deviceMatrix, strike->roundingSpec());
232 
233             strike->prepareForDrawingMasksCPU(&fAccepted);
234             auto variants = fAccepted.accepted().get<0>();
235             for (auto [variant, srcPos] : SkMakeZip(variants, sourcePositions)) {
236                 const SkGlyph* glyph = variant.glyph();
237                 SkMask mask = glyph->mask();
238                 // TODO: is this needed will A8 and BW just work?
239                 if (mask.fFormat != SkMask::kARGB32_Format) {
240                     continue;
241                 }
242                 SkBitmap bm;
243                 bm.installPixels(SkImageInfo::MakeN32Premul(mask.fBounds.size()),
244                                  mask.fImage,
245                                  mask.fRowBytes);
246 
247                 // Since the glyph in the cache is scaled by maxScale, its top left vector is too
248                 // long. Reduce it to find proper positions on the device.
249                 SkPoint realPos = srcPos
250                         + SkPoint::Make(mask.fBounds.left(), mask.fBounds.top()) * (1.0f/maxScale);
251 
252                 // Calculate the preConcat matrix for drawBitmap to get the rectangle from the
253                 // glyph cache (which is multiplied by maxScale) to land in the right place.
254                 SkMatrix translate = SkMatrix::Translate(realPos);
255                 translate.preScale(1.0f/maxScale, 1.0f/maxScale);
256 
257                 // Draw the bitmap using the rect from the scaled cache, and not the source
258                 // rectangle for the glyph.
259                 bitmapDevice->drawBitmap(
260                         bm, translate, nullptr, SkSamplingOptions{SkFilterMode::kLinear},
261                         paint);
262             }
263             fRejected.flipRejectsToSource();
264         }
265 
266         // TODO: have the mask stage above reject the glyphs that are too big, and handle the
267         //  rejects in a more sophisticated stage.
268     }
269 }
270 
271 // Use the following in your args.gn to dump telemetry for diagnosing chrome Renderer/GPU
272 // differences.
273 // extra_cflags = ["-D", "SK_TRACE_GLYPH_RUN_PROCESS"]
274 
275 #if SK_SUPPORT_GPU
processGlyphRun(SkGlyphRunPainterInterface * process,const SkGlyphRun & glyphRun,const SkMatrix & drawMatrix,const SkPaint & runPaint,const GrSDFTControl & control,const char * tag,uint64_t uniqueID)276 void SkGlyphRunListPainter::processGlyphRun(SkGlyphRunPainterInterface* process,
277                                             const SkGlyphRun& glyphRun,
278                                             const SkMatrix& drawMatrix,
279                                             const SkPaint& runPaint,
280                                             const GrSDFTControl& control,
281                                             const char* tag,
282                                             uint64_t uniqueID) {
283     #if defined(SK_TRACE_GLYPH_RUN_PROCESS)
284         SkString msg;
285         msg.appendf("\nStart glyph run processing");
286         if (tag != nullptr) {
287             msg.appendf(" for %s ", tag);
288             if (uniqueID != SK_InvalidUniqueID) {
289                 msg.appendf(" uniqueID: %" PRIu64, uniqueID);
290             }
291         }
292         msg.appendf("\n   matrix\n");
293         msg.appendf("   %7.3g %7.3g %7.3g\n   %7.3g %7.3g %7.3g\n",
294                     drawMatrix[0], drawMatrix[1], drawMatrix[2],
295                     drawMatrix[3], drawMatrix[4], drawMatrix[5]);
296     #endif
297     ScopedBuffers _ = this->ensureBuffers(glyphRun);
298     fRejected.setSource(glyphRun.source());
299     const SkFont& runFont = glyphRun.font();
300 
301     // Only consider using direct or SDFT drawing if not drawing hairlines and not perspective.
302     if ((runPaint.getStyle() != SkPaint::kStroke_Style || runPaint.getStrokeWidth() != 0)
303              && !drawMatrix.hasPerspective()) {
304         SkScalar approximateDeviceTextSize =
305                 SkFontPriv::ApproximateTransformedTextSize(runFont, drawMatrix);
306 
307         if (control.isSDFT(approximateDeviceTextSize, runPaint)) {
308             // Process SDFT - This should be the .009% case.
309             const auto& [strikeSpec, strikeToSourceScale, matrixRange] =
310                     SkStrikeSpec::MakeSDFT(runFont, runPaint, fDeviceProps, drawMatrix, control);
311 
312             #if defined(SK_TRACE_GLYPH_RUN_PROCESS)
313                 msg.appendf("  SDFT case:\n%s", strikeSpec.dump().c_str());
314             #endif
315 
316             if (!SkScalarNearlyZero(strikeToSourceScale)) {
317                 SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(fStrikeCache);
318 
319                 fAccepted.startSource(fRejected.source());
320                 #if defined(SK_TRACE_GLYPH_RUN_PROCESS)
321                     msg.appendf("    glyphs:(x,y):\n      %s\n", fAccepted.dumpInput().c_str());
322                 #endif
323                 strike->prepareForSDFTDrawing(&fAccepted, &fRejected);
324                 fRejected.flipRejectsToSource();
325 
326                 if (process && !fAccepted.empty()) {
327                     // processSourceSDFT must be called even if there are no glyphs to make sure
328                     // runs are set correctly.
329                     process->processSourceSDFT(fAccepted.accepted(),
330                                                strike->getUnderlyingStrike(),
331                                                strikeToSourceScale,
332                                                runFont,
333                                                matrixRange);
334                 }
335             }
336         }
337 
338         if (!fRejected.source().empty()) {
339             // Process masks including ARGB - this should be the 99.99% case.
340             // This will handle medium size emoji that are sharing the run with SDFT drawn text.
341             // If things are too big they will be passed along to the drawing of last resort below.
342             SkStrikeSpec strikeSpec = SkStrikeSpec::MakeMask(
343                     runFont, runPaint, fDeviceProps, fScalerContextFlags, drawMatrix);
344 
345             #if defined(SK_TRACE_GLYPH_RUN_PROCESS)
346                 msg.appendf("  Mask case:\n%s", strikeSpec.dump().c_str());
347             #endif
348 
349             SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(fStrikeCache);
350 
351             fAccepted.startGPUDevice(fRejected.source(), drawMatrix, strike->roundingSpec());
352             #if defined(SK_TRACE_GLYPH_RUN_PROCESS)
353                 msg.appendf("    glyphs:(x,y):\n      %s\n", fAccepted.dumpInput().c_str());
354             #endif
355             strike->prepareForMaskDrawing(&fAccepted, &fRejected);
356             fRejected.flipRejectsToSource();
357 
358             if (process && !fAccepted.empty()) {
359                 // processDeviceMasks must be called even if there are no glyphs to make sure runs
360                 // are set correctly.
361                 process->processDeviceMasks(fAccepted.accepted(), strike->getUnderlyingStrike());
362             }
363         }
364     }
365 
366     // Glyphs are generated in different scales relative to the source space. Masks are drawn
367     // in device space, and SDFT and Paths are draw in a fixed constant space. The
368     // maxDimensionInSourceSpace is used to calculate the factor from strike space to source
369     // space.
370     SkScalar maxDimensionInSourceSpace = 0.0;
371     if (!fRejected.source().empty()) {
372         // Drawable case - handle big things with that have a drawable.
373         auto [strikeSpec, strikeToSourceScale] =
374                 SkStrikeSpec::MakePath(runFont, runPaint, fDeviceProps, fScalerContextFlags);
375 
376         #if defined(SK_TRACE_GLYPH_RUN_PROCESS)
377             msg.appendf("  Drawable case:\n%s", strikeSpec.dump().c_str());
378         #endif
379 
380         if (!SkScalarNearlyZero(strikeToSourceScale)) {
381             SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(fStrikeCache);
382 
383             fAccepted.startSource(fRejected.source());
384             #if defined(SK_TRACE_GLYPH_RUN_PROCESS)
385                 msg.appendf("    glyphs:(x,y):\n      %s\n", fAccepted.dumpInput().c_str());
386             #endif
387             strike->prepareForDrawableDrawing(&fAccepted, &fRejected);
388             fRejected.flipRejectsToSource();
389             auto [minHint, maxHint] = fRejected.maxDimensionHint();
390             maxDimensionInSourceSpace = SkScalarCeilToScalar(maxHint * strikeToSourceScale);
391 
392             if (process && !fAccepted.empty()) {
393                 // processSourceDrawables must be called even if there are no glyphs to make sure
394                 // runs are set correctly.
395                 process->processSourceDrawables(fAccepted.accepted(), runFont, strikeToSourceScale);
396             }
397         }
398     }
399     if (!fRejected.source().empty()) {
400         // Path case - handle big things without color and that have a path.
401         auto [strikeSpec, strikeToSourceScale] =
402                 SkStrikeSpec::MakePath(runFont, runPaint, fDeviceProps, fScalerContextFlags);
403 
404         #if defined(SK_TRACE_GLYPH_RUN_PROCESS)
405             msg.appendf("  Path case:\n%s", strikeSpec.dump().c_str());
406         #endif
407 
408         if (!SkScalarNearlyZero(strikeToSourceScale)) {
409             SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(fStrikeCache);
410 
411             fAccepted.startSource(fRejected.source());
412             #if defined(SK_TRACE_GLYPH_RUN_PROCESS)
413                 msg.appendf("    glyphs:(x,y):\n      %s\n", fAccepted.dumpInput().c_str());
414             #endif
415             strike->prepareForPathDrawing(&fAccepted, &fRejected);
416             fRejected.flipRejectsToSource();
417             auto [minHint, maxHint] = fRejected.maxDimensionHint();
418             maxDimensionInSourceSpace = SkScalarCeilToScalar(maxHint * strikeToSourceScale);
419 
420             if (process && !fAccepted.empty()) {
421                 // processSourcePaths must be called even if there are no glyphs to make sure
422                 // runs are set correctly.
423                 process->processSourcePaths(fAccepted.accepted(), runFont, strikeToSourceScale);
424             }
425         }
426     }
427 
428     if (!fRejected.source().empty() && maxDimensionInSourceSpace != 0) {
429         // Draw of last resort. Scale the bitmap to the screen.
430         auto [strikeSpec, strikeToSourceScale] = SkStrikeSpec::MakeSourceFallback(
431                 runFont, runPaint, fDeviceProps,
432                 fScalerContextFlags, maxDimensionInSourceSpace);
433 
434         #if defined(SK_TRACE_GLYPH_RUN_PROCESS)
435             msg.appendf("Transformed case:\n%s", strikeSpec.dump().c_str());
436         #endif
437 
438         if (!SkScalarNearlyZero(strikeToSourceScale)) {
439             SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(fStrikeCache);
440 
441             fAccepted.startSource(fRejected.source());
442             #if defined(SK_TRACE_GLYPH_RUN_PROCESS)
443                 msg.appendf("glyphs:(x,y):\n      %s\n", fAccepted.dumpInput().c_str());
444             #endif
445             strike->prepareForMaskDrawing(&fAccepted, &fRejected);
446             fRejected.flipRejectsToSource();
447             SkASSERT(fRejected.source().empty());
448 
449             if (process && !fAccepted.empty()) {
450                 process->processSourceMasks(
451                         fAccepted.accepted(), strike->getUnderlyingStrike(), strikeToSourceScale);
452             }
453         }
454     }
455     #if defined(SK_TRACE_GLYPH_RUN_PROCESS)
456         msg.appendf("End glyph run processing");
457         if (tag != nullptr) {
458             msg.appendf(" for %s ", tag);
459         }
460         SkDebugf("%s\n", msg.c_str());
461     #endif
462 }
463 #endif  // SK_SUPPORT_GPU
464 
ensureBuffers(const SkGlyphRunList & glyphRunList)465 auto SkGlyphRunListPainter::ensureBuffers(const SkGlyphRunList& glyphRunList) -> ScopedBuffers {
466     size_t size = 0;
467     for (const SkGlyphRun& run : glyphRunList) {
468         size = std::max(run.runSize(), size);
469     }
470     return ScopedBuffers(this, size);
471 }
472 
ensureBuffers(const SkGlyphRun & glyphRun)473 auto SkGlyphRunListPainter::ensureBuffers(const SkGlyphRun& glyphRun) -> ScopedBuffers {
474     return ScopedBuffers(this, glyphRun.runSize());
475 }
476 
ScopedBuffers(SkGlyphRunListPainter * painter,size_t size)477 SkGlyphRunListPainter::ScopedBuffers::ScopedBuffers(SkGlyphRunListPainter* painter, size_t size)
478         : fPainter{painter} {
479     fPainter->fAccepted.ensureSize(size);
480 }
481 
~ScopedBuffers()482 SkGlyphRunListPainter::ScopedBuffers::~ScopedBuffers() {
483     fPainter->fAccepted.reset();
484     fPainter->fRejected.reset();
485 }
486 
HalfAxisSampleFreq(bool isSubpixel,SkAxisAlignment axisAlignment)487 SkVector SkGlyphPositionRoundingSpec::HalfAxisSampleFreq(
488         bool isSubpixel, SkAxisAlignment axisAlignment) {
489     if (!isSubpixel) {
490         return {SK_ScalarHalf, SK_ScalarHalf};
491     } else {
492         switch (axisAlignment) {
493             case kX_SkAxisAlignment:
494                 return {SkPackedGlyphID::kSubpixelRound, SK_ScalarHalf};
495             case kY_SkAxisAlignment:
496                 return {SK_ScalarHalf, SkPackedGlyphID::kSubpixelRound};
497             case kNone_SkAxisAlignment:
498                 return {SkPackedGlyphID::kSubpixelRound, SkPackedGlyphID::kSubpixelRound};
499         }
500     }
501 
502     // Some compilers need this.
503     return {0, 0};
504 }
505 
IgnorePositionMask(bool isSubpixel,SkAxisAlignment axisAlignment)506 SkIPoint SkGlyphPositionRoundingSpec::IgnorePositionMask(
507         bool isSubpixel, SkAxisAlignment axisAlignment) {
508     return SkIPoint::Make((!isSubpixel || axisAlignment == kY_SkAxisAlignment) ? 0 : ~0,
509                           (!isSubpixel || axisAlignment == kX_SkAxisAlignment) ? 0 : ~0);
510 }
511 
IgnorePositionFieldMask(bool isSubpixel,SkAxisAlignment axisAlignment)512 SkIPoint SkGlyphPositionRoundingSpec::IgnorePositionFieldMask(bool isSubpixel,
513                                                               SkAxisAlignment axisAlignment) {
514     SkIPoint ignoreMask = IgnorePositionMask(isSubpixel, axisAlignment);
515     SkIPoint answer{ignoreMask.x() & SkPackedGlyphID::kXYFieldMask.x(),
516                     ignoreMask.y() & SkPackedGlyphID::kXYFieldMask.y()};
517     return answer;
518 }
519 
SkGlyphPositionRoundingSpec(bool isSubpixel,SkAxisAlignment axisAlignment)520 SkGlyphPositionRoundingSpec::SkGlyphPositionRoundingSpec(
521         bool isSubpixel,SkAxisAlignment axisAlignment)
522     : halfAxisSampleFreq{HalfAxisSampleFreq(isSubpixel, axisAlignment)}
523     , ignorePositionMask{IgnorePositionMask(isSubpixel, axisAlignment)}
524     , ignorePositionFieldMask {IgnorePositionFieldMask(isSubpixel, axisAlignment)}{ }
525