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