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