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