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