1 /*
2 * Copyright 2015 Google Inc.
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 "include/core/SkColorFilter.h"
9 #include "include/gpu/GrRecordingContext.h"
10 #include "include/private/SkTemplates.h"
11 #include "src/core/SkMaskFilterBase.h"
12 #include "src/core/SkMatrixProvider.h"
13 #include "src/core/SkPaintPriv.h"
14 #include "src/core/SkStrikeCache.h"
15 #include "src/core/SkStrikeSpec.h"
16 #include "src/gpu/GrClip.h"
17 #include "src/gpu/GrGlyph.h"
18 #include "src/gpu/GrMeshDrawTarget.h"
19 #include "src/gpu/GrRecordingContextPriv.h"
20 #include "src/gpu/GrStyle.h"
21 #include "src/gpu/SkGr.h"
22 #include "src/gpu/effects/GrDistanceFieldGeoProc.h"
23 #include "src/gpu/geometry/GrStyledShape.h"
24 #include "src/gpu/text/GrAtlasManager.h"
25 #include "src/gpu/text/GrStrikeCache.h"
26 #include "src/gpu/text/GrTextBlob.h"
27
28 #include "src/gpu/GrBlurUtils.h"
29 #include "src/gpu/ops/AtlasTextOp.h"
30 #include "src/gpu/v1/SurfaceDrawContext_v1.h"
31
32 using AtlasTextOp = skgpu::v1::AtlasTextOp;
33
34 namespace {
35 struct AtlasPt {
36 uint16_t u;
37 uint16_t v;
38 };
39
40 // Normal text mask, SDFT, or color.
41 struct Mask2DVertex {
42 SkPoint devicePos;
43 GrColor color;
44 AtlasPt atlasPos;
45 };
46
47 struct ARGB2DVertex {
ARGB2DVertex__anonac70990d0111::ARGB2DVertex48 ARGB2DVertex(SkPoint d, GrColor, AtlasPt a) : devicePos{d}, atlasPos{a} {}
49
50 SkPoint devicePos;
51 AtlasPt atlasPos;
52 };
53
54 // Perspective SDFT or SDFT forced to 3D or perspective color.
55 struct Mask3DVertex {
56 SkPoint3 devicePos;
57 GrColor color;
58 AtlasPt atlasPos;
59 };
60
61 struct ARGB3DVertex {
ARGB3DVertex__anonac70990d0111::ARGB3DVertex62 ARGB3DVertex(SkPoint3 d, GrColor, AtlasPt a) : devicePos{d}, atlasPos{a} {}
63
64 SkPoint3 devicePos;
65 AtlasPt atlasPos;
66 };
67
op_mask_type(GrMaskFormat grMaskFormat)68 AtlasTextOp::MaskType op_mask_type(GrMaskFormat grMaskFormat) {
69 switch (grMaskFormat) {
70 case kA8_GrMaskFormat: return AtlasTextOp::MaskType::kGrayscaleCoverage;
71 case kA565_GrMaskFormat: return AtlasTextOp::MaskType::kLCDCoverage;
72 case kARGB_GrMaskFormat: return AtlasTextOp::MaskType::kColorBitmap;
73 }
74 SkUNREACHABLE;
75 }
76
calculate_colors(skgpu::SurfaceContext * sc,const SkPaint & paint,const SkMatrixProvider & matrix,GrMaskFormat grMaskFormat,GrPaint * grPaint)77 SkPMColor4f calculate_colors(skgpu::SurfaceContext* sc,
78 const SkPaint& paint,
79 const SkMatrixProvider& matrix,
80 GrMaskFormat grMaskFormat,
81 GrPaint* grPaint) {
82 GrRecordingContext* rContext = sc->recordingContext();
83 const GrColorInfo& colorInfo = sc->colorInfo();
84 if (grMaskFormat == kARGB_GrMaskFormat) {
85 SkPaintToGrPaintWithPrimitiveColor(rContext, colorInfo, paint, matrix, grPaint);
86 return SK_PMColor4fWHITE;
87 } else {
88 SkPaintToGrPaint(rContext, colorInfo, paint, matrix, grPaint);
89 return grPaint->getColor4f();
90 }
91 }
92
93 template<typename Quad, typename VertexData>
fill_transformed_vertices_2D(SkZip<Quad,const GrGlyph *,const VertexData> quadData,SkScalar dstPadding,SkScalar strikeToSource,GrColor color,const SkMatrix & matrix)94 void fill_transformed_vertices_2D(SkZip<Quad, const GrGlyph*, const VertexData> quadData,
95 SkScalar dstPadding,
96 SkScalar strikeToSource,
97 GrColor color,
98 const SkMatrix& matrix) {
99 SkPoint inset = {dstPadding, dstPadding};
100 for (auto[quad, glyph, vertexData] : quadData) {
101 auto[pos, rect] = vertexData;
102 auto[l, t, r, b] = rect;
103 SkPoint sLT = (SkPoint::Make(l, t) + inset) * strikeToSource + pos,
104 sRB = (SkPoint::Make(r, b) - inset) * strikeToSource + pos;
105 SkPoint lt = matrix.mapXY(sLT.x(), sLT.y()),
106 lb = matrix.mapXY(sLT.x(), sRB.y()),
107 rt = matrix.mapXY(sRB.x(), sLT.y()),
108 rb = matrix.mapXY(sRB.x(), sRB.y());
109 auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
110 quad[0] = {lt, color, {al, at}}; // L,T
111 quad[1] = {lb, color, {al, ab}}; // L,B
112 quad[2] = {rt, color, {ar, at}}; // R,T
113 quad[3] = {rb, color, {ar, ab}}; // R,B
114 }
115 }
116
117 template<typename Quad, typename VertexData>
fill_transformed_vertices_3D(SkZip<Quad,const GrGlyph *,const VertexData> quadData,SkScalar dstPadding,SkScalar strikeToSource,GrColor color,const SkMatrix & positionMatrix)118 void fill_transformed_vertices_3D(SkZip<Quad, const GrGlyph*, const VertexData> quadData,
119 SkScalar dstPadding,
120 SkScalar strikeToSource,
121 GrColor color,
122 const SkMatrix& positionMatrix) {
123 SkPoint inset = {dstPadding, dstPadding};
124 auto mapXYZ = [&](SkScalar x, SkScalar y) {
125 SkPoint pt{x, y};
126 SkPoint3 result;
127 positionMatrix.mapHomogeneousPoints(&result, &pt, 1);
128 return result;
129 };
130 for (auto[quad, glyph, vertexData] : quadData) {
131 auto[pos, rect] = vertexData;
132 auto [l, t, r, b] = rect;
133 SkPoint sLT = (SkPoint::Make(l, t) + inset) * strikeToSource + pos,
134 sRB = (SkPoint::Make(r, b) - inset) * strikeToSource + pos;
135 SkPoint3 lt = mapXYZ(sLT.x(), sLT.y()),
136 lb = mapXYZ(sLT.x(), sRB.y()),
137 rt = mapXYZ(sRB.x(), sLT.y()),
138 rb = mapXYZ(sRB.x(), sRB.y());
139 auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
140 quad[0] = {lt, color, {al, at}}; // L,T
141 quad[1] = {lb, color, {al, ab}}; // L,B
142 quad[2] = {rt, color, {ar, at}}; // R,T
143 quad[3] = {rb, color, {ar, ab}}; // R,B
144 }
145 }
146
147 // Check for integer translate with the same 2x2 matrix.
check_integer_translate(const SkMatrix & initialMatrix,const SkMatrix & drawMatrix)148 std::tuple<bool, SkVector> check_integer_translate(
149 const SkMatrix& initialMatrix, const SkMatrix& drawMatrix) {
150 if (initialMatrix.getScaleX() != drawMatrix.getScaleX() ||
151 initialMatrix.getScaleY() != drawMatrix.getScaleY() ||
152 initialMatrix.getSkewX() != drawMatrix.getSkewX() ||
153 initialMatrix.getSkewY() != drawMatrix.getSkewY())
154 {
155 return {false, {0, 0}};
156 }
157
158 // We can update the positions in the text blob without regenerating the whole
159 // blob, but only for integer translations.
160 // Calculate the translation in source space to a translation in device space by mapping
161 // (0, 0) through both the initial matrix and the draw matrix; take the difference.
162 return {true, {0, 0}};
163 }
164
165 // -- PathSubRun -----------------------------------------------------------------------------------
166 class PathSubRun final : public GrSubRun {
167 struct PathGlyph;
168
169 public:
170 PathSubRun(bool isAntiAliased,
171 SkScalar strikeToSourceScale,
172 const GrTextBlob& blob,
173 SkSpan<PathGlyph> paths,
174 std::unique_ptr<PathGlyph[], GrSubRunAllocator::ArrayDestroyer> pathData);
175
176 void draw(const GrClip*,
177 const SkMatrixProvider& viewMatrix,
178 const SkGlyphRunList&,
179 const SkPaint&,
180 skgpu::v1::SurfaceDrawContext*) const override;
181
182 bool canReuse(const SkPaint& paint, const SkMatrix& drawMatrix) const override;
183
184 GrAtlasSubRun* testingOnly_atlasSubRun() override;
185
186 static GrSubRunOwner Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
187 bool isAntiAliased,
188 SkScalar strikeToSourceScale,
189 const GrTextBlob& blob,
190 GrSubRunAllocator* alloc);
191
192 private:
193 struct PathGlyph {
194 PathGlyph(const SkPath& path, SkPoint origin);
195 SkPath fPath;
196 SkPoint fOrigin;
197 };
198
199 const bool fIsAntiAliased;
200 const SkScalar fStrikeToSourceScale;
201 const SkSpan<const PathGlyph> fPaths;
202 const std::unique_ptr<PathGlyph[], GrSubRunAllocator::ArrayDestroyer> fPathData;
203 };
204
PathSubRun(bool isAntiAliased,SkScalar strikeToSourceScale,const GrTextBlob & blob,SkSpan<PathGlyph> paths,std::unique_ptr<PathGlyph[],GrSubRunAllocator::ArrayDestroyer> pathData)205 PathSubRun::PathSubRun(bool isAntiAliased,
206 SkScalar strikeToSourceScale,
207 const GrTextBlob& blob,
208 SkSpan<PathGlyph> paths,
209 std::unique_ptr<PathGlyph[], GrSubRunAllocator::ArrayDestroyer> pathData)
210 : fIsAntiAliased{isAntiAliased}
211 , fStrikeToSourceScale{strikeToSourceScale}
212 , fPaths{paths}
213 , fPathData{std::move(pathData)} { }
214
draw(const GrClip * clip,const SkMatrixProvider & viewMatrix,const SkGlyphRunList & glyphRunList,const SkPaint & paint,skgpu::v1::SurfaceDrawContext * sdc) const215 void PathSubRun::draw(const GrClip* clip,
216 const SkMatrixProvider& viewMatrix,
217 const SkGlyphRunList& glyphRunList,
218 const SkPaint& paint,
219 skgpu::v1::SurfaceDrawContext* sdc) const {
220 SkASSERT(!fPaths.empty());
221 SkPoint drawOrigin = glyphRunList.origin();
222 SkPaint runPaint{paint};
223 runPaint.setAntiAlias(fIsAntiAliased);
224 // If there are shaders, blurs or styles, the path must be scaled into source
225 // space independently of the CTM. This allows the CTM to be correct for the
226 // different effects.
227 GrStyle style(runPaint);
228
229 bool needsExactCTM = runPaint.getShader()
230 || style.applies()
231 || runPaint.getMaskFilter();
232
233 // Calculate the matrix that maps the path glyphs from their size in the strike to
234 // the graphics source space.
235 SkMatrix strikeToSource = SkMatrix::Scale(fStrikeToSourceScale, fStrikeToSourceScale);
236 strikeToSource.postTranslate(drawOrigin.x(), drawOrigin.y());
237 if (!needsExactCTM) {
238 for (const auto& pathPos : fPaths) {
239 const SkPath& path = pathPos.fPath;
240 const SkPoint pos = pathPos.fOrigin; // Transform the glyph to source space.
241 SkMatrix pathMatrix = strikeToSource;
242 pathMatrix.postTranslate(pos.x(), pos.y());
243 SkPreConcatMatrixProvider strikeToDevice(viewMatrix, pathMatrix);
244
245 GrStyledShape shape(path, paint);
246 GrBlurUtils::drawShapeWithMaskFilter(
247 sdc->recordingContext(), sdc, clip, runPaint, strikeToDevice, shape);
248 }
249 } else {
250 // Transform the path to device because the deviceMatrix must be unchanged to
251 // draw effect, filter or shader paths.
252 for (const auto& pathPos : fPaths) {
253 const SkPath& path = pathPos.fPath;
254 const SkPoint pos = pathPos.fOrigin;
255 // Transform the glyph to source space.
256 SkMatrix pathMatrix = strikeToSource;
257 pathMatrix.postTranslate(pos.x(), pos.y());
258
259 SkPath deviceOutline;
260 path.transform(pathMatrix, &deviceOutline);
261 deviceOutline.setIsVolatile(true);
262 GrStyledShape shape(deviceOutline, paint);
263 GrBlurUtils::drawShapeWithMaskFilter(sdc->recordingContext(), sdc, clip, runPaint,
264 viewMatrix, shape);
265 }
266 }
267 }
268
canReuse(const SkPaint & paint,const SkMatrix & drawMatrix) const269 bool PathSubRun::canReuse(const SkPaint& paint, const SkMatrix& drawMatrix) const {
270 return true;
271 }
272
Make(const SkZip<SkGlyphVariant,SkPoint> & drawables,bool isAntiAliased,SkScalar strikeToSourceScale,const GrTextBlob & blob,GrSubRunAllocator * alloc)273 GrSubRunOwner PathSubRun::Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
274 bool isAntiAliased,
275 SkScalar strikeToSourceScale,
276 const GrTextBlob& blob,
277 GrSubRunAllocator* alloc) {
278 auto pathData = alloc->makeUniqueArray<PathGlyph>(
279 drawables.size(),
280 [&](int i){
281 auto [variant, pos] = drawables[i];
282 return PathGlyph{*variant.path(), pos};
283 });
284 SkSpan<PathGlyph> paths{pathData.get(), drawables.size()};
285
286 return alloc->makeUnique<PathSubRun>(
287 isAntiAliased, strikeToSourceScale, blob, paths, std::move(pathData));
288 }
289
testingOnly_atlasSubRun()290 GrAtlasSubRun* PathSubRun::testingOnly_atlasSubRun() {
291 return nullptr;
292 };
293
294 // -- PathSubRun::PathGlyph ------------------------------------------------------------------------
PathGlyph(const SkPath & path,SkPoint origin)295 PathSubRun::PathGlyph::PathGlyph(const SkPath& path, SkPoint origin)
296 : fPath(path)
297 , fOrigin(origin) {}
298
299 // -- GlyphVector ----------------------------------------------------------------------------------
300 // GlyphVector provides a way to delay the lookup of GrGlyphs until the code is running on the
301 // GPU in single threaded mode. The GlyphVector is created in a multi-threaded environment, but
302 // the GrStrikeCache is only single threaded (and must be single threaded because of the atlas).
303 class GlyphVector {
304 public:
305 union Variant {
306 // Initially, filled with packed id, but changed to GrGlyph* in the onPrepare stage.
307 SkPackedGlyphID packedGlyphID;
308 GrGlyph* grGlyph;
309 // Add ctors to help SkArenaAlloc create arrays.
Variant()310 Variant() : grGlyph{nullptr} {}
Variant(SkPackedGlyphID id)311 Variant(SkPackedGlyphID id) : packedGlyphID{id} {}
312 };
313
314 GlyphVector(sk_sp<SkStrike>&& strike, SkSpan<Variant> glyphs);
315
316 static GlyphVector Make(
317 sk_sp<SkStrike>&& strike, SkSpan<SkGlyphVariant> glyphs, GrSubRunAllocator* alloc);
318 SkSpan<const GrGlyph*> glyphs() const;
319
320 void packedGlyphIDToGrGlyph(GrStrikeCache* cache);
321
322 std::tuple<bool, int> regenerateAtlas(
323 int begin, int end,
324 GrMaskFormat maskFormat,
325 int srcPadding,
326 GrMeshDrawTarget *,
327 bool bilerpPadding = false);
328
GlyphVectorSize(size_t count)329 static size_t GlyphVectorSize(size_t count) {
330 return sizeof(Variant) * count;
331 }
332
333 private:
334 sk_sp<SkStrike> fStrike;
335 SkSpan<Variant> fGlyphs;
336 sk_sp<GrTextStrike> fGrStrike{nullptr};
337 uint64_t fAtlasGeneration{GrDrawOpAtlas::kInvalidAtlasGeneration};
338 GrDrawOpAtlas::BulkUseTokenUpdater fBulkUseToken;
339 };
340
GlyphVector(sk_sp<SkStrike> && strike,SkSpan<Variant> glyphs)341 GlyphVector::GlyphVector(sk_sp<SkStrike>&& strike, SkSpan<Variant> glyphs)
342 : fStrike{std::move(strike)}
343 , fGlyphs{glyphs} {
344 SkASSERT(fStrike != nullptr);
345 }
346
Make(sk_sp<SkStrike> && strike,SkSpan<SkGlyphVariant> glyphs,GrSubRunAllocator * alloc)347 GlyphVector GlyphVector::Make(
348 sk_sp<SkStrike>&& strike, SkSpan<SkGlyphVariant> glyphs, GrSubRunAllocator* alloc) {
349 Variant* variants = alloc->makePODArray<Variant>(glyphs.size());
350 for (auto [i, gv] : SkMakeEnumerate(glyphs)) {
351 variants[i] = gv.glyph()->getPackedID();
352 }
353
354 return GlyphVector{std::move(strike), SkMakeSpan(variants, glyphs.size())};
355 }
356
glyphs() const357 SkSpan<const GrGlyph*> GlyphVector::glyphs() const {
358 return SkMakeSpan(reinterpret_cast<const GrGlyph**>(fGlyphs.data()), fGlyphs.size());
359 }
360
361 // packedGlyphIDToGrGlyph must be run in single-threaded mode.
362 // If fStrike != nullptr then the conversion to GrGlyph* has not happened.
packedGlyphIDToGrGlyph(GrStrikeCache * cache)363 void GlyphVector::packedGlyphIDToGrGlyph(GrStrikeCache* cache) {
364 if (fStrike != nullptr) {
365 fGrStrike = cache->findOrCreateStrike(fStrike->strikeSpec());
366
367 for (auto& variant : fGlyphs) {
368 variant.grGlyph = fGrStrike->getGlyph(variant.packedGlyphID);
369 }
370
371 // Drop the ref on the strike that was taken in the SkGlyphRunPainter process* methods.
372 fStrike = nullptr;
373 }
374 }
375
regenerateAtlas(int begin,int end,GrMaskFormat maskFormat,int srcPadding,GrMeshDrawTarget * target,bool bilerpPadding)376 std::tuple<bool, int> GlyphVector::regenerateAtlas(int begin, int end,
377 GrMaskFormat maskFormat,
378 int srcPadding,
379 GrMeshDrawTarget* target,
380 bool bilerpPadding) {
381 GrAtlasManager* atlasManager = target->atlasManager();
382 GrDeferredUploadTarget* uploadTarget = target->deferredUploadTarget();
383
384 uint64_t currentAtlasGen = atlasManager->atlasGeneration(maskFormat);
385
386 this->packedGlyphIDToGrGlyph(target->strikeCache());
387
388 if (fAtlasGeneration != currentAtlasGen) {
389 // Calculate the texture coordinates for the vertexes during first use (fAtlasGeneration
390 // is set to kInvalidAtlasGeneration) or the atlas has changed in subsequent calls..
391 fBulkUseToken.reset();
392
393 SkBulkGlyphMetricsAndImages metricsAndImages{fGrStrike->strikeSpec()};
394
395 // Update the atlas information in the GrStrike.
396 auto tokenTracker = uploadTarget->tokenTracker();
397 auto glyphs = fGlyphs.subspan(begin, end - begin);
398 int glyphsPlacedInAtlas = 0;
399 bool success = true;
400 for (const Variant& variant : glyphs) {
401 GrGlyph* grGlyph = variant.grGlyph;
402 SkASSERT(grGlyph != nullptr);
403
404 if (!atlasManager->hasGlyph(maskFormat, grGlyph)) {
405 #if defined(SK_ENABLE_SMALL_PAGE) || defined(SK_DEBUG_ATLAS_HIT_RATE)
406 atlasManager->incAtlasMissCount();
407 #endif
408 const SkGlyph& skGlyph = *metricsAndImages.glyph(grGlyph->fPackedID);
409 auto code = atlasManager->addGlyphToAtlas(
410 skGlyph, grGlyph, srcPadding, target->resourceProvider(),
411 uploadTarget, bilerpPadding);
412 if (code != GrDrawOpAtlas::ErrorCode::kSucceeded) {
413 success = code != GrDrawOpAtlas::ErrorCode::kError;
414 break;
415 }
416 }
417 #if defined(SK_ENABLE_SMALL_PAGE) || defined(SK_DEBUG_ATLAS_HIT_RATE)
418 else {
419 atlasManager->incAtlasHitCount();
420 }
421 #endif
422 atlasManager->addGlyphToBulkAndSetUseToken(
423 &fBulkUseToken, maskFormat, grGlyph,
424 tokenTracker->nextDrawToken());
425 glyphsPlacedInAtlas++;
426 }
427
428 // Update atlas generation if there are no more glyphs to put in the atlas.
429 if (success && begin + glyphsPlacedInAtlas == SkCount(fGlyphs)) {
430 // Need to get the freshest value of the atlas' generation because
431 // updateTextureCoordinates may have changed it.
432 fAtlasGeneration = atlasManager->atlasGeneration(maskFormat);
433 }
434
435 return {success, glyphsPlacedInAtlas};
436 } else {
437 // The atlas hasn't changed, so our texture coordinates are still valid.
438 if (end == SkCount(fGlyphs)) {
439 // The atlas hasn't changed and the texture coordinates are all still valid. Update
440 // all the plots used to the new use token.
441 atlasManager->setUseTokenBulk(fBulkUseToken,
442 uploadTarget->tokenTracker()->nextDrawToken(),
443 maskFormat);
444 }
445 return {true, end - begin};
446 }
447 }
448
449 // -- DirectMaskSubRun -----------------------------------------------------------------------------
450 class DirectMaskSubRun final : public GrSubRun, public GrAtlasSubRun {
451 public:
452 using DevicePosition = skvx::Vec<2, int16_t>;
453
454 DirectMaskSubRun(GrMaskFormat format,
455 GrTextBlob* blob,
456 const SkGlyphRect& deviceBounds,
457 SkSpan<const DevicePosition> devicePositions,
458 GlyphVector&& glyphs,
459 bool glyphsOutOfBounds);
460
461 static GrSubRunOwner Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
462 sk_sp<SkStrike>&& strike,
463 SkScalar,
464 GrMaskFormat format,
465 GrTextBlob* blob,
466 GrSubRunAllocator* alloc);
467
468 void draw(const GrClip*,
469 const SkMatrixProvider& viewMatrix,
470 const SkGlyphRunList&,
471 const SkPaint&,
472 skgpu::v1::SurfaceDrawContext*) const override;
473
474 std::tuple<const GrClip*, GrOp::Owner>
475 makeAtlasTextOp(const GrClip* clip,
476 const SkMatrixProvider& viewMatrix,
477 SkPoint drawOrigin,
478 const SkPaint& paint,
479 skgpu::v1::SurfaceDrawContext* sdc,
480 GrAtlasSubRunOwner) const override;
481
482 bool canReuse(const SkPaint& paint, const SkMatrix& drawMatrix) const override;
483
484 GrAtlasSubRun* testingOnly_atlasSubRun() override;
485
486 size_t vertexStride(const SkMatrix& drawMatrix) const override;
487
488 int glyphCount() const override;
489
490 void testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) override;
491
492 std::tuple<bool, int>
493 regenerateAtlas(int begin, int end, GrMeshDrawTarget*) const override;
494
495 void fillVertexData(void* vertexDst, int offset, int count, GrColor color,
496 const SkMatrix& positionMatrix, SkIRect clip) const override;
497 private:
498 // The rectangle that surrounds all the glyph bounding boxes in device space.
499 SkRect deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const;
500
501 const GrMaskFormat fMaskFormat;
502 GrTextBlob* const fBlob;
503
504 // The union of all the glyph bounds in device space.
505 const SkGlyphRect fGlyphDeviceBounds;
506 const SkSpan<const DevicePosition> fLeftTopDevicePos;
507 const bool fSomeGlyphsExcluded;
508
509 // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
510 // be single threaded.
511 mutable GlyphVector fGlyphs;
512 };
513
DirectMaskSubRun(GrMaskFormat format,GrTextBlob * blob,const SkGlyphRect & deviceBounds,SkSpan<const DevicePosition> devicePositions,GlyphVector && glyphs,bool glyphsOutOfBounds)514 DirectMaskSubRun::DirectMaskSubRun(GrMaskFormat format,
515 GrTextBlob* blob,
516 const SkGlyphRect& deviceBounds,
517 SkSpan<const DevicePosition> devicePositions,
518 GlyphVector&& glyphs,
519 bool glyphsOutOfBounds)
520 : fMaskFormat{format}
521 , fBlob{blob}
522 , fGlyphDeviceBounds{deviceBounds}
523 , fLeftTopDevicePos{devicePositions}
524 , fSomeGlyphsExcluded{glyphsOutOfBounds}
525 , fGlyphs{std::move(glyphs)} {}
526
Make(const SkZip<SkGlyphVariant,SkPoint> & drawables,sk_sp<SkStrike> && strike,SkScalar,GrMaskFormat format,GrTextBlob * blob,GrSubRunAllocator * alloc)527 GrSubRunOwner DirectMaskSubRun::Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
528 sk_sp<SkStrike>&& strike,
529 SkScalar,
530 GrMaskFormat format,
531 GrTextBlob* blob,
532 GrSubRunAllocator* alloc) {
533 DevicePosition* glyphLeftTop = alloc->makePODArray<DevicePosition>(drawables.size());
534 GlyphVector::Variant* glyphIDs = alloc->makePODArray<GlyphVector::Variant>(drawables.size());
535
536 // Because this is the direct case, the maximum width or height is the size that fits in the
537 // atlas. This boundary is checked below to ensure that the call to SkGlyphRect below will
538 // not overflow.
539 constexpr SkScalar kMaxPos =
540 std::numeric_limits<int16_t>::max() - SkStrikeCommon::kSkSideTooBigForAtlas;
541 SkGlyphRect runBounds = skglyph::empty_rect();
542 size_t goodPosCount = 0;
543 for (auto [variant, pos] : drawables) {
544 auto [x, y] = pos;
545 // Ensure that the .offset() call below does not overflow. And, at this point none of the
546 // rectangles are empty because they were culled before the run was created. Basically,
547 // cull all the glyphs that can't appear on the screen.
548 if (-kMaxPos < x && x < kMaxPos && -kMaxPos < y && y < kMaxPos) {
549 const SkGlyph* const skGlyph = variant;
550 const SkGlyphRect deviceBounds =
551 skGlyph->glyphRect().offset(SkScalarRoundToInt(x), SkScalarRoundToInt(y));
552 runBounds = skglyph::rect_union(runBounds, deviceBounds);
553 glyphLeftTop[goodPosCount] = deviceBounds.topLeft();
554 glyphIDs[goodPosCount].packedGlyphID = skGlyph->getPackedID();
555 goodPosCount += 1;
556 }
557 }
558
559 // Wow! no glyphs are in bounds and had non-empty bounds.
560 if (goodPosCount == 0) {
561 return nullptr;
562 }
563
564 // If some glyphs were excluded by the bounds, then this subrun can't be generally be used
565 // for other draws. Mark the subrun as not general.
566 bool glyphsExcluded = goodPosCount != drawables.size();
567 SkSpan<const DevicePosition> leftTop{glyphLeftTop, goodPosCount};
568 return alloc->makeUnique<DirectMaskSubRun>(
569 format, blob, runBounds, leftTop,
570 GlyphVector{std::move(strike), {glyphIDs, goodPosCount}}, glyphsExcluded);
571 }
572
canReuse(const SkPaint & paint,const SkMatrix & drawMatrix) const573 bool DirectMaskSubRun::canReuse(const SkPaint& paint, const SkMatrix& drawMatrix) const {
574 auto [reuse, translation] = check_integer_translate(fBlob->initialMatrix(), drawMatrix);
575
576 // If glyphs were excluded because of position bounds, then this subrun can only be reused if
577 // there is no change in position.
578 if (fSomeGlyphsExcluded) {
579 return translation.x() == 0 && translation.y() == 0;
580 }
581
582 return reuse;
583 }
584
vertexStride(const SkMatrix &) const585 size_t DirectMaskSubRun::vertexStride(const SkMatrix&) const {
586 if (fMaskFormat != kARGB_GrMaskFormat) {
587 return sizeof(Mask2DVertex);
588 } else {
589 return sizeof(ARGB2DVertex);
590 }
591 }
592
glyphCount() const593 int DirectMaskSubRun::glyphCount() const {
594 return SkCount(fGlyphs.glyphs());
595 }
596
draw(const GrClip * clip,const SkMatrixProvider & viewMatrix,const SkGlyphRunList & glyphRunList,const SkPaint & paint,skgpu::v1::SurfaceDrawContext * sdc) const597 void DirectMaskSubRun::draw(const GrClip* clip,
598 const SkMatrixProvider& viewMatrix,
599 const SkGlyphRunList& glyphRunList,
600 const SkPaint& paint,
601 skgpu::v1::SurfaceDrawContext* sdc) const{
602 auto[drawingClip, op] = this->makeAtlasTextOp(
603 clip, viewMatrix, glyphRunList.origin(), paint, sdc, nullptr);
604 if (op != nullptr) {
605 sdc->addDrawOp(drawingClip, std::move(op));
606 }
607 }
608
609 namespace {
610 enum ClipMethod {
611 kClippedOut,
612 kUnclipped,
613 kGPUClipped,
614 kGeometryClipped
615 };
616
617 std::tuple<ClipMethod, SkIRect>
calculate_clip(const GrClip * clip,SkRect deviceBounds,SkRect glyphBounds)618 calculate_clip(const GrClip* clip, SkRect deviceBounds, SkRect glyphBounds) {
619 if (clip == nullptr && !deviceBounds.intersects(glyphBounds)) {
620 return {kClippedOut, SkIRect::MakeEmpty()};
621 } else if (clip != nullptr) {
622 switch (auto result = clip->preApply(glyphBounds, GrAA::kNo); result.fEffect) {
623 case GrClip::Effect::kClippedOut:
624 return {kClippedOut, SkIRect::MakeEmpty()};
625 case GrClip::Effect::kUnclipped:
626 return {kUnclipped, SkIRect::MakeEmpty()};
627 case GrClip::Effect::kClipped: {
628 if (result.fIsRRect && result.fRRect.isRect()) {
629 SkRect r = result.fRRect.rect();
630 if (result.fAA == GrAA::kNo || GrClip::IsPixelAligned(r)) {
631 SkIRect clipRect = SkIRect::MakeEmpty();
632 // Clip geometrically during onPrepare using clipRect.
633 r.round(&clipRect);
634 if (clipRect.contains(glyphBounds)) {
635 // If fully within the clip, signal no clipping using the empty rect.
636 return {kUnclipped, SkIRect::MakeEmpty()};
637 }
638 // Use the clipRect to clip the geometry.
639 return {kGeometryClipped, clipRect};
640 }
641 // Partial pixel clipped at this point. Have the GPU handle it.
642 }
643 }
644 break;
645 }
646 }
647 return {kGPUClipped, SkIRect::MakeEmpty()};
648 }
649 } // namespace
650
651 std::tuple<const GrClip*, GrOp::Owner>
makeAtlasTextOp(const GrClip * clip,const SkMatrixProvider & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,skgpu::v1::SurfaceDrawContext * sdc,GrAtlasSubRunOwner) const652 DirectMaskSubRun::makeAtlasTextOp(const GrClip* clip,
653 const SkMatrixProvider& viewMatrix,
654 SkPoint drawOrigin,
655 const SkPaint& paint,
656 skgpu::v1::SurfaceDrawContext* sdc,
657 GrAtlasSubRunOwner) const {
658 SkASSERT(this->glyphCount() != 0);
659
660 const SkMatrix& drawMatrix = viewMatrix.localToDevice();
661
662 // We can clip geometrically using clipRect and ignore clip when an axis-aligned rectangular
663 // non-AA clip is used. If clipRect is empty, and clip is nullptr, then there is no clipping
664 // needed.
665 const SkRect subRunBounds = this->deviceRect(drawMatrix, drawOrigin);
666 const SkRect deviceBounds = SkRect::MakeWH(sdc->width(), sdc->height());
667 auto [clipMethod, clipRect] = calculate_clip(clip, deviceBounds, subRunBounds);
668
669 switch (clipMethod) {
670 case kClippedOut:
671 // Returning nullptr as op means skip this op.
672 return {nullptr, nullptr};
673 case kUnclipped:
674 case kGeometryClipped:
675 // GPU clip is not needed.
676 clip = nullptr;
677 break;
678 case kGPUClipped:
679 // Use the the GPU clip; clipRect is ignored.
680 break;
681 }
682
683 if (!clipRect.isEmpty()) { SkASSERT(clip == nullptr); }
684
685 GrPaint grPaint;
686 const SkPMColor4f drawingColor =
687 calculate_colors(sdc, paint, viewMatrix, fMaskFormat, &grPaint);
688
689 auto geometry = AtlasTextOp::Geometry::MakeForBlob(*this,
690 drawMatrix,
691 drawOrigin,
692 clipRect,
693 sk_ref_sp<GrTextBlob>(fBlob),
694 drawingColor,
695 sdc->arenaAlloc());
696
697 GrRecordingContext* const rContext = sdc->recordingContext();
698 GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext,
699 op_mask_type(fMaskFormat),
700 false,
701 this->glyphCount(),
702 subRunBounds,
703 geometry,
704 std::move(grPaint));
705
706 return {clip, std::move(op)};
707 }
708
testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache * cache)709 void DirectMaskSubRun::testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) {
710 fGlyphs.packedGlyphIDToGrGlyph(cache);
711 }
712
713 std::tuple<bool, int>
regenerateAtlas(int begin,int end,GrMeshDrawTarget * target) const714 DirectMaskSubRun::regenerateAtlas(int begin, int end, GrMeshDrawTarget* target) const {
715 return fGlyphs.regenerateAtlas(begin, end, fMaskFormat, 0, target);
716 }
717
718 // The 99% case. No clip. Non-color only.
direct_2D(SkZip<Mask2DVertex[4],const GrGlyph *,const DirectMaskSubRun::DevicePosition> quadData,GrColor color,SkIPoint integralOriginOffset)719 void direct_2D(SkZip<Mask2DVertex[4],
720 const GrGlyph*,
721 const DirectMaskSubRun::DevicePosition> quadData,
722 GrColor color,
723 SkIPoint integralOriginOffset) {
724 for (auto[quad, glyph, leftTop] : quadData) {
725 auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
726 SkScalar dl = leftTop[0] + integralOriginOffset.x(),
727 dt = leftTop[1] + integralOriginOffset.y(),
728 dr = dl + (ar - al),
729 db = dt + (ab - at);
730
731 quad[0] = {{dl, dt}, color, {al, at}}; // L,T
732 quad[1] = {{dl, db}, color, {al, ab}}; // L,B
733 quad[2] = {{dr, dt}, color, {ar, at}}; // R,T
734 quad[3] = {{dr, db}, color, {ar, ab}}; // R,B
735 }
736 }
737
738 template <typename Rect>
ltbr(const Rect & r)739 auto ltbr(const Rect& r) {
740 return std::make_tuple(r.left(), r.top(), r.right(), r.bottom());
741 }
742
743 // Handle any combination of BW or color and clip or no clip.
744 template<typename Quad, typename VertexData>
generalized_direct_2D(SkZip<Quad,const GrGlyph *,const VertexData> quadData,GrColor color,SkIPoint integralOriginOffset,SkIRect * clip=nullptr)745 void generalized_direct_2D(SkZip<Quad, const GrGlyph*, const VertexData> quadData,
746 GrColor color,
747 SkIPoint integralOriginOffset,
748 SkIRect* clip = nullptr) {
749 for (auto[quad, glyph, leftTop] : quadData) {
750 auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
751 uint16_t w = ar - al,
752 h = ab - at;
753 SkScalar l = (SkScalar)leftTop[0] + integralOriginOffset.x(),
754 t = (SkScalar)leftTop[1] + integralOriginOffset.y();
755 if (clip == nullptr) {
756 auto[dl, dt, dr, db] = SkRect::MakeLTRB(l, t, l + w, t + h);
757 quad[0] = {{dl, dt}, color, {al, at}}; // L,T
758 quad[1] = {{dl, db}, color, {al, ab}}; // L,B
759 quad[2] = {{dr, dt}, color, {ar, at}}; // R,T
760 quad[3] = {{dr, db}, color, {ar, ab}}; // R,B
761 } else {
762 SkIRect devIRect = SkIRect::MakeLTRB(l, t, l + w, t + h);
763 SkScalar dl, dt, dr, db;
764 if (!clip->containsNoEmptyCheck(devIRect)) {
765 if (SkIRect clipped; clipped.intersect(devIRect, *clip)) {
766 al += clipped.left() - devIRect.left();
767 at += clipped.top() - devIRect.top();
768 ar += clipped.right() - devIRect.right();
769 ab += clipped.bottom() - devIRect.bottom();
770 std::tie(dl, dt, dr, db) = ltbr(clipped);
771 } else {
772 // TODO: omit generating any vertex data for fully clipped glyphs ?
773 std::tie(dl, dt, dr, db) = std::make_tuple(0, 0, 0, 0);
774 std::tie(al, at, ar, ab) = std::make_tuple(0, 0, 0, 0);
775 }
776 } else {
777 std::tie(dl, dt, dr, db) = ltbr(devIRect);
778 }
779 quad[0] = {{dl, dt}, color, {al, at}}; // L,T
780 quad[1] = {{dl, db}, color, {al, ab}}; // L,B
781 quad[2] = {{dr, dt}, color, {ar, at}}; // R,T
782 quad[3] = {{dr, db}, color, {ar, ab}}; // R,B
783 }
784 }
785 }
786
fillVertexData(void * vertexDst,int offset,int count,GrColor color,const SkMatrix & positionMatrix,SkIRect clip) const787 void DirectMaskSubRun::fillVertexData(void* vertexDst, int offset, int count, GrColor color,
788 const SkMatrix& positionMatrix, SkIRect clip) const {
789 auto quadData = [&](auto dst) {
790 return SkMakeZip(dst,
791 fGlyphs.glyphs().subspan(offset, count),
792 fLeftTopDevicePos.subspan(offset, count));
793 };
794
795 SkPoint originOffset = positionMatrix.mapOrigin() - fBlob->initialMatrix().mapOrigin();
796 SkIPoint integralOriginOffset =
797 {SkScalarRoundToInt(originOffset.x()), SkScalarRoundToInt(originOffset.y())};
798
799 if (clip.isEmpty()) {
800 if (fMaskFormat != kARGB_GrMaskFormat) {
801 using Quad = Mask2DVertex[4];
802 SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
803 direct_2D(quadData((Quad*)vertexDst), color, integralOriginOffset);
804 } else {
805 using Quad = ARGB2DVertex[4];
806 SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
807 generalized_direct_2D(quadData((Quad*)vertexDst), color, integralOriginOffset);
808 }
809 } else {
810 if (fMaskFormat != kARGB_GrMaskFormat) {
811 using Quad = Mask2DVertex[4];
812 SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
813 generalized_direct_2D(quadData((Quad*)vertexDst), color, integralOriginOffset, &clip);
814 } else {
815 using Quad = ARGB2DVertex[4];
816 SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
817 generalized_direct_2D(quadData((Quad*)vertexDst), color, integralOriginOffset, &clip);
818 }
819 }
820 }
821
deviceRect(const SkMatrix & drawMatrix,SkPoint drawOrigin) const822 SkRect DirectMaskSubRun::deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const {
823 SkIRect outBounds = fGlyphDeviceBounds.iRect();
824
825 // Calculate the offset from the initial device origin to the current device origin.
826 SkVector offset = drawMatrix.mapPoint(drawOrigin) - fBlob->initialMatrix().mapOrigin();
827
828 // The offset should be integer, but make sure.
829 SkIVector iOffset = {SkScalarRoundToInt(offset.x()), SkScalarRoundToInt(offset.y())};
830
831 return SkRect::Make(outBounds.makeOffset(iOffset));
832 }
833
testingOnly_atlasSubRun()834 GrAtlasSubRun* DirectMaskSubRun::testingOnly_atlasSubRun() {
835 return this;
836 }
837
838 // -- TransformedMaskSubRun ------------------------------------------------------------------------
839 class TransformedMaskSubRun final : public GrSubRun, public GrAtlasSubRun {
840 public:
841 struct VertexData {
842 const SkPoint pos;
843 // The rectangle of the glyphs in strike space. But, for kDirectMask this also implies a
844 // device space rect.
845 GrIRect16 rect;
846 };
847
848 TransformedMaskSubRun(GrMaskFormat format,
849 GrTextBlob* blob,
850 SkScalar strikeToSourceScale,
851 const SkRect& bounds,
852 SkSpan<const VertexData> vertexData,
853 GlyphVector&& glyphs);
854
855 static GrSubRunOwner Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
856 sk_sp<SkStrike>&& strike,
857 SkScalar strikeToSourceScale,
858 GrMaskFormat format,
859 GrTextBlob* blob,
860 GrSubRunAllocator* alloc);
861
862 void draw(const GrClip*,
863 const SkMatrixProvider& viewMatrix,
864 const SkGlyphRunList&,
865 const SkPaint&,
866 skgpu::v1::SurfaceDrawContext*) const override;
867
868 std::tuple<const GrClip*, GrOp::Owner>
869 makeAtlasTextOp(const GrClip*,
870 const SkMatrixProvider& viewMatrix,
871 SkPoint drawOrigin,
872 const SkPaint&,
873 skgpu::v1::SurfaceDrawContext*,
874 GrAtlasSubRunOwner) const override;
875
876 bool canReuse(const SkPaint& paint, const SkMatrix& drawMatrix) const override;
877
878 GrAtlasSubRun* testingOnly_atlasSubRun() override;
879
880 void testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) override;
881
882 std::tuple<bool, int> regenerateAtlas(int begin, int end, GrMeshDrawTarget*) const override;
883
884 void fillVertexData(
885 void* vertexDst, int offset, int count,
886 GrColor color, const SkMatrix& positionMatrix, SkIRect clip) const override;
887
888 size_t vertexStride(const SkMatrix& drawMatrix) const override;
889 int glyphCount() const override;
890
891 private:
892 // The rectangle that surrounds all the glyph bounding boxes in device space.
893 SkRect deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const;
894
895 const GrMaskFormat fMaskFormat;
896 GrTextBlob* fBlob;
897
898 // The scale factor between the strike size, and the source size.
899 const SkScalar fStrikeToSourceScale;
900
901 // The bounds in source space. The bounds are the joined rectangles of all the glyphs.
902 const SkRect fVertexBounds;
903 const SkSpan<const VertexData> fVertexData;
904
905 // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
906 // be single threaded.
907 mutable GlyphVector fGlyphs;
908 };
909
TransformedMaskSubRun(GrMaskFormat format,GrTextBlob * blob,SkScalar strikeToSourceScale,const SkRect & bounds,SkSpan<const VertexData> vertexData,GlyphVector && glyphs)910 TransformedMaskSubRun::TransformedMaskSubRun(GrMaskFormat format,
911 GrTextBlob* blob,
912 SkScalar strikeToSourceScale,
913 const SkRect& bounds,
914 SkSpan<const VertexData> vertexData,
915 GlyphVector&& glyphs)
916 : fMaskFormat{format}
917 , fBlob{blob}
918 , fStrikeToSourceScale{strikeToSourceScale}
919 , fVertexBounds{bounds}
920 , fVertexData{vertexData}
921 , fGlyphs{std::move(glyphs)} { }
922
Make(const SkZip<SkGlyphVariant,SkPoint> & drawables,sk_sp<SkStrike> && strike,SkScalar strikeToSourceScale,GrMaskFormat format,GrTextBlob * blob,GrSubRunAllocator * alloc)923 GrSubRunOwner TransformedMaskSubRun::Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
924 sk_sp<SkStrike>&& strike,
925 SkScalar strikeToSourceScale,
926 GrMaskFormat format,
927 GrTextBlob* blob,
928 GrSubRunAllocator* alloc) {
929 SkRect bounds = SkRectPriv::MakeLargestInverted();
930
931 SkSpan<VertexData> vertexData = alloc->makePODArray<VertexData>(
932 drawables,
933 [&](auto e) {
934 auto [variant, pos] = e;
935 SkGlyph* skGlyph = variant;
936 int16_t l = skGlyph->left(),
937 t = skGlyph->top(),
938 r = l + skGlyph->width(),
939 b = t + skGlyph->height();
940 SkPoint lt = SkPoint::Make(l, t) * strikeToSourceScale + pos,
941 rb = SkPoint::Make(r, b) * strikeToSourceScale + pos;
942
943 bounds.joinPossiblyEmptyRect(SkRect::MakeLTRB(lt.x(), lt.y(), rb.x(), rb.y()));
944 return VertexData{pos, {l, t, r, b}};
945 });
946
947 return alloc->makeUnique<TransformedMaskSubRun>(
948 format, blob, strikeToSourceScale, bounds, vertexData,
949 GlyphVector::Make(std::move(strike), drawables.get<0>(), alloc));
950 }
951
draw(const GrClip * clip,const SkMatrixProvider & viewMatrix,const SkGlyphRunList & glyphRunList,const SkPaint & paint,skgpu::v1::SurfaceDrawContext * sdc) const952 void TransformedMaskSubRun::draw(const GrClip* clip,
953 const SkMatrixProvider& viewMatrix,
954 const SkGlyphRunList& glyphRunList,
955 const SkPaint& paint,
956 skgpu::v1::SurfaceDrawContext* sdc) const {
957 auto[drawingClip, op] = this->makeAtlasTextOp(
958 clip, viewMatrix, glyphRunList.origin(), paint, sdc, nullptr);
959 if (op != nullptr) {
960 sdc->addDrawOp(drawingClip, std::move(op));
961 }
962 }
963
964 std::tuple<const GrClip*, GrOp::Owner>
makeAtlasTextOp(const GrClip * clip,const SkMatrixProvider & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,skgpu::v1::SurfaceDrawContext * sdc,GrAtlasSubRunOwner) const965 TransformedMaskSubRun::makeAtlasTextOp(const GrClip* clip,
966 const SkMatrixProvider& viewMatrix,
967 SkPoint drawOrigin,
968 const SkPaint& paint,
969 skgpu::v1::SurfaceDrawContext* sdc,
970 GrAtlasSubRunOwner) const {
971 SkASSERT(this->glyphCount() != 0);
972
973 const SkMatrix& drawMatrix = viewMatrix.localToDevice();
974
975 GrPaint grPaint;
976 SkPMColor4f drawingColor = calculate_colors(sdc, paint, viewMatrix, fMaskFormat, &grPaint);
977
978 auto geometry = AtlasTextOp::Geometry::MakeForBlob(*this,
979 drawMatrix,
980 drawOrigin,
981 SkIRect::MakeEmpty(),
982 sk_ref_sp<GrTextBlob>(fBlob),
983 drawingColor,
984 sdc->arenaAlloc());
985
986 GrRecordingContext* const rContext = sdc->recordingContext();
987 GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext,
988 op_mask_type(fMaskFormat),
989 true,
990 this->glyphCount(),
991 this->deviceRect(drawMatrix, drawOrigin),
992 geometry,
993 std::move(grPaint));
994 return {clip, std::move(op)};
995 }
996
997 // If we are not scaling the cache entry to be larger, than a cache with smaller glyphs may be
998 // better.
canReuse(const SkPaint & paint,const SkMatrix & drawMatrix) const999 bool TransformedMaskSubRun::canReuse(const SkPaint& paint, const SkMatrix& drawMatrix) const {
1000 if (fBlob->initialMatrix().getMaxScale() < 1) {
1001 return false;
1002 }
1003 return true;
1004 }
1005
testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache * cache)1006 void TransformedMaskSubRun::testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) {
1007 fGlyphs.packedGlyphIDToGrGlyph(cache);
1008 }
1009
regenerateAtlas(int begin,int end,GrMeshDrawTarget * target) const1010 std::tuple<bool, int> TransformedMaskSubRun::regenerateAtlas(int begin, int end,
1011 GrMeshDrawTarget* target) const {
1012 return fGlyphs.regenerateAtlas(begin, end, fMaskFormat, 1, target, true);
1013 }
1014
fillVertexData(void * vertexDst,int offset,int count,GrColor color,const SkMatrix & positionMatrix,SkIRect clip) const1015 void TransformedMaskSubRun::fillVertexData(
1016 void* vertexDst, int offset, int count, GrColor color, const SkMatrix& positionMatrix,
1017 SkIRect clip) const {
1018 constexpr SkScalar kDstPadding = 0.f;
1019
1020 auto quadData = [&](auto dst) {
1021 return SkMakeZip(dst,
1022 fGlyphs.glyphs().subspan(offset, count),
1023 fVertexData.subspan(offset, count));
1024 };
1025
1026 if (!positionMatrix.hasPerspective()) {
1027 if (fMaskFormat == GrMaskFormat::kARGB_GrMaskFormat) {
1028 using Quad = ARGB2DVertex[4];
1029 SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
1030 fill_transformed_vertices_2D(
1031 quadData((Quad*) vertexDst),
1032 kDstPadding,
1033 fStrikeToSourceScale,
1034 color,
1035 positionMatrix);
1036 } else {
1037 using Quad = Mask2DVertex[4];
1038 SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
1039 fill_transformed_vertices_2D(
1040 quadData((Quad*) vertexDst),
1041 kDstPadding,
1042 fStrikeToSourceScale,
1043 color,
1044 positionMatrix);
1045 }
1046 } else {
1047 if (fMaskFormat == GrMaskFormat::kARGB_GrMaskFormat) {
1048 using Quad = ARGB3DVertex[4];
1049 SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
1050 fill_transformed_vertices_3D(
1051 quadData((Quad*) vertexDst),
1052 kDstPadding,
1053 fStrikeToSourceScale,
1054 color,
1055 positionMatrix);
1056 } else {
1057 using Quad = Mask3DVertex[4];
1058 SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
1059 fill_transformed_vertices_3D(
1060 quadData((Quad*) vertexDst),
1061 kDstPadding,
1062 fStrikeToSourceScale,
1063 color,
1064 positionMatrix);
1065 }
1066 }
1067 }
1068
vertexStride(const SkMatrix & drawMatrix) const1069 size_t TransformedMaskSubRun::vertexStride(const SkMatrix& drawMatrix) const {
1070 switch (fMaskFormat) {
1071 case kA8_GrMaskFormat:
1072 return drawMatrix.hasPerspective() ? sizeof(Mask3DVertex) : sizeof(Mask2DVertex);
1073 case kARGB_GrMaskFormat:
1074 return drawMatrix.hasPerspective() ? sizeof(ARGB3DVertex) : sizeof(ARGB2DVertex);
1075 default:
1076 SkASSERT(!drawMatrix.hasPerspective());
1077 return sizeof(Mask2DVertex);
1078 }
1079 SkUNREACHABLE;
1080 }
1081
glyphCount() const1082 int TransformedMaskSubRun::glyphCount() const {
1083 return SkCount(fVertexData);
1084 }
1085
deviceRect(const SkMatrix & drawMatrix,SkPoint drawOrigin) const1086 SkRect TransformedMaskSubRun::deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const {
1087 SkRect outBounds = fVertexBounds;
1088 outBounds.offset(drawOrigin);
1089 return drawMatrix.mapRect(outBounds);
1090 }
1091
testingOnly_atlasSubRun()1092 GrAtlasSubRun* TransformedMaskSubRun::testingOnly_atlasSubRun() {
1093 return this;
1094 }
1095
1096 // -- SDFTSubRun -----------------------------------------------------------------------------------
1097 class SDFTSubRun final : public GrSubRun, public GrAtlasSubRun {
1098 public:
1099 struct VertexData {
1100 const SkPoint pos;
1101 // The rectangle of the glyphs in strike space.
1102 GrIRect16 rect;
1103 };
1104
1105 SDFTSubRun(GrMaskFormat format,
1106 GrTextBlob* blob,
1107 SkScalar strikeToSource,
1108 SkRect vertexBounds,
1109 SkSpan<const VertexData> vertexData,
1110 GlyphVector&& glyphs,
1111 bool useLCDText,
1112 bool antiAliased);
1113
1114 static GrSubRunOwner Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
1115 const SkFont& runFont,
1116 sk_sp<SkStrike>&& strike,
1117 SkScalar strikeToSourceScale,
1118 GrTextBlob* blob,
1119 GrSubRunAllocator* alloc);
1120
1121 void draw(const GrClip*,
1122 const SkMatrixProvider& viewMatrix,
1123 const SkGlyphRunList&,
1124 const SkPaint&,
1125 skgpu::v1::SurfaceDrawContext*) const override;
1126
1127 std::tuple<const GrClip*, GrOp::Owner>
1128 makeAtlasTextOp(const GrClip*,
1129 const SkMatrixProvider& viewMatrix,
1130 SkPoint drawOrigin,
1131 const SkPaint&,
1132 skgpu::v1::SurfaceDrawContext*,
1133 GrAtlasSubRunOwner) const override;
1134
1135 bool canReuse(const SkPaint& paint, const SkMatrix& drawMatrix) const override;
1136
1137 GrAtlasSubRun* testingOnly_atlasSubRun() override;
1138
1139 void testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) override;
1140
1141 std::tuple<bool, int> regenerateAtlas(int begin, int end, GrMeshDrawTarget*) const override;
1142
1143 void fillVertexData(
1144 void* vertexDst, int offset, int count,
1145 GrColor color, const SkMatrix& positionMatrix, SkIRect clip) const override;
1146
1147 size_t vertexStride(const SkMatrix& drawMatrix) const override;
1148 int glyphCount() const override;
1149
1150 private:
1151 // The rectangle that surrounds all the glyph bounding boxes in device space.
1152 SkRect deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const;
1153
1154 const GrMaskFormat fMaskFormat;
1155 GrTextBlob* fBlob;
1156
1157 // The scale factor between the strike size, and the source size.
1158 const SkScalar fStrikeToSourceScale;
1159
1160 // The bounds in source space. The bounds are the joined rectangles of all the glyphs.
1161 const SkRect fVertexBounds;
1162 const SkSpan<const VertexData> fVertexData;
1163
1164 // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
1165 // be single threaded.
1166 mutable GlyphVector fGlyphs;
1167
1168 const bool fUseLCDText;
1169 const bool fAntiAliased;
1170 };
1171
SDFTSubRun(GrMaskFormat format,GrTextBlob * textBlob,SkScalar strikeToSource,SkRect vertexBounds,SkSpan<const VertexData> vertexData,GlyphVector && glyphs,bool useLCDText,bool antiAliased)1172 SDFTSubRun::SDFTSubRun(GrMaskFormat format,
1173 GrTextBlob* textBlob,
1174 SkScalar strikeToSource,
1175 SkRect vertexBounds,
1176 SkSpan<const VertexData> vertexData,
1177 GlyphVector&& glyphs,
1178 bool useLCDText,
1179 bool antiAliased)
1180 : fMaskFormat{format}
1181 , fBlob{textBlob}
1182 , fStrikeToSourceScale{strikeToSource}
1183 , fVertexBounds{vertexBounds}
1184 , fVertexData{vertexData}
1185 , fGlyphs{std::move(glyphs)}
1186 , fUseLCDText{useLCDText}
1187 , fAntiAliased{antiAliased} {}
1188
has_some_antialiasing(const SkFont & font)1189 bool has_some_antialiasing(const SkFont& font ) {
1190 SkFont::Edging edging = font.getEdging();
1191 return edging == SkFont::Edging::kAntiAlias
1192 || edging == SkFont::Edging::kSubpixelAntiAlias;
1193 }
1194
Make(const SkZip<SkGlyphVariant,SkPoint> & drawables,const SkFont & runFont,sk_sp<SkStrike> && strike,SkScalar strikeToSourceScale,GrTextBlob * blob,GrSubRunAllocator * alloc)1195 GrSubRunOwner SDFTSubRun::Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
1196 const SkFont& runFont,
1197 sk_sp<SkStrike>&& strike,
1198 SkScalar strikeToSourceScale,
1199 GrTextBlob* blob,
1200 GrSubRunAllocator* alloc) {
1201 SkRect bounds = SkRectPriv::MakeLargestInverted();
1202 auto mapper = [&](const auto& d) {
1203 auto& [variant, pos] = d;
1204 SkGlyph* skGlyph = variant;
1205 int16_t l = skGlyph->left(),
1206 t = skGlyph->top(),
1207 r = l + skGlyph->width(),
1208 b = t + skGlyph->height();
1209 SkPoint lt = SkPoint::Make(l, t) * strikeToSourceScale + pos,
1210 rb = SkPoint::Make(r, b) * strikeToSourceScale + pos;
1211
1212 bounds.joinPossiblyEmptyRect(SkRect::MakeLTRB(lt.x(), lt.y(), rb.x(), rb.y()));
1213 return VertexData{pos, {l, t, r, b}};
1214 };
1215
1216 SkSpan<VertexData> vertexData = alloc->makePODArray<VertexData>(drawables, mapper);
1217
1218 return alloc->makeUnique<SDFTSubRun>(
1219 kA8_GrMaskFormat,
1220 blob,
1221 strikeToSourceScale,
1222 bounds,
1223 vertexData,
1224 GlyphVector::Make(std::move(strike), drawables.get<0>(), alloc),
1225 runFont.getEdging() == SkFont::Edging::kSubpixelAntiAlias,
1226 has_some_antialiasing(runFont));
1227 }
1228
draw(const GrClip * clip,const SkMatrixProvider & viewMatrix,const SkGlyphRunList & glyphRunList,const SkPaint & paint,skgpu::v1::SurfaceDrawContext * sdc) const1229 void SDFTSubRun::draw(const GrClip* clip,
1230 const SkMatrixProvider& viewMatrix,
1231 const SkGlyphRunList& glyphRunList,
1232 const SkPaint& paint,
1233 skgpu::v1::SurfaceDrawContext* sdc) const {
1234 auto[drawingClip, op] = this->makeAtlasTextOp(
1235 clip, viewMatrix, glyphRunList.origin(), paint, sdc, nullptr);
1236 if (op != nullptr) {
1237 sdc->addDrawOp(drawingClip, std::move(op));
1238 }
1239 }
1240
calculate_sdf_parameters(const skgpu::v1::SurfaceDrawContext & sdc,const SkMatrix & drawMatrix,bool useLCDText,bool isAntiAliased)1241 static std::tuple<AtlasTextOp::MaskType, uint32_t, bool> calculate_sdf_parameters(
1242 const skgpu::v1::SurfaceDrawContext& sdc,
1243 const SkMatrix& drawMatrix,
1244 bool useLCDText,
1245 bool isAntiAliased) {
1246 const GrColorInfo& colorInfo = sdc.colorInfo();
1247 const SkSurfaceProps& props = sdc.surfaceProps();
1248 bool isBGR = SkPixelGeometryIsBGR(props.pixelGeometry());
1249 bool isLCD = useLCDText && SkPixelGeometryIsH(props.pixelGeometry());
1250 using MT = AtlasTextOp::MaskType;
1251 MT maskType = !isAntiAliased ? MT::kAliasedDistanceField
1252 : isLCD ? (isBGR ? MT::kLCDBGRDistanceField
1253 : MT::kLCDDistanceField)
1254 : MT::kGrayscaleDistanceField;
1255
1256 bool useGammaCorrectDistanceTable = colorInfo.isLinearlyBlended();
1257 uint32_t DFGPFlags = drawMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
1258 DFGPFlags |= drawMatrix.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0;
1259 DFGPFlags |= useGammaCorrectDistanceTable ? kGammaCorrect_DistanceFieldEffectFlag : 0;
1260 DFGPFlags |= MT::kAliasedDistanceField == maskType ? kAliased_DistanceFieldEffectFlag : 0;
1261
1262 if (isLCD) {
1263 DFGPFlags |= kUseLCD_DistanceFieldEffectFlag;
1264 DFGPFlags |= MT::kLCDBGRDistanceField == maskType ? kBGR_DistanceFieldEffectFlag : 0;
1265 }
1266 return {maskType, DFGPFlags, useGammaCorrectDistanceTable};
1267 }
1268
1269 std::tuple<const GrClip*, GrOp::Owner >
makeAtlasTextOp(const GrClip * clip,const SkMatrixProvider & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,skgpu::v1::SurfaceDrawContext * sdc,GrAtlasSubRunOwner) const1270 SDFTSubRun::makeAtlasTextOp(const GrClip* clip,
1271 const SkMatrixProvider& viewMatrix,
1272 SkPoint drawOrigin,
1273 const SkPaint& paint,
1274 skgpu::v1::SurfaceDrawContext* sdc,
1275 GrAtlasSubRunOwner) const {
1276 SkASSERT(this->glyphCount() != 0);
1277 SkASSERT(!viewMatrix.localToDevice().hasPerspective());
1278
1279 const SkMatrix& drawMatrix = viewMatrix.localToDevice();
1280
1281 GrPaint grPaint;
1282 SkPMColor4f drawingColor = calculate_colors(sdc, paint, viewMatrix, fMaskFormat, &grPaint);
1283
1284 auto [maskType, DFGPFlags, useGammaCorrectDistanceTable] =
1285 calculate_sdf_parameters(*sdc, drawMatrix, fUseLCDText, fAntiAliased);
1286
1287 auto geometry = AtlasTextOp::Geometry::MakeForBlob(*this,
1288 drawMatrix,
1289 drawOrigin,
1290 SkIRect::MakeEmpty(),
1291 sk_ref_sp<GrTextBlob>(fBlob),
1292 drawingColor,
1293 sdc->arenaAlloc());
1294
1295 GrRecordingContext* const rContext = sdc->recordingContext();
1296 GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext,
1297 maskType,
1298 true,
1299 this->glyphCount(),
1300 this->deviceRect(drawMatrix, drawOrigin),
1301 SkPaintPriv::ComputeLuminanceColor(paint),
1302 useGammaCorrectDistanceTable,
1303 DFGPFlags,
1304 geometry,
1305 std::move(grPaint));
1306
1307 return {clip, std::move(op)};
1308 }
1309
canReuse(const SkPaint & paint,const SkMatrix & drawMatrix) const1310 bool SDFTSubRun::canReuse(const SkPaint& paint, const SkMatrix& drawMatrix) const {
1311 const SkMatrix& initialMatrix = fBlob->initialMatrix();
1312
1313 // A scale outside of [blob.fMaxMinScale, blob.fMinMaxScale] would result in a different
1314 // distance field being generated, so we have to regenerate in those cases
1315 SkScalar newMaxScale = drawMatrix.getMaxScale();
1316 SkScalar oldMaxScale = initialMatrix.getMaxScale();
1317 SkScalar scaleAdjust = newMaxScale / oldMaxScale;
1318 auto [maxMinScale, minMaxScale] = fBlob->scaleBounds();
1319 if (scaleAdjust < maxMinScale || scaleAdjust > minMaxScale) {
1320 return false;
1321 }
1322 return true;
1323 }
1324
testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache * cache)1325 void SDFTSubRun::testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) {
1326 fGlyphs.packedGlyphIDToGrGlyph(cache);
1327 }
1328
regenerateAtlas(int begin,int end,GrMeshDrawTarget * target) const1329 std::tuple<bool, int> SDFTSubRun::regenerateAtlas(
1330 int begin, int end, GrMeshDrawTarget *target) const {
1331
1332 return fGlyphs.regenerateAtlas(begin, end, fMaskFormat, SK_DistanceFieldInset, target);
1333 }
1334
vertexStride(const SkMatrix & drawMatrix) const1335 size_t SDFTSubRun::vertexStride(const SkMatrix& drawMatrix) const {
1336 return sizeof(Mask2DVertex);
1337 }
1338
fillVertexData(void * vertexDst,int offset,int count,GrColor color,const SkMatrix & positionMatrix,SkIRect clip) const1339 void SDFTSubRun::fillVertexData(
1340 void *vertexDst, int offset, int count,
1341 GrColor color, const SkMatrix& positionMatrix, SkIRect clip) const {
1342
1343 using Quad = Mask2DVertex[4];
1344 SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
1345 fill_transformed_vertices_2D(
1346 SkMakeZip((Quad*)vertexDst,
1347 fGlyphs.glyphs().subspan(offset, count),
1348 fVertexData.subspan(offset, count)),
1349 SK_DistanceFieldInset,
1350 fStrikeToSourceScale,
1351 color,
1352 positionMatrix);
1353 }
1354
glyphCount() const1355 int SDFTSubRun::glyphCount() const {
1356 return SkCount(fVertexData);
1357 }
1358
deviceRect(const SkMatrix & drawMatrix,SkPoint drawOrigin) const1359 SkRect SDFTSubRun::deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const {
1360 SkRect outBounds = fVertexBounds;
1361 outBounds.offset(drawOrigin);
1362 return drawMatrix.mapRect(outBounds);
1363 }
1364
testingOnly_atlasSubRun()1365 GrAtlasSubRun* SDFTSubRun::testingOnly_atlasSubRun() {
1366 return this;
1367 }
1368 } // namespace
1369
1370 // -- GrTextBlob::Key ------------------------------------------------------------------------------
1371
compute_canonical_color(const SkPaint & paint,bool lcd)1372 static SkColor compute_canonical_color(const SkPaint& paint, bool lcd) {
1373 SkColor canonicalColor = SkPaintPriv::ComputeLuminanceColor(paint);
1374 if (lcd) {
1375 // This is the correct computation for canonicalColor, but there are tons of cases where LCD
1376 // can be modified. For now we just regenerate if any run in a textblob has LCD.
1377 // TODO figure out where all of these modifications are and see if we can incorporate that
1378 // logic at a higher level *OR* use sRGB
1379 //canonicalColor = SkMaskGamma::CanonicalColor(canonicalColor);
1380
1381 // TODO we want to figure out a way to be able to use the canonical color on LCD text,
1382 // see the note above. We pick a placeholder value for LCD text to ensure we always match
1383 // the same key
1384 return SK_ColorTRANSPARENT;
1385 } else {
1386 // A8, though can have mixed BMP text but it shouldn't matter because BMP text won't have
1387 // gamma corrected masks anyways, nor color
1388 U8CPU lum = SkComputeLuminance(SkColorGetR(canonicalColor),
1389 SkColorGetG(canonicalColor),
1390 SkColorGetB(canonicalColor));
1391 // reduce to our finite number of bits
1392 canonicalColor = SkMaskGamma::CanonicalColor(SkColorSetRGB(lum, lum, lum));
1393 }
1394 return canonicalColor;
1395 }
1396
Make(const SkGlyphRunList & glyphRunList,const SkPaint & paint,const SkSurfaceProps & surfaceProps,const GrColorInfo & colorInfo,const SkMatrix & drawMatrix,const GrSDFTControl & control)1397 auto GrTextBlob::Key::Make(const SkGlyphRunList& glyphRunList,
1398 const SkPaint& paint,
1399 const SkSurfaceProps& surfaceProps,
1400 const GrColorInfo& colorInfo,
1401 const SkMatrix& drawMatrix,
1402 const GrSDFTControl& control) -> std::tuple<bool, Key> {
1403 SkMaskFilterBase::BlurRec blurRec;
1404 // It might be worth caching these things, but its not clear at this time
1405 // TODO for animated mask filters, this will fill up our cache. We need a safeguard here
1406 const SkMaskFilter* maskFilter = paint.getMaskFilter();
1407 bool canCache = glyphRunList.canCache() &&
1408 !(paint.getPathEffect() ||
1409 (maskFilter && !as_MFB(maskFilter)->asABlur(&blurRec)));
1410
1411 // If we're doing linear blending, then we can disable the gamma hacks.
1412 // Otherwise, leave them on. In either case, we still want the contrast boost:
1413 // TODO: Can we be even smarter about mask gamma based on the dest transfer function?
1414 SkScalerContextFlags scalerContextFlags = colorInfo.isLinearlyBlended()
1415 ? SkScalerContextFlags::kBoostContrast
1416 : SkScalerContextFlags::kFakeGammaAndBoostContrast;
1417
1418 GrTextBlob::Key key;
1419 if (canCache) {
1420 bool hasLCD = glyphRunList.anyRunsLCD();
1421
1422 // We canonicalize all non-lcd draws to use kUnknown_SkPixelGeometry
1423 SkPixelGeometry pixelGeometry =
1424 hasLCD ? surfaceProps.pixelGeometry() : kUnknown_SkPixelGeometry;
1425
1426 GrColor canonicalColor = compute_canonical_color(paint, hasLCD);
1427
1428 key.fPixelGeometry = pixelGeometry;
1429 key.fUniqueID = glyphRunList.uniqueID();
1430 key.fStyle = paint.getStyle();
1431 if (key.fStyle != SkPaint::kFill_Style) {
1432 key.fFrameWidth = paint.getStrokeWidth();
1433 key.fMiterLimit = paint.getStrokeMiter();
1434 key.fJoin = paint.getStrokeJoin();
1435 }
1436 key.fHasBlur = maskFilter != nullptr;
1437 if (key.fHasBlur) {
1438 key.fBlurRec = blurRec;
1439 }
1440 key.fCanonicalColor = canonicalColor;
1441 key.fScalerContextFlags = scalerContextFlags;
1442
1443 // Calculate the set of drawing types.
1444 key.fSetOfDrawingTypes = 0;
1445 for (auto& run : glyphRunList) {
1446 key.fSetOfDrawingTypes |= control.drawingType(run.font(), paint, drawMatrix);
1447 }
1448
1449 if (key.fSetOfDrawingTypes & GrSDFTControl::kDirect) {
1450 // Store the fractional offset of the position. We know that the matrix can't be
1451 // perspective at this point.
1452 SkPoint mappedOrigin = drawMatrix.mapOrigin();
1453 key.fDrawMatrix = drawMatrix;
1454 key.fDrawMatrix.setTranslateX(
1455 mappedOrigin.x() - SkScalarFloorToScalar(mappedOrigin.x()));
1456 key.fDrawMatrix.setTranslateY(
1457 mappedOrigin.y() - SkScalarFloorToScalar(mappedOrigin.y()));
1458 } else {
1459 // For path and SDFT, the matrix doesn't matter.
1460 key.fDrawMatrix = SkMatrix::I();
1461 }
1462 }
1463
1464 return {canCache, key};
1465 }
1466
operator ==(const GrTextBlob::Key & that) const1467 bool GrTextBlob::Key::operator==(const GrTextBlob::Key& that) const {
1468 if (fUniqueID != that.fUniqueID) { return false; }
1469 if (fCanonicalColor != that.fCanonicalColor) { return false; }
1470 if (fStyle != that.fStyle) { return false; }
1471 if (fStyle != SkPaint::kFill_Style) {
1472 if (fFrameWidth != that.fFrameWidth ||
1473 fMiterLimit != that.fMiterLimit ||
1474 fJoin != that.fJoin) {
1475 return false;
1476 }
1477 }
1478 if (fPixelGeometry != that.fPixelGeometry) { return false; }
1479 if (fHasBlur != that.fHasBlur) { return false; }
1480 if (fHasBlur) {
1481 if (fBlurRec.fStyle != that.fBlurRec.fStyle || fBlurRec.fSigma != that.fBlurRec.fSigma) {
1482 return false;
1483 }
1484 }
1485 if (fScalerContextFlags != that.fScalerContextFlags) { return false; }
1486
1487 // Just punt on perspective.
1488 if (fDrawMatrix.hasPerspective()) {
1489 return false;
1490 }
1491
1492 if (fSetOfDrawingTypes != that.fSetOfDrawingTypes) {
1493 return false;
1494 }
1495
1496 if (fSetOfDrawingTypes & GrSDFTControl::kDirect) {
1497 auto [compatible, _] = check_integer_translate(fDrawMatrix, that.fDrawMatrix);
1498 return compatible;
1499 }
1500
1501 return true;
1502 }
1503
1504 // -- GrTextBlob -----------------------------------------------------------------------------------
operator delete(void * p)1505 void GrTextBlob::operator delete(void* p) { ::operator delete(p); }
operator new(size_t)1506 void* GrTextBlob::operator new(size_t) { SK_ABORT("All blobs are created by placement new."); }
operator new(size_t,void * p)1507 void* GrTextBlob::operator new(size_t, void* p) { return p; }
1508
1509 GrTextBlob::~GrTextBlob() = default;
1510
Make(const SkGlyphRunList & glyphRunList,const SkPaint & paint,const SkMatrix & drawMatrix,const GrSDFTControl & control,SkGlyphRunListPainter * painter)1511 sk_sp<GrTextBlob> GrTextBlob::Make(const SkGlyphRunList& glyphRunList,
1512 const SkPaint& paint,
1513 const SkMatrix& drawMatrix,
1514 const GrSDFTControl& control,
1515 SkGlyphRunListPainter* painter) {
1516 // The difference in alignment from the per-glyph data to the SubRun;
1517 constexpr size_t alignDiff =
1518 alignof(DirectMaskSubRun) - alignof(DirectMaskSubRun::DevicePosition);
1519 constexpr size_t vertexDataToSubRunPadding = alignDiff > 0 ? alignDiff : 0;
1520 size_t totalGlyphCount = glyphRunList.totalGlyphCount();
1521
1522 // The neededForSubRun is optimized for DirectMaskSubRun which is by far the most common case.
1523 size_t bytesNeededForSubRun = GrBagOfBytes::PlatformMinimumSizeWithOverhead(
1524 totalGlyphCount * sizeof(DirectMaskSubRun::DevicePosition)
1525 + GlyphVector::GlyphVectorSize(totalGlyphCount)
1526 + glyphRunList.runCount() * (sizeof(DirectMaskSubRun) + vertexDataToSubRunPadding),
1527 alignof(GrTextBlob));
1528
1529 size_t allocationSize = sizeof(GrTextBlob) + bytesNeededForSubRun;
1530
1531 void* allocation = ::operator new (allocationSize);
1532
1533 SkColor initialLuminance = SkPaintPriv::ComputeLuminanceColor(paint);
1534 sk_sp<GrTextBlob> blob{new (allocation)
1535 GrTextBlob(bytesNeededForSubRun, drawMatrix, initialLuminance)};
1536
1537 for (auto& glyphRun : glyphRunList) {
1538 painter->processGlyphRun(glyphRun,
1539 drawMatrix,
1540 paint,
1541 control,
1542 blob.get(),
1543 "GrTextBlob");
1544 }
1545
1546 return blob;
1547 }
1548
addKey(const Key & key)1549 void GrTextBlob::addKey(const Key& key) {
1550 fKey = key;
1551 }
1552
hasPerspective() const1553 bool GrTextBlob::hasPerspective() const { return fInitialMatrix.hasPerspective(); }
1554
canReuse(const SkPaint & paint,const SkMatrix & drawMatrix) const1555 bool GrTextBlob::canReuse(const SkPaint& paint, const SkMatrix& drawMatrix) const {
1556 // A singular matrix will create a GrTextBlob with no SubRuns, but unknown glyphs can
1557 // also cause empty runs. If there are no subRuns or some glyphs were excluded or perspective,
1558 // then regenerate when the matrices don't match.
1559 if ((fSubRunList.isEmpty() || fSomeGlyphsExcluded || hasPerspective()) &&
1560 fInitialMatrix != drawMatrix)
1561 {
1562 return false;
1563 }
1564
1565 // If we have LCD text then our canonical color will be set to transparent, in this case we have
1566 // to regenerate the blob on any color change
1567 // We use the grPaint to get any color filter effects
1568 if (fKey.fCanonicalColor == SK_ColorTRANSPARENT &&
1569 fInitialLuminance != SkPaintPriv::ComputeLuminanceColor(paint)) {
1570 return false;
1571 }
1572
1573 for (const GrSubRun& subRun : fSubRunList) {
1574 if (!subRun.canReuse(paint, drawMatrix)) {
1575 return false;
1576 }
1577 }
1578
1579 return true;
1580 }
1581
key() const1582 const GrTextBlob::Key& GrTextBlob::key() const { return fKey; }
size() const1583 size_t GrTextBlob::size() const { return fSize; }
1584
1585 template<typename AddSingleMaskFormat>
addMultiMaskFormat(AddSingleMaskFormat addSingle,const SkZip<SkGlyphVariant,SkPoint> & drawables,sk_sp<SkStrike> && strike,SkScalar strikeToSourceScale)1586 void GrTextBlob::addMultiMaskFormat(
1587 AddSingleMaskFormat addSingle,
1588 const SkZip<SkGlyphVariant, SkPoint>& drawables,
1589 sk_sp<SkStrike>&& strike,
1590 SkScalar strikeToSourceScale) {
1591 if (drawables.empty()) { return; }
1592
1593 auto addSameFormat = [&](const SkZip<SkGlyphVariant, SkPoint>& drawable,
1594 GrMaskFormat format,
1595 sk_sp<SkStrike>&& runStrike) {
1596 GrSubRunOwner subRun = addSingle(
1597 drawable, std::move(runStrike), strikeToSourceScale, format, this, &fAlloc);
1598 if (subRun != nullptr) {
1599 fSubRunList.append(std::move(subRun));
1600 } else {
1601 fSomeGlyphsExcluded = true;
1602 }
1603 };
1604
1605 auto glyphSpan = drawables.get<0>();
1606 SkGlyph* glyph = glyphSpan[0];
1607 GrMaskFormat format = GrGlyph::FormatFromSkGlyph(glyph->maskFormat());
1608 size_t startIndex = 0;
1609 for (size_t i = 1; i < drawables.size(); i++) {
1610 glyph = glyphSpan[i];
1611 GrMaskFormat nextFormat = GrGlyph::FormatFromSkGlyph(glyph->maskFormat());
1612 if (format != nextFormat) {
1613 auto sameFormat = drawables.subspan(startIndex, i - startIndex);
1614 // Take a ref on the strike. This should rarely happen.
1615 addSameFormat(sameFormat, format, sk_sp<SkStrike>(strike));
1616 format = nextFormat;
1617 startIndex = i;
1618 }
1619 }
1620 auto sameFormat = drawables.last(drawables.size() - startIndex);
1621 addSameFormat(sameFormat, format, std::move(strike));
1622 }
1623
GrTextBlob(int allocSize,const SkMatrix & drawMatrix,SkColor initialLuminance)1624 GrTextBlob::GrTextBlob(int allocSize,
1625 const SkMatrix& drawMatrix,
1626 SkColor initialLuminance)
1627 : fAlloc{SkTAddOffset<char>(this, sizeof(GrTextBlob)), allocSize, allocSize/2}
1628 , fSize{allocSize}
1629 , fInitialMatrix{drawMatrix}
1630 , fInitialLuminance{initialLuminance} { }
1631
processDeviceMasks(const SkZip<SkGlyphVariant,SkPoint> & drawables,sk_sp<SkStrike> && strike)1632 void GrTextBlob::processDeviceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
1633 sk_sp<SkStrike>&& strike) {
1634 SkASSERT(strike != nullptr);
1635 this->addMultiMaskFormat(DirectMaskSubRun::Make, drawables, std::move(strike), 1);
1636 }
1637
processSourcePaths(const SkZip<SkGlyphVariant,SkPoint> & drawables,const SkFont & runFont,SkScalar strikeToSourceScale)1638 void GrTextBlob::processSourcePaths(const SkZip<SkGlyphVariant, SkPoint>& drawables,
1639 const SkFont& runFont,
1640 SkScalar strikeToSourceScale) {
1641 fSubRunList.append(PathSubRun::Make(drawables,
1642 has_some_antialiasing(runFont),
1643 strikeToSourceScale,
1644 *this,
1645 &fAlloc));
1646 }
1647
processSourceSDFT(const SkZip<SkGlyphVariant,SkPoint> & drawables,sk_sp<SkStrike> && strike,SkScalar strikeToSourceScale,const SkFont & runFont,SkScalar minScale,SkScalar maxScale)1648 void GrTextBlob::processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawables,
1649 sk_sp<SkStrike>&& strike,
1650 SkScalar strikeToSourceScale,
1651 const SkFont& runFont,
1652 SkScalar minScale,
1653 SkScalar maxScale) {
1654
1655 fMaxMinScale = std::max(minScale, fMaxMinScale);
1656 fMinMaxScale = std::min(maxScale, fMinMaxScale);
1657 fSubRunList.append(
1658 SDFTSubRun::Make(drawables, runFont, std::move(strike), strikeToSourceScale,this, &fAlloc));
1659 }
1660
processSourceMasks(const SkZip<SkGlyphVariant,SkPoint> & drawables,sk_sp<SkStrike> && strike,SkScalar strikeToSourceScale)1661 void GrTextBlob::processSourceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
1662 sk_sp<SkStrike>&& strike,
1663 SkScalar strikeToSourceScale) {
1664 this->addMultiMaskFormat(
1665 TransformedMaskSubRun::Make, drawables, std::move(strike), strikeToSourceScale);
1666 }
1667
1668 // ----------------------------- Begin no cache implementation -------------------------------------
1669 namespace {
1670 // -- DirectMaskSubRunNoCache ----------------------------------------------------------------------
1671 class DirectMaskSubRunNoCache final : public GrAtlasSubRun {
1672 public:
1673 using DevicePosition = skvx::Vec<2, int16_t>;
1674
1675 DirectMaskSubRunNoCache(GrMaskFormat format,
1676 const SkRect& bounds,
1677 SkSpan<const DevicePosition> devicePositions,
1678 GlyphVector&& glyphs);
1679
1680 static GrAtlasSubRunOwner Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
1681 sk_sp<SkStrike>&& strike,
1682 GrMaskFormat format,
1683 GrSubRunAllocator* alloc);
1684
1685 size_t vertexStride(const SkMatrix& drawMatrix) const override;
1686
1687 int glyphCount() const override;
1688
1689 std::tuple<const GrClip*, GrOp::Owner>
1690 makeAtlasTextOp(const GrClip*,
1691 const SkMatrixProvider& viewMatrix,
1692 SkPoint,
1693 const SkPaint&,
1694 skgpu::v1::SurfaceDrawContext*,
1695 GrAtlasSubRunOwner) const override;
1696
1697 void testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) override;
1698
1699 std::tuple<bool, int>
1700 regenerateAtlas(int begin, int end, GrMeshDrawTarget*) const override;
1701
1702 void fillVertexData(void* vertexDst, int offset, int count, GrColor color,
1703 const SkMatrix& positionMatrix, SkIRect clip) const override;
1704
1705 private:
1706 const GrMaskFormat fMaskFormat;
1707
1708 // The vertex bounds in device space. The bounds are the joined rectangles of all the glyphs.
1709 const SkRect fGlyphDeviceBounds;
1710 const SkSpan<const DevicePosition> fLeftTopDevicePos;
1711
1712 // Space for geometry
1713 alignas(alignof(AtlasTextOp::Geometry)) char fGeom[sizeof(AtlasTextOp::Geometry)];
1714
1715 // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
1716 // be single threaded.
1717 mutable GlyphVector fGlyphs;
1718 };
1719
DirectMaskSubRunNoCache(GrMaskFormat format,const SkRect & deviceBounds,SkSpan<const DevicePosition> devicePositions,GlyphVector && glyphs)1720 DirectMaskSubRunNoCache::DirectMaskSubRunNoCache(GrMaskFormat format,
1721 const SkRect& deviceBounds,
1722 SkSpan<const DevicePosition> devicePositions,
1723 GlyphVector&& glyphs)
1724 : fMaskFormat{format}
1725 , fGlyphDeviceBounds{deviceBounds}
1726 , fLeftTopDevicePos{devicePositions}
1727 , fGlyphs{std::move(glyphs)} { }
1728
Make(const SkZip<SkGlyphVariant,SkPoint> & drawables,sk_sp<SkStrike> && strike,GrMaskFormat format,GrSubRunAllocator * alloc)1729 GrAtlasSubRunOwner DirectMaskSubRunNoCache::Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
1730 sk_sp<SkStrike>&& strike,
1731 GrMaskFormat format,
1732 GrSubRunAllocator* alloc) {
1733 DevicePosition* glyphLeftTop = alloc->makePODArray<DevicePosition>(drawables.size());
1734
1735 GlyphVector::Variant* glyphIDs = static_cast<GlyphVector::Variant*>(
1736 alloc->alignedBytes(drawables.size() * sizeof(GlyphVector::Variant),
1737 alignof(GlyphVector::Variant)));
1738
1739 // Because this is the direct case, the maximum width or height is the size that fits in the
1740 // atlas. This boundary is checked below to ensure that the call to SkGlyphRect below will
1741 // not overflow.
1742 constexpr SkScalar kMaxPos =
1743 std::numeric_limits<int16_t>::max() - SkStrikeCommon::kSkSideTooBigForAtlas;
1744 SkGlyphRect runBounds = skglyph::empty_rect();
1745 size_t goodPosCount = 0;
1746 for (auto [variant, pos] : drawables) {
1747 auto [x, y] = pos;
1748 // Ensure that the .offset() call below does not overflow. And, at this point none of the
1749 // rectangles are empty because they were culled before the run was created. Basically,
1750 // cull all the glyphs that can't appear on the screen.
1751 if (-kMaxPos < x && x < kMaxPos && -kMaxPos < y && y < kMaxPos) {
1752 const SkGlyph* const skGlyph = variant;
1753 const SkGlyphRect deviceBounds =
1754 skGlyph->glyphRect().offset(SkScalarRoundToInt(x), SkScalarRoundToInt(y));
1755 runBounds = skglyph::rect_union(runBounds, deviceBounds);
1756 glyphLeftTop[goodPosCount] = deviceBounds.topLeft();
1757 glyphIDs[goodPosCount].packedGlyphID = skGlyph->getPackedID();
1758 goodPosCount += 1;
1759 }
1760 }
1761
1762 // Wow! no glyphs are in bounds and had non-empty bounds.
1763 if (goodPosCount == 0) {
1764 return nullptr;
1765 }
1766
1767 SkSpan<const DevicePosition> leftTop{glyphLeftTop, goodPosCount};
1768 return alloc->makeUnique<DirectMaskSubRunNoCache>(
1769 format, runBounds.rect(), leftTop,
1770 GlyphVector{std::move(strike), {glyphIDs, goodPosCount}});
1771 }
1772
vertexStride(const SkMatrix &) const1773 size_t DirectMaskSubRunNoCache::vertexStride(const SkMatrix&) const {
1774 if (fMaskFormat != kARGB_GrMaskFormat) {
1775 return sizeof(Mask2DVertex);
1776 } else {
1777 return sizeof(ARGB2DVertex);
1778 }
1779 }
1780
glyphCount() const1781 int DirectMaskSubRunNoCache::glyphCount() const {
1782 return SkCount(fGlyphs.glyphs());
1783 }
1784
1785 std::tuple<const GrClip*, GrOp::Owner>
makeAtlasTextOp(const GrClip * clip,const SkMatrixProvider & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,skgpu::v1::SurfaceDrawContext * sdc,GrAtlasSubRunOwner subRunOwner) const1786 DirectMaskSubRunNoCache::makeAtlasTextOp(const GrClip* clip,
1787 const SkMatrixProvider& viewMatrix,
1788 SkPoint drawOrigin,
1789 const SkPaint& paint,
1790 skgpu::v1::SurfaceDrawContext* sdc,
1791 GrAtlasSubRunOwner subRunOwner) const {
1792 SkASSERT(this->glyphCount() != 0);
1793
1794 const SkMatrix& drawMatrix = viewMatrix.localToDevice();
1795
1796 // We can clip geometrically using clipRect and ignore clip when an axis-aligned rectangular
1797 // non-AA clip is used. If clipRect is empty, and clip is nullptr, then there is no clipping
1798 // needed.
1799 const SkRect deviceBounds = SkRect::MakeWH(sdc->width(), sdc->height());
1800 auto [clipMethod, clipRect] = calculate_clip(clip, deviceBounds, fGlyphDeviceBounds);
1801
1802 switch (clipMethod) {
1803 case kClippedOut:
1804 // Returning nullptr as op means skip this op.
1805 return {nullptr, nullptr};
1806 case kUnclipped:
1807 case kGeometryClipped:
1808 // GPU clip is not needed.
1809 clip = nullptr;
1810 break;
1811 case kGPUClipped:
1812 // Use the the GPU clip; clipRect is ignored.
1813 break;
1814 }
1815
1816 if (!clipRect.isEmpty()) { SkASSERT(clip == nullptr); }
1817
1818 GrPaint grPaint;
1819 const SkPMColor4f drawingColor =
1820 calculate_colors(sdc, paint, viewMatrix, fMaskFormat, &grPaint);
1821
1822 GrRecordingContext* const rContext = sdc->recordingContext();
1823
1824 auto geometry = new ((void*)fGeom) AtlasTextOp::Geometry{
1825 *this,
1826 drawMatrix,
1827 drawOrigin,
1828 clipRect,
1829 nullptr,
1830 std::move(subRunOwner),
1831 drawingColor
1832 };
1833
1834 GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext,
1835 op_mask_type(fMaskFormat),
1836 false,
1837 this->glyphCount(),
1838 fGlyphDeviceBounds,
1839 geometry,
1840 std::move(grPaint));
1841
1842 return {clip, std::move(op)};
1843 }
1844
testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache * cache)1845 void DirectMaskSubRunNoCache::testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) {
1846 fGlyphs.packedGlyphIDToGrGlyph(cache);
1847 }
1848
1849 std::tuple<bool, int>
regenerateAtlas(int begin,int end,GrMeshDrawTarget * target) const1850 DirectMaskSubRunNoCache::regenerateAtlas(int begin, int end, GrMeshDrawTarget* target) const {
1851 return fGlyphs.regenerateAtlas(begin, end, fMaskFormat, 0, target);
1852 }
1853
1854 // The 99% case. No clip. Non-color only.
direct_2D2(SkZip<Mask2DVertex[4],const GrGlyph *,const DirectMaskSubRunNoCache::DevicePosition> quadData,GrColor color)1855 void direct_2D2(SkZip<Mask2DVertex[4],
1856 const GrGlyph*,
1857 const DirectMaskSubRunNoCache::DevicePosition> quadData,
1858 GrColor color) {
1859 for (auto[quad, glyph, leftTop] : quadData) {
1860 auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
1861 SkScalar dl = leftTop[0],
1862 dt = leftTop[1],
1863 dr = dl + (ar - al),
1864 db = dt + (ab - at);
1865
1866 quad[0] = {{dl, dt}, color, {al, at}}; // L,T
1867 quad[1] = {{dl, db}, color, {al, ab}}; // L,B
1868 quad[2] = {{dr, dt}, color, {ar, at}}; // R,T
1869 quad[3] = {{dr, db}, color, {ar, ab}}; // R,B
1870 }
1871 }
1872
fillVertexData(void * vertexDst,int offset,int count,GrColor color,const SkMatrix & positionMatrix,SkIRect clip) const1873 void DirectMaskSubRunNoCache::fillVertexData(void* vertexDst, int offset, int count, GrColor color,
1874 const SkMatrix& positionMatrix, SkIRect clip) const {
1875 auto quadData = [&](auto dst) {
1876 return SkMakeZip(dst,
1877 fGlyphs.glyphs().subspan(offset, count),
1878 fLeftTopDevicePos.subspan(offset, count));
1879 };
1880
1881 if (clip.isEmpty()) {
1882 if (fMaskFormat != kARGB_GrMaskFormat) {
1883 using Quad = Mask2DVertex[4];
1884 SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
1885 direct_2D2(quadData((Quad*)vertexDst), color);
1886 } else {
1887 using Quad = ARGB2DVertex[4];
1888 SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
1889 generalized_direct_2D(quadData((Quad*)vertexDst), color, {0,0});
1890 }
1891 } else {
1892 if (fMaskFormat != kARGB_GrMaskFormat) {
1893 using Quad = Mask2DVertex[4];
1894 SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
1895 generalized_direct_2D(quadData((Quad*)vertexDst), color, {0,0}, &clip);
1896 } else {
1897 using Quad = ARGB2DVertex[4];
1898 SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
1899 generalized_direct_2D(quadData((Quad*)vertexDst), color, {0,0}, &clip);
1900 }
1901 }
1902 }
1903
1904 // -- TransformedMaskSubRunNoCache -----------------------------------------------------------------
1905 class TransformedMaskSubRunNoCache final : public GrAtlasSubRun {
1906 public:
1907 struct VertexData {
1908 const SkPoint pos;
1909 // The rectangle of the glyphs in strike space. But, for kDirectMask this also implies a
1910 // device space rect.
1911 GrIRect16 rect;
1912 };
1913
1914 TransformedMaskSubRunNoCache(GrMaskFormat format,
1915 SkScalar strikeToSourceScale,
1916 const SkRect& bounds,
1917 SkSpan<const VertexData> vertexData,
1918 GlyphVector&& glyphs);
1919
1920 static GrAtlasSubRunOwner Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
1921 sk_sp<SkStrike>&& strike,
1922 SkScalar strikeToSourceScale,
1923 GrMaskFormat format,
1924 GrSubRunAllocator* alloc);
1925
1926 std::tuple<const GrClip*, GrOp::Owner>
1927 makeAtlasTextOp(const GrClip*,
1928 const SkMatrixProvider& viewMatrix,
1929 SkPoint drawOrigin,
1930 const SkPaint&,
1931 skgpu::v1::SurfaceDrawContext*,
1932 GrAtlasSubRunOwner) const override;
1933
1934 void testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) override;
1935
1936 std::tuple<bool, int> regenerateAtlas(int begin, int end, GrMeshDrawTarget*) const override;
1937
1938 void fillVertexData(
1939 void* vertexDst, int offset, int count,
1940 GrColor color, const SkMatrix& positionMatrix, SkIRect clip) const override;
1941
1942 size_t vertexStride(const SkMatrix& drawMatrix) const override;
1943 int glyphCount() const override;
1944
1945 private:
1946 // The rectangle that surrounds all the glyph bounding boxes in device space.
1947 SkRect deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const;
1948
1949 const GrMaskFormat fMaskFormat;
1950
1951 // The scale factor between the strike size, and the source size.
1952 const SkScalar fStrikeToSourceScale;
1953
1954 // The bounds in source space. The bounds are the joined rectangles of all the glyphs.
1955 const SkRect fVertexBounds;
1956 const SkSpan<const VertexData> fVertexData;
1957
1958 // Space for geometry
1959 alignas(alignof(AtlasTextOp::Geometry)) char fGeom[sizeof(AtlasTextOp::Geometry)];
1960
1961 // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
1962 // be single threaded.
1963 mutable GlyphVector fGlyphs;
1964 };
1965
TransformedMaskSubRunNoCache(GrMaskFormat format,SkScalar strikeToSourceScale,const SkRect & bounds,SkSpan<const VertexData> vertexData,GlyphVector && glyphs)1966 TransformedMaskSubRunNoCache::TransformedMaskSubRunNoCache(GrMaskFormat format,
1967 SkScalar strikeToSourceScale,
1968 const SkRect& bounds,
1969 SkSpan<const VertexData> vertexData,
1970 GlyphVector&& glyphs)
1971 : fMaskFormat{format}
1972 , fStrikeToSourceScale{strikeToSourceScale}
1973 , fVertexBounds{bounds}
1974 , fVertexData{vertexData}
1975 , fGlyphs{std::move(glyphs)} {}
1976
Make(const SkZip<SkGlyphVariant,SkPoint> & drawables,sk_sp<SkStrike> && strike,SkScalar strikeToSourceScale,GrMaskFormat format,GrSubRunAllocator * alloc)1977 GrAtlasSubRunOwner TransformedMaskSubRunNoCache::Make(
1978 const SkZip<SkGlyphVariant, SkPoint>& drawables,
1979 sk_sp<SkStrike>&& strike,
1980 SkScalar strikeToSourceScale,
1981 GrMaskFormat format,
1982 GrSubRunAllocator* alloc) {
1983 SkRect bounds = SkRectPriv::MakeLargestInverted();
1984 auto initializer = [&](auto drawable) {
1985 auto [variant, pos] = drawable;
1986 SkGlyph* skGlyph = variant;
1987 int16_t l = skGlyph->left(),
1988 t = skGlyph->top(),
1989 r = l + skGlyph->width(),
1990 b = t + skGlyph->height();
1991 SkPoint lt = SkPoint::Make(l, t) * strikeToSourceScale + pos,
1992 rb = SkPoint::Make(r, b) * strikeToSourceScale + pos;
1993
1994 bounds.joinPossiblyEmptyRect(SkRect::MakeLTRB(lt.x(), lt.y(), rb.x(), rb.y()));
1995 return VertexData{pos, {l, t, r, b}};
1996 };
1997
1998 SkSpan<VertexData> vertexData = alloc->makePODArray<VertexData>(drawables, initializer);
1999
2000 return alloc->makeUnique<TransformedMaskSubRunNoCache>(
2001 format, strikeToSourceScale, bounds, vertexData,
2002 GlyphVector::Make(std::move(strike), drawables.get<0>(), alloc));
2003 }
2004
2005 std::tuple<const GrClip*, GrOp::Owner>
makeAtlasTextOp(const GrClip * clip,const SkMatrixProvider & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,skgpu::v1::SurfaceDrawContext * sdc,GrAtlasSubRunOwner subRunOwner) const2006 TransformedMaskSubRunNoCache::makeAtlasTextOp(const GrClip* clip,
2007 const SkMatrixProvider& viewMatrix,
2008 SkPoint drawOrigin,
2009 const SkPaint& paint,
2010 skgpu::v1::SurfaceDrawContext* sdc,
2011 GrAtlasSubRunOwner subRunOwner) const {
2012 SkASSERT(this->glyphCount() != 0);
2013
2014 const SkMatrix& drawMatrix = viewMatrix.localToDevice();
2015
2016 GrPaint grPaint;
2017 SkPMColor4f drawingColor = calculate_colors(sdc, paint, viewMatrix, fMaskFormat, &grPaint);
2018
2019 // We can clip geometrically using clipRect and ignore clip if we're not using SDFs or
2020 // transformed glyphs, and we have an axis-aligned rectangular non-AA clip.
2021 auto geometry = new ((void*)fGeom) AtlasTextOp::Geometry{
2022 *this,
2023 drawMatrix,
2024 drawOrigin,
2025 SkIRect::MakeEmpty(),
2026 nullptr,
2027 std::move(subRunOwner),
2028 drawingColor
2029 };
2030
2031 GrRecordingContext* rContext = sdc->recordingContext();
2032 GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext,
2033 op_mask_type(fMaskFormat),
2034 true,
2035 this->glyphCount(),
2036 this->deviceRect(drawMatrix, drawOrigin),
2037 geometry,
2038 std::move(grPaint));
2039 return {clip, std::move(op)};
2040 }
2041
testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache * cache)2042 void TransformedMaskSubRunNoCache::testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) {
2043 fGlyphs.packedGlyphIDToGrGlyph(cache);
2044 }
2045
regenerateAtlas(int begin,int end,GrMeshDrawTarget * target) const2046 std::tuple<bool, int> TransformedMaskSubRunNoCache::regenerateAtlas(
2047 int begin, int end, GrMeshDrawTarget* target) const {
2048 return fGlyphs.regenerateAtlas(begin, end, fMaskFormat, 1, target, true);
2049 }
2050
fillVertexData(void * vertexDst,int offset,int count,GrColor color,const SkMatrix & positionMatrix,SkIRect clip) const2051 void TransformedMaskSubRunNoCache::fillVertexData(
2052 void* vertexDst, int offset, int count, GrColor color,
2053 const SkMatrix& positionMatrix, SkIRect clip) const {
2054 constexpr SkScalar kDstPadding = 0.f;
2055
2056 auto quadData = [&](auto dst) {
2057 return SkMakeZip(dst,
2058 fGlyphs.glyphs().subspan(offset, count),
2059 fVertexData.subspan(offset, count));
2060 };
2061
2062 if (!positionMatrix.hasPerspective()) {
2063 if (fMaskFormat == GrMaskFormat::kARGB_GrMaskFormat) {
2064 using Quad = ARGB2DVertex[4];
2065 SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
2066 fill_transformed_vertices_2D(
2067 quadData((Quad*) vertexDst),
2068 kDstPadding,
2069 fStrikeToSourceScale,
2070 color,
2071 positionMatrix);
2072 } else {
2073 using Quad = Mask2DVertex[4];
2074 SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
2075 fill_transformed_vertices_2D(
2076 quadData((Quad*) vertexDst),
2077 kDstPadding,
2078 fStrikeToSourceScale,
2079 color,
2080 positionMatrix);
2081 }
2082 } else {
2083 if (fMaskFormat == GrMaskFormat::kARGB_GrMaskFormat) {
2084 using Quad = ARGB3DVertex[4];
2085 SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
2086 fill_transformed_vertices_3D(
2087 quadData((Quad*) vertexDst),
2088 kDstPadding,
2089 fStrikeToSourceScale,
2090 color,
2091 positionMatrix);
2092 } else {
2093 using Quad = Mask3DVertex[4];
2094 SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
2095 fill_transformed_vertices_3D(
2096 quadData((Quad*) vertexDst),
2097 kDstPadding,
2098 fStrikeToSourceScale,
2099 color,
2100 positionMatrix);
2101 }
2102 }
2103 }
2104
vertexStride(const SkMatrix & drawMatrix) const2105 size_t TransformedMaskSubRunNoCache::vertexStride(const SkMatrix& drawMatrix) const {
2106 switch (fMaskFormat) {
2107 case kA8_GrMaskFormat:
2108 return drawMatrix.hasPerspective() ? sizeof(Mask3DVertex) : sizeof(Mask2DVertex);
2109 case kARGB_GrMaskFormat:
2110 return drawMatrix.hasPerspective() ? sizeof(ARGB3DVertex) : sizeof(ARGB2DVertex);
2111 default:
2112 SkASSERT(!drawMatrix.hasPerspective());
2113 return sizeof(Mask2DVertex);
2114 }
2115 SkUNREACHABLE;
2116 }
2117
glyphCount() const2118 int TransformedMaskSubRunNoCache::glyphCount() const {
2119 return SkCount(fVertexData);
2120 }
2121
deviceRect(const SkMatrix & drawMatrix,SkPoint drawOrigin) const2122 SkRect TransformedMaskSubRunNoCache::deviceRect(
2123 const SkMatrix& drawMatrix, SkPoint drawOrigin) const {
2124 SkRect outBounds = fVertexBounds;
2125 outBounds.offset(drawOrigin);
2126 return drawMatrix.mapRect(outBounds);
2127 }
2128
2129 // -- SDFTSubRunNoCache ----------------------------------------------------------------------------
2130 class SDFTSubRunNoCache final : public GrAtlasSubRun {
2131 public:
2132 struct VertexData {
2133 const SkPoint pos;
2134 // The rectangle of the glyphs in strike space.
2135 GrIRect16 rect;
2136 };
2137
2138 SDFTSubRunNoCache(GrMaskFormat format,
2139 SkScalar strikeToSourceScale,
2140 SkRect vertexBounds,
2141 SkSpan<const VertexData> vertexData,
2142 GlyphVector&& glyphs,
2143 bool useLCDText,
2144 bool antiAliased);
2145
2146 static GrAtlasSubRunOwner Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
2147 const SkFont& runFont,
2148 sk_sp<SkStrike>&& strike,
2149 SkScalar strikeToSourceScale,
2150 GrSubRunAllocator* alloc);
2151
2152 std::tuple<const GrClip*, GrOp::Owner>
2153 makeAtlasTextOp(const GrClip*,
2154 const SkMatrixProvider& viewMatrix,
2155 SkPoint drawOrigin,
2156 const SkPaint&,
2157 skgpu::v1::SurfaceDrawContext*,
2158 GrAtlasSubRunOwner) const override;
2159
2160 void testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) override;
2161
2162 std::tuple<bool, int> regenerateAtlas(int begin, int end, GrMeshDrawTarget*) const override;
2163
2164 void fillVertexData(
2165 void* vertexDst, int offset, int count,
2166 GrColor color, const SkMatrix& positionMatrix, SkIRect clip) const override;
2167
2168 size_t vertexStride(const SkMatrix& drawMatrix) const override;
2169 int glyphCount() const override;
2170
2171 private:
2172 // The rectangle that surrounds all the glyph bounding boxes in device space.
2173 SkRect deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const;
2174
2175 const GrMaskFormat fMaskFormat;
2176
2177 // The scale factor between the strike size, and the source size.
2178 const SkScalar fStrikeToSourceScale;
2179
2180 // The bounds in source space. The bounds are the joined rectangles of all the glyphs.
2181 const SkRect fVertexBounds;
2182 const SkSpan<const VertexData> fVertexData;
2183
2184 // Space for geometry
2185 alignas(alignof(AtlasTextOp::Geometry)) char fGeom[sizeof(AtlasTextOp::Geometry)];
2186
2187 // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
2188 // be single threaded.
2189 mutable GlyphVector fGlyphs;
2190
2191 const bool fUseLCDText;
2192 const bool fAntiAliased;
2193 };
2194
SDFTSubRunNoCache(GrMaskFormat format,SkScalar strikeToSourceScale,SkRect vertexBounds,SkSpan<const VertexData> vertexData,GlyphVector && glyphs,bool useLCDText,bool antiAliased)2195 SDFTSubRunNoCache::SDFTSubRunNoCache(GrMaskFormat format,
2196 SkScalar strikeToSourceScale,
2197 SkRect vertexBounds,
2198 SkSpan<const VertexData> vertexData,
2199 GlyphVector&& glyphs,
2200 bool useLCDText,
2201 bool antiAliased)
2202 : fMaskFormat{format}
2203 , fStrikeToSourceScale{strikeToSourceScale}
2204 , fVertexBounds{vertexBounds}
2205 , fVertexData{vertexData}
2206 , fGlyphs{std::move(glyphs)}
2207 , fUseLCDText{useLCDText}
2208 , fAntiAliased{antiAliased} {}
2209
2210
Make(const SkZip<SkGlyphVariant,SkPoint> & drawables,const SkFont & runFont,sk_sp<SkStrike> && strike,SkScalar strikeToSourceScale,GrSubRunAllocator * alloc)2211 GrAtlasSubRunOwner SDFTSubRunNoCache::Make(
2212 const SkZip<SkGlyphVariant, SkPoint>& drawables,
2213 const SkFont& runFont,
2214 sk_sp<SkStrike>&& strike,
2215 SkScalar strikeToSourceScale,
2216 GrSubRunAllocator* alloc) {
2217
2218 SkRect bounds = SkRectPriv::MakeLargestInverted();
2219 auto initializer = [&](auto drawable) {
2220 auto [variant, pos] = drawable;
2221 SkGlyph* skGlyph = variant;
2222 int16_t l = skGlyph->left(),
2223 t = skGlyph->top(),
2224 r = l + skGlyph->width(),
2225 b = t + skGlyph->height();
2226 SkPoint lt = SkPoint::Make(l, t) * strikeToSourceScale + pos,
2227 rb = SkPoint::Make(r, b) * strikeToSourceScale + pos;
2228
2229 bounds.joinPossiblyEmptyRect(SkRect::MakeLTRB(lt.x(), lt.y(), rb.x(), rb.y()));
2230 return VertexData{pos, {l, t, r, b}};
2231 };
2232
2233 SkSpan<VertexData> vertexData = alloc->makePODArray<VertexData>(drawables, initializer);
2234
2235 return alloc->makeUnique<SDFTSubRunNoCache>(
2236 kA8_GrMaskFormat,
2237 strikeToSourceScale,
2238 bounds,
2239 vertexData,
2240 GlyphVector::Make(std::move(strike), drawables.get<0>(), alloc),
2241 runFont.getEdging() == SkFont::Edging::kSubpixelAntiAlias,
2242 has_some_antialiasing(runFont));
2243 }
2244
2245 std::tuple<const GrClip*, GrOp::Owner>
makeAtlasTextOp(const GrClip * clip,const SkMatrixProvider & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,skgpu::v1::SurfaceDrawContext * sdc,GrAtlasSubRunOwner subRunOwner) const2246 SDFTSubRunNoCache::makeAtlasTextOp(const GrClip* clip,
2247 const SkMatrixProvider& viewMatrix,
2248 SkPoint drawOrigin,
2249 const SkPaint& paint,
2250 skgpu::v1::SurfaceDrawContext* sdc,
2251 GrAtlasSubRunOwner subRunOwner) const {
2252 SkASSERT(this->glyphCount() != 0);
2253
2254 const SkMatrix& drawMatrix = viewMatrix.localToDevice();
2255
2256 GrPaint grPaint;
2257 SkPMColor4f drawingColor = calculate_colors(sdc, paint, viewMatrix, fMaskFormat, &grPaint);
2258
2259 auto [maskType, DFGPFlags, useGammaCorrectDistanceTable] =
2260 calculate_sdf_parameters(*sdc, drawMatrix, fUseLCDText, fAntiAliased);
2261
2262 auto geometry = new ((void*)fGeom) AtlasTextOp::Geometry {
2263 *this,
2264 drawMatrix,
2265 drawOrigin,
2266 SkIRect::MakeEmpty(),
2267 nullptr,
2268 std::move(subRunOwner),
2269 drawingColor
2270 };
2271
2272 GrRecordingContext* rContext = sdc->recordingContext();
2273 GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext,
2274 maskType,
2275 true,
2276 this->glyphCount(),
2277 this->deviceRect(drawMatrix, drawOrigin),
2278 SkPaintPriv::ComputeLuminanceColor(paint),
2279 useGammaCorrectDistanceTable,
2280 DFGPFlags,
2281 geometry,
2282 std::move(grPaint));
2283
2284 return {clip, std::move(op)};
2285 }
2286
testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache * cache)2287 void SDFTSubRunNoCache::testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) {
2288 fGlyphs.packedGlyphIDToGrGlyph(cache);
2289 }
2290
regenerateAtlas(int begin,int end,GrMeshDrawTarget * target) const2291 std::tuple<bool, int> SDFTSubRunNoCache::regenerateAtlas(
2292 int begin, int end, GrMeshDrawTarget *target) const {
2293
2294 return fGlyphs.regenerateAtlas(begin, end, fMaskFormat, SK_DistanceFieldInset, target);
2295 }
2296
vertexStride(const SkMatrix & drawMatrix) const2297 size_t SDFTSubRunNoCache::vertexStride(const SkMatrix& drawMatrix) const {
2298 return sizeof(Mask2DVertex);
2299 }
2300
fillVertexData(void * vertexDst,int offset,int count,GrColor color,const SkMatrix & positionMatrix,SkIRect clip) const2301 void SDFTSubRunNoCache::fillVertexData(
2302 void *vertexDst, int offset, int count,
2303 GrColor color, const SkMatrix& positionMatrix, SkIRect clip) const {
2304 using Quad = Mask2DVertex[4];
2305 SkASSERT(sizeof(Quad) == this->vertexStride(positionMatrix) * kVerticesPerGlyph);
2306 fill_transformed_vertices_2D(
2307 SkMakeZip((Quad*)vertexDst,
2308 fGlyphs.glyphs().subspan(offset, count),
2309 fVertexData.subspan(offset, count)),
2310 SK_DistanceFieldInset,
2311 fStrikeToSourceScale,
2312 color,
2313 positionMatrix);
2314 }
2315
glyphCount() const2316 int SDFTSubRunNoCache::glyphCount() const {
2317 return SkCount(fVertexData);
2318 }
2319
deviceRect(const SkMatrix & drawMatrix,SkPoint drawOrigin) const2320 SkRect SDFTSubRunNoCache::deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const {
2321 SkRect outBounds = fVertexBounds;
2322 outBounds.offset(drawOrigin);
2323 return drawMatrix.mapRect(outBounds);
2324 }
2325 } // namespace
2326
GrSubRunNoCachePainter(skgpu::v1::SurfaceDrawContext * sdc,GrSubRunAllocator * alloc,const GrClip * clip,const SkMatrixProvider & viewMatrix,const SkGlyphRunList & glyphRunList,const SkPaint & paint)2327 GrSubRunNoCachePainter::GrSubRunNoCachePainter(skgpu::v1::SurfaceDrawContext* sdc,
2328 GrSubRunAllocator* alloc,
2329 const GrClip* clip,
2330 const SkMatrixProvider& viewMatrix,
2331 const SkGlyphRunList& glyphRunList,
2332 const SkPaint& paint)
2333 : fSDC{sdc}
2334 , fAlloc{alloc}
2335 , fClip{clip}
2336 , fViewMatrix{viewMatrix}
2337 , fGlyphRunList{glyphRunList}
2338 , fPaint {paint} {}
2339
processDeviceMasks(const SkZip<SkGlyphVariant,SkPoint> & drawables,sk_sp<SkStrike> && strike)2340 void GrSubRunNoCachePainter::processDeviceMasks(
2341 const SkZip<SkGlyphVariant, SkPoint>& drawables, sk_sp<SkStrike>&& strike) {
2342 if (drawables.empty()) { return; }
2343
2344 auto glyphSpan = drawables.get<0>();
2345 SkGlyph* glyph = glyphSpan[0];
2346 GrMaskFormat format = GrGlyph::FormatFromSkGlyph(glyph->maskFormat());
2347 size_t startIndex = 0;
2348 for (size_t i = 1; i < drawables.size(); i++) {
2349 glyph = glyphSpan[i];
2350 GrMaskFormat nextFormat = GrGlyph::FormatFromSkGlyph(glyph->maskFormat());
2351 if (format != nextFormat) {
2352 auto sameFormat = drawables.subspan(startIndex, i - startIndex);
2353 // Take an extra ref on the strike. This should rarely happen.
2354 this->draw(
2355 DirectMaskSubRunNoCache::Make(sameFormat, sk_sp<SkStrike>(strike), format, fAlloc));
2356 format = nextFormat;
2357 startIndex = i;
2358 }
2359 }
2360 auto sameFormat = drawables.last(drawables.size() - startIndex);
2361 this->draw(DirectMaskSubRunNoCache::Make(sameFormat, std::move(strike), format, fAlloc));
2362 }
2363
processSourceMasks(const SkZip<SkGlyphVariant,SkPoint> & drawables,sk_sp<SkStrike> && strike,SkScalar strikeToSourceScale)2364 void GrSubRunNoCachePainter::processSourceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
2365 sk_sp<SkStrike>&& strike,
2366 SkScalar strikeToSourceScale) {
2367 if (drawables.empty()) {
2368 return;
2369 }
2370
2371 auto glyphSpan = drawables.get<0>();
2372 SkGlyph* glyph = glyphSpan[0];
2373 GrMaskFormat format = GrGlyph::FormatFromSkGlyph(glyph->maskFormat());
2374 size_t startIndex = 0;
2375 for (size_t i = 1; i < drawables.size(); i++) {
2376 glyph = glyphSpan[i];
2377 GrMaskFormat nextFormat = GrGlyph::FormatFromSkGlyph(glyph->maskFormat());
2378 if (format != nextFormat) {
2379 auto sameFormat = drawables.subspan(startIndex, i - startIndex);
2380 this->draw(
2381 // Add an extra ref to the strike. This should rarely happen.
2382 TransformedMaskSubRunNoCache::Make(
2383 sameFormat, sk_sp<SkStrike>(strike), strikeToSourceScale, format, fAlloc));
2384 format = nextFormat;
2385 startIndex = i;
2386 }
2387 }
2388 auto sameFormat = drawables.last(drawables.size() - startIndex);
2389 this->draw(
2390 TransformedMaskSubRunNoCache::Make(
2391 sameFormat, std::move(strike), strikeToSourceScale, format, fAlloc));
2392 }
2393
processSourcePaths(const SkZip<SkGlyphVariant,SkPoint> & drawables,const SkFont & runFont,SkScalar strikeToSourceScale)2394 void GrSubRunNoCachePainter::processSourcePaths(const SkZip<SkGlyphVariant, SkPoint>& drawables,
2395 const SkFont& runFont,
2396 SkScalar strikeToSourceScale) {
2397 SkASSERT(!drawables.empty());
2398 SkPoint drawOrigin = fGlyphRunList.origin();
2399 const SkPaint& drawPaint = fPaint;
2400 SkPaint runPaint{drawPaint};
2401 runPaint.setAntiAlias(has_some_antialiasing(runFont));
2402 // If there are shaders, blurs or styles, the path must be scaled into source
2403 // space independently of the CTM. This allows the CTM to be correct for the
2404 // different effects.
2405 GrStyle style(runPaint);
2406
2407 bool needsExactCTM = runPaint.getShader()
2408 || style.applies()
2409 || runPaint.getMaskFilter();
2410
2411 // Calculate the matrix that maps the path glyphs from their size in the strike to
2412 // the graphics source space.
2413 SkMatrix strikeToSource = SkMatrix::Scale(strikeToSourceScale, strikeToSourceScale);
2414 strikeToSource.postTranslate(drawOrigin.x(), drawOrigin.y());
2415 if (!needsExactCTM) {
2416 for (auto [variant, pos] : drawables) {
2417 const SkPath& path = *variant.path();
2418 SkMatrix pathMatrix = strikeToSource;
2419 pathMatrix.postTranslate(pos.x(), pos.y());
2420 SkPreConcatMatrixProvider strikeToDevice(fViewMatrix, pathMatrix);
2421
2422 GrStyledShape shape(path, drawPaint);
2423 GrBlurUtils::drawShapeWithMaskFilter(
2424 fSDC->recordingContext(), fSDC, fClip, runPaint,
2425 strikeToDevice, shape);
2426 }
2427 } else {
2428 // Transform the path to device space because the deviceMatrix must be unchanged to
2429 // draw effect, filter or shader paths.
2430 for (auto [variant, pos] : drawables) {
2431 const SkPath& path = *variant.path();
2432 // Transform the glyph to source space.
2433 SkMatrix pathMatrix = strikeToSource;
2434 pathMatrix.postTranslate(pos.x(), pos.y());
2435
2436 SkPath sourceSpacePath;
2437 path.transform(pathMatrix, &sourceSpacePath);
2438 sourceSpacePath.setIsVolatile(true);
2439 GrStyledShape shape(sourceSpacePath, drawPaint);
2440 GrBlurUtils::drawShapeWithMaskFilter(
2441 fSDC->recordingContext(), fSDC, fClip, runPaint, fViewMatrix, shape);
2442 }
2443 }
2444 }
2445
processSourceSDFT(const SkZip<SkGlyphVariant,SkPoint> & drawables,sk_sp<SkStrike> && strike,SkScalar strikeToSourceScale,const SkFont & runFont,SkScalar minScale,SkScalar maxScale)2446 void GrSubRunNoCachePainter::processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawables,
2447 sk_sp<SkStrike>&& strike,
2448 SkScalar strikeToSourceScale,
2449 const SkFont& runFont,
2450 SkScalar minScale, SkScalar maxScale) {
2451 if (drawables.empty()) {
2452 return;
2453 }
2454 this->draw(SDFTSubRunNoCache::Make(
2455 drawables, runFont, std::move(strike), strikeToSourceScale, fAlloc));
2456 }
2457
draw(GrAtlasSubRunOwner subRun)2458 void GrSubRunNoCachePainter::draw(GrAtlasSubRunOwner subRun) {
2459 if (subRun == nullptr) {
2460 return;
2461 }
2462 GrAtlasSubRun* subRunPtr = subRun.get();
2463 auto [drawingClip, op] = subRunPtr->makeAtlasTextOp(
2464 fClip, fViewMatrix, fGlyphRunList.origin(), fPaint, fSDC, std::move(subRun));
2465 if (op != nullptr) {
2466 fSDC->addDrawOp(drawingClip, std::move(op));
2467 }
2468 }
2469
2470