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