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 "include/private/chromium/GrSlug.h"
12 #include "include/private/chromium/SkChromeRemoteGlyphCache.h"
13 #include "src/core/SkFontPriv.h"
14 #include "src/core/SkMaskFilterBase.h"
15 #include "src/core/SkMatrixProvider.h"
16 #include "src/core/SkPaintPriv.h"
17 #include "src/core/SkReadBuffer.h"
18 #include "src/core/SkStrikeCache.h"
19 #include "src/core/SkStrikeSpec.h"
20 #include "src/gpu/GrClip.h"
21 #include "src/gpu/GrGlyph.h"
22 #include "src/gpu/GrMeshDrawTarget.h"
23 #include "src/gpu/GrRecordingContextPriv.h"
24 #include "src/gpu/GrStyle.h"
25 #include "src/gpu/SkGr.h"
26 #include "src/gpu/effects/GrDistanceFieldGeoProc.h"
27 #include "src/gpu/geometry/GrStyledShape.h"
28 #include "src/gpu/text/GrAtlasManager.h"
29 #include "src/gpu/text/GrGlyphVector.h"
30 #include "src/gpu/text/GrStrikeCache.h"
31 #include "src/gpu/text/GrTextBlob.h"
32
33 #include "src/gpu/GrBlurUtils.h"
34 #include "src/gpu/ops/AtlasTextOp.h"
35 #include "src/gpu/v1/Device_v1.h"
36 #include "src/gpu/v1/SurfaceDrawContext_v1.h"
37
38 using AtlasTextOp = skgpu::v1::AtlasTextOp;
39
40 // -- GPU Text -------------------------------------------------------------------------------------
41 // There are three broad types of SubRun implementations for drawing text using the GPU.
42 // GrTextBlob (runs with no postfix) - these runs support drawing for GrTextBlobs.
43 // GrSlug (Slug postfix) - these runs support drawing of GrSlugs.
44 // (NoCache postfix) - These runs support Canvas direct drawing like drawText, etc.
45 //
46 // Naming conventions
47 // * drawMatrix - the CTM from the canvas.
48 // * drawOrigin - the x, y location of the drawTextBlob call.
49 // * positionMatrix - this is the combination of the drawMatrix and the drawOrigin:
50 // positionMatrix = drawMatrix * TranslationMatrix(drawOrigin.x, drawOrigin.y);
51 //
52 // Note:
53 // In order to use GrSlugs, you need to set the fSupportBilerpFromGlyphAtlas on GrContextOptions.
54
55 enum GrSubRun::SubRunType : int {
56 kBad = 0, // Make this 0 to line up with errors from readInt.
57 kDirectMask,
58 kSDFT,
59 kTransformMask,
60 kPath,
61 kDrawable,
62 kSubRunTypeCount,
63 };
64
65 // -- GrBlobSubRun ---------------------------------------------------------------------------------
66 class GrBlobSubRun {
67 public:
68 virtual ~GrBlobSubRun() = default;
69 // Given an already cached subRun, can this subRun handle this combination paint, matrix, and
70 // position.
71 virtual bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const = 0;
72
73 // Return the underlying atlas SubRun if it exists. Otherwise, return nullptr.
74 // * Don't use this API. It is only to support testing.
75 virtual const GrAtlasSubRun* testingOnly_atlasSubRun() const = 0;
76 };
77
78 // -- GrSubRun -------------------------------------------------------------------------------------
79 GrSubRun::~GrSubRun() = default;
blobCast() const80 const GrBlobSubRun* GrSubRun::blobCast() const {
81 SK_ABORT("This is not a subclass of GrBlobSubRun.");
82 }
83
84 namespace {
85 // -- TransformedMaskVertexFiller ------------------------------------------------------------------
86 class TransformedMaskVertexFiller {
87 public:
88 TransformedMaskVertexFiller(GrMaskFormat maskFormat,
89 int dstPadding,
90 SkScalar strikeToSourceScale);
91
92 struct PositionAndExtent {
93 const SkPoint pos;
94 // The rectangle of the glyphs in strike space. But, for kDirectMask this also implies a
95 // device space rect.
96 GrIRect16 rect;
97 };
98
vertexStride(const SkMatrix & matrix) const99 size_t vertexStride(const SkMatrix& matrix) const {
100 if (fMaskType != kARGB_GrMaskFormat) {
101 // For formats kA565_GrMaskFormat and kA8_GrMaskFormat where A8 include SDFT.
102 return matrix.hasPerspective() ? sizeof(Mask3DVertex) : sizeof(Mask2DVertex);
103 } else {
104 // For format kARGB_GrMaskFormat
105 return matrix.hasPerspective() ? sizeof(ARGB3DVertex) : sizeof(ARGB2DVertex);
106 }
107 }
108
109 void fillVertexData(SkSpan<const GrGlyph*> glyphs,
110 SkSpan<const PositionAndExtent> positioning,
111 GrColor color,
112 const SkMatrix& positionMatrix,
113 SkIRect clip,
114 void* vertexBuffer) const;
115
116 AtlasTextOp::MaskType opMaskType() const;
grMaskType() const117 GrMaskFormat grMaskType() const {return fMaskType;}
118
119 private:
120 struct AtlasPt {
121 uint16_t u;
122 uint16_t v;
123 };
124
125 // Normal text mask, SDFT, or color.
126 struct Mask2DVertex {
127 SkPoint devicePos;
128 GrColor color;
129 AtlasPt atlasPos;
130 };
131
132 struct ARGB2DVertex {
ARGB2DVertex__anon2878be480111::TransformedMaskVertexFiller::ARGB2DVertex133 ARGB2DVertex(SkPoint d, GrColor, AtlasPt a) : devicePos{d}, atlasPos{a} {}
134
135 SkPoint devicePos;
136 AtlasPt atlasPos;
137 };
138
139 // Perspective SDFT or SDFT forced to 3D or perspective color.
140 struct Mask3DVertex {
141 SkPoint3 devicePos;
142 GrColor color;
143 AtlasPt atlasPos;
144 };
145
146 struct ARGB3DVertex {
ARGB3DVertex__anon2878be480111::TransformedMaskVertexFiller::ARGB3DVertex147 ARGB3DVertex(SkPoint3 d, GrColor, AtlasPt a) : devicePos{d}, atlasPos{a} {}
148
149 SkPoint3 devicePos;
150 AtlasPt atlasPos;
151 };
152
153 std::array<SkScalar, 4> sourceRect(PositionAndExtent positionAndExtent) const;
154
155 template<typename Quad, typename VertexData>
156 void fill2D(SkZip<Quad, const GrGlyph*, const VertexData> quadData,
157 GrColor color,
158 const SkMatrix& matrix) const;
159
160 template<typename Quad, typename VertexData>
161 void fill3D(SkZip<Quad, const GrGlyph*, const VertexData> quadData,
162 GrColor color,
163 const SkMatrix& matrix) const;
164
165 const GrMaskFormat fMaskType;
166 const SkPoint fPaddingInset;
167 const SkScalar fStrikeToSourceScale;
168 };
169
TransformedMaskVertexFiller(GrMaskFormat maskFormat,int dstPadding,SkScalar strikeToSourceScale)170 TransformedMaskVertexFiller::TransformedMaskVertexFiller(GrMaskFormat maskFormat,
171 int dstPadding,
172 SkScalar strikeToSourceScale)
173 : fMaskType{maskFormat}
174 , fPaddingInset{SkPoint::Make(dstPadding, dstPadding)}
175 , fStrikeToSourceScale{strikeToSourceScale} {}
176
fillVertexData(SkSpan<const GrGlyph * > glyphs,SkSpan<const PositionAndExtent> positioning,GrColor color,const SkMatrix & positionMatrix,SkIRect clip,void * vertexBuffer) const177 void TransformedMaskVertexFiller::fillVertexData(SkSpan<const GrGlyph*> glyphs,
178 SkSpan<const PositionAndExtent> positioning,
179 GrColor color,
180 const SkMatrix& positionMatrix,
181 SkIRect clip,
182 void* vertexBuffer) const {
183 auto quadData = [&](auto dst) {
184 return SkMakeZip(dst, glyphs, positioning);
185 };
186
187 if (!positionMatrix.hasPerspective()) {
188 if (fMaskType == GrMaskFormat::kARGB_GrMaskFormat) {
189 using Quad = ARGB2DVertex[4];
190 SkASSERT(sizeof(ARGB2DVertex) == this->vertexStride(positionMatrix));
191 this->fill2D(quadData((Quad*) vertexBuffer), color, positionMatrix);
192 } else {
193 using Quad = Mask2DVertex[4];
194 SkASSERT(sizeof(Mask2DVertex) == this->vertexStride(positionMatrix));
195 this->fill2D(quadData((Quad*) vertexBuffer), color, positionMatrix);
196 }
197 } else {
198 if (fMaskType == GrMaskFormat::kARGB_GrMaskFormat) {
199 using Quad = ARGB3DVertex[4];
200 SkASSERT(sizeof(ARGB3DVertex) == this->vertexStride(positionMatrix));
201 this->fill3D(quadData((Quad*) vertexBuffer), color, positionMatrix);
202 } else {
203 using Quad = Mask3DVertex[4];
204 SkASSERT(sizeof(Mask3DVertex) == this->vertexStride(positionMatrix));
205 this->fill3D(quadData((Quad*) vertexBuffer), color, positionMatrix);
206 }
207 }
208 }
209
210 std::array<SkScalar, 4>
sourceRect(PositionAndExtent positionAndExtent) const211 TransformedMaskVertexFiller::sourceRect(PositionAndExtent positionAndExtent) const {
212 auto[pos, rect] = positionAndExtent;
213 auto[l, t, r, b] = rect;
214 SkPoint LT = (SkPoint::Make(l, t) + fPaddingInset) * fStrikeToSourceScale + pos,
215 RB = (SkPoint::Make(r, b) - fPaddingInset) * fStrikeToSourceScale + pos;
216 return {LT.x(), LT.y(), RB.x(), RB.y()};
217 }
218
219 template<typename Quad, typename VertexData>
fill2D(SkZip<Quad,const GrGlyph *,const VertexData> quadData,GrColor color,const SkMatrix & positionMatrix) const220 void TransformedMaskVertexFiller::fill2D(SkZip<Quad, const GrGlyph*, const VertexData> quadData,
221 GrColor color,
222 const SkMatrix& positionMatrix) const {
223 for (auto[quad, glyph, positionAndExtent] : quadData) {
224 auto [l, t, r, b] = this->sourceRect(positionAndExtent);
225 SkPoint lt = positionMatrix.mapXY(l, t),
226 lb = positionMatrix.mapXY(l, b),
227 rt = positionMatrix.mapXY(r, t),
228 rb = positionMatrix.mapXY(r, b);
229 auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
230 quad[0] = {lt, color, {al, at}}; // L,T
231 quad[1] = {lb, color, {al, ab}}; // L,B
232 quad[2] = {rt, color, {ar, at}}; // R,T
233 quad[3] = {rb, color, {ar, ab}}; // R,B
234 }
235 }
236
237 template<typename Quad, typename VertexData>
fill3D(SkZip<Quad,const GrGlyph *,const VertexData> quadData,GrColor color,const SkMatrix & positionMatrix) const238 void TransformedMaskVertexFiller::fill3D(SkZip<Quad, const GrGlyph*, const VertexData> quadData,
239 GrColor color,
240 const SkMatrix& positionMatrix) const {
241 auto mapXYZ = [&](SkScalar x, SkScalar y) {
242 SkPoint pt{x, y};
243 SkPoint3 result;
244 positionMatrix.mapHomogeneousPoints(&result, &pt, 1);
245 return result;
246 };
247 for (auto[quad, glyph, positionAndExtent] : quadData) {
248 auto [l, t, r, b] = this->sourceRect(positionAndExtent);
249 SkPoint3 lt = mapXYZ(l, t),
250 lb = mapXYZ(l, b),
251 rt = mapXYZ(r, t),
252 rb = mapXYZ(r, b);
253 auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
254 quad[0] = {lt, color, {al, at}}; // L,T
255 quad[1] = {lb, color, {al, ab}}; // L,B
256 quad[2] = {rt, color, {ar, at}}; // R,T
257 quad[3] = {rb, color, {ar, ab}}; // R,B
258 }
259 }
260
opMaskType() const261 AtlasTextOp::MaskType TransformedMaskVertexFiller::opMaskType() const {
262 switch (fMaskType) {
263 case kA8_GrMaskFormat: return AtlasTextOp::MaskType::kGrayscaleCoverage;
264 case kA565_GrMaskFormat: return AtlasTextOp::MaskType::kLCDCoverage;
265 case kARGB_GrMaskFormat: return AtlasTextOp::MaskType::kColorBitmap;
266 }
267 SkUNREACHABLE;
268 }
269
270 struct AtlasPt {
271 uint16_t u;
272 uint16_t v;
273 };
274
275 // Normal text mask, SDFT, or color.
276 struct Mask2DVertex {
277 SkPoint devicePos;
278 GrColor color;
279 AtlasPt atlasPos;
280 };
281
282 struct ARGB2DVertex {
ARGB2DVertex__anon2878be480111::ARGB2DVertex283 ARGB2DVertex(SkPoint d, GrColor, AtlasPt a) : devicePos{d}, atlasPos{a} {}
284
285 SkPoint devicePos;
286 AtlasPt atlasPos;
287 };
288
289 // Perspective SDFT or SDFT forced to 3D or perspective color.
290 struct Mask3DVertex {
291 SkPoint3 devicePos;
292 GrColor color;
293 AtlasPt atlasPos;
294 };
295
296 struct ARGB3DVertex {
ARGB3DVertex__anon2878be480111::ARGB3DVertex297 ARGB3DVertex(SkPoint3 d, GrColor, AtlasPt a) : devicePos{d}, atlasPos{a} {}
298
299 SkPoint3 devicePos;
300 AtlasPt atlasPos;
301 };
302
op_mask_type(GrMaskFormat grMaskFormat)303 AtlasTextOp::MaskType op_mask_type(GrMaskFormat grMaskFormat) {
304 switch (grMaskFormat) {
305 case kA8_GrMaskFormat: return AtlasTextOp::MaskType::kGrayscaleCoverage;
306 case kA565_GrMaskFormat: return AtlasTextOp::MaskType::kLCDCoverage;
307 case kARGB_GrMaskFormat: return AtlasTextOp::MaskType::kColorBitmap;
308 }
309 SkUNREACHABLE;
310 }
311
position_matrix(const SkMatrix & drawMatrix,SkPoint drawOrigin)312 SkMatrix position_matrix(const SkMatrix& drawMatrix, SkPoint drawOrigin) {
313 SkMatrix position_matrix = drawMatrix;
314 return position_matrix.preTranslate(drawOrigin.x(), drawOrigin.y());
315 }
316
calculate_colors(skgpu::SurfaceContext * sc,const SkPaint & paint,const SkMatrixProvider & matrix,GrMaskFormat grMaskFormat,GrPaint * grPaint)317 SkPMColor4f calculate_colors(skgpu::SurfaceContext* sc,
318 const SkPaint& paint,
319 const SkMatrixProvider& matrix,
320 GrMaskFormat grMaskFormat,
321 GrPaint* grPaint) {
322 GrRecordingContext* rContext = sc->recordingContext();
323 const GrColorInfo& colorInfo = sc->colorInfo();
324 if (grMaskFormat == kARGB_GrMaskFormat) {
325 SkPaintToGrPaintReplaceShader(rContext, colorInfo, paint, matrix, nullptr, grPaint);
326 float a = grPaint->getColor4f().fA;
327 return {a, a, a, a};
328 }
329 SkPaintToGrPaint(rContext, colorInfo, paint, matrix, grPaint);
330 return grPaint->getColor4f();
331 }
332
333 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)334 void fill_transformed_vertices_2D(SkZip<Quad, const GrGlyph*, const VertexData> quadData,
335 SkScalar dstPadding,
336 SkScalar strikeToSource,
337 GrColor color,
338 const SkMatrix& matrix) {
339 SkPoint inset = {dstPadding, dstPadding};
340 for (auto[quad, glyph, vertexData] : quadData) {
341 auto[pos, rect] = vertexData;
342 auto[l, t, r, b] = rect;
343 SkPoint sLT = (SkPoint::Make(l, t) + inset) * strikeToSource + pos,
344 sRB = (SkPoint::Make(r, b) - inset) * strikeToSource + pos;
345 SkPoint lt = matrix.mapXY(sLT.x(), sLT.y()),
346 lb = matrix.mapXY(sLT.x(), sRB.y()),
347 rt = matrix.mapXY(sRB.x(), sLT.y()),
348 rb = matrix.mapXY(sRB.x(), sRB.y());
349 auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
350 quad[0] = {lt, color, {al, at}}; // L,T
351 quad[1] = {lb, color, {al, ab}}; // L,B
352 quad[2] = {rt, color, {ar, at}}; // R,T
353 quad[3] = {rb, color, {ar, ab}}; // R,B
354 }
355 }
356
357 // Check for integer translate with the same 2x2 matrix.
358 // Returns the translation, and true if the change from initial matrix to the position matrix
359 // support using direct glyph masks.
can_use_direct(const SkMatrix & initialPositionMatrix,const SkMatrix & positionMatrix)360 std::tuple<bool, SkVector> can_use_direct(
361 const SkMatrix& initialPositionMatrix, const SkMatrix& positionMatrix) {
362 // The existing direct glyph info can be used if the initialPositionMatrix, and the
363 // positionMatrix have the same 2x2, and the translation between them is integer.
364 // Calculate the translation in source space to a translation in device space by mapping
365 // (0, 0) through both the initial position matrix and the position matrix; take the difference.
366 SkVector translation = positionMatrix.mapOrigin() - initialPositionMatrix.mapOrigin();
367 return {initialPositionMatrix.getScaleX() == positionMatrix.getScaleX() &&
368 initialPositionMatrix.getScaleY() == positionMatrix.getScaleY() &&
369 initialPositionMatrix.getSkewX() == positionMatrix.getSkewX() &&
370 initialPositionMatrix.getSkewY() == positionMatrix.getSkewY() &&
371 SkScalarIsInt(translation.x()) && SkScalarIsInt(translation.y()),
372 translation};
373 }
374
375 // -- PathOpSubmitter ------------------------------------------------------------------------------
376 // Shared code for submitting GPU ops for drawing glyphs as paths.
377 class PathOpSubmitter {
378 struct PathAndPosition;
379 public:
380 PathOpSubmitter(bool isAntiAliased,
381 SkScalar strikeToSourceScale,
382 SkSpan<PathAndPosition> paths,
383 std::unique_ptr<PathAndPosition[], GrSubRunAllocator::ArrayDestroyer> pathData);
384
385 PathOpSubmitter(PathOpSubmitter&& that);
386
387 static PathOpSubmitter Make(const SkZip<SkGlyphVariant, SkPoint>& accepted,
388 bool isAntiAliased,
389 SkScalar strikeToSourceScale,
390 GrSubRunAllocator* alloc);
391
392 void submitOps(SkCanvas*,
393 const GrClip* clip,
394 const SkMatrixProvider& viewMatrix,
395 SkPoint drawOrigin,
396 const SkPaint& paint,
397 skgpu::v1::SurfaceDrawContext* sdc) const;
398
399 private:
400 struct PathAndPosition {
401 SkPath fPath;
402 SkPoint fPosition;
403 };
404 const bool fIsAntiAliased;
405 const SkScalar fStrikeToSourceScale;
406 const SkSpan<const PathAndPosition> fPaths;
407 std::unique_ptr<PathAndPosition[], GrSubRunAllocator::ArrayDestroyer> fPathData;
408 };
409
PathOpSubmitter(bool isAntiAliased,SkScalar strikeToSourceScale,SkSpan<PathAndPosition> paths,std::unique_ptr<PathAndPosition[],GrSubRunAllocator::ArrayDestroyer> pathData)410 PathOpSubmitter::PathOpSubmitter(
411 bool isAntiAliased,
412 SkScalar strikeToSourceScale,
413 SkSpan<PathAndPosition> paths,
414 std::unique_ptr<PathAndPosition[], GrSubRunAllocator::ArrayDestroyer> pathData)
415 : fIsAntiAliased{isAntiAliased}
416 , fStrikeToSourceScale{strikeToSourceScale}
417 , fPaths{paths}
418 , fPathData{std::move(pathData)} {
419 SkASSERT(!fPaths.empty());
420 }
421
PathOpSubmitter(PathOpSubmitter && that)422 PathOpSubmitter::PathOpSubmitter(PathOpSubmitter&& that)
423 : fIsAntiAliased{that.fIsAntiAliased}
424 , fStrikeToSourceScale{that.fStrikeToSourceScale}
425 , fPaths{that.fPaths}
426 , fPathData{std::move(that.fPathData)} {}
427
Make(const SkZip<SkGlyphVariant,SkPoint> & accepted,bool isAntiAliased,SkScalar strikeToSourceScale,GrSubRunAllocator * alloc)428 PathOpSubmitter PathOpSubmitter::Make(const SkZip<SkGlyphVariant, SkPoint>& accepted,
429 bool isAntiAliased,
430 SkScalar strikeToSourceScale,
431 GrSubRunAllocator* alloc) {
432 auto pathData = alloc->makeUniqueArray<PathAndPosition>(
433 accepted.size(),
434 [&](int i){
435 auto [variant, pos] = accepted[i];
436 return PathAndPosition{*variant.path(), pos};
437 });
438 SkSpan<PathAndPosition> paths{pathData.get(), accepted.size()};
439
440 return PathOpSubmitter{isAntiAliased, strikeToSourceScale, paths, std::move(pathData)};
441 }
442
submitOps(SkCanvas * canvas,const GrClip * clip,const SkMatrixProvider & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,skgpu::v1::SurfaceDrawContext * sdc) const443 void PathOpSubmitter::submitOps(SkCanvas* canvas,
444 const GrClip* clip,
445 const SkMatrixProvider& viewMatrix,
446 SkPoint drawOrigin,
447 const SkPaint& paint,
448 skgpu::v1::SurfaceDrawContext* sdc) const {
449 SkPaint runPaint{paint};
450 runPaint.setAntiAlias(fIsAntiAliased);
451 // If there are shaders, blurs or styles, the path must be scaled into source
452 // space independently of the CTM. This allows the CTM to be correct for the
453 // different effects.
454 GrStyle style(runPaint);
455
456 bool needsExactCTM = runPaint.getShader()
457 || style.applies()
458 || runPaint.getMaskFilter();
459
460 // Calculate the matrix that maps the path glyphs from their size in the strike to
461 // the graphics source space.
462 SkMatrix strikeToSource = SkMatrix::Scale(fStrikeToSourceScale, fStrikeToSourceScale);
463 strikeToSource.postTranslate(drawOrigin.x(), drawOrigin.y());
464 if (!needsExactCTM) {
465 for (const auto& pathPos : fPaths) {
466 const SkPath& path = pathPos.fPath;
467 const SkPoint pos = pathPos.fPosition;
468 // Transform the glyph to source space.
469 SkMatrix pathMatrix = strikeToSource;
470 pathMatrix.postTranslate(pos.x(), pos.y());
471
472 SkAutoCanvasRestore acr(canvas, true);
473 canvas->concat(pathMatrix);
474 canvas->drawPath(path, runPaint);
475 }
476 } else {
477 // Transform the path to device because the deviceMatrix must be unchanged to
478 // draw effect, filter or shader paths.
479 for (const auto& pathPos : fPaths) {
480 const SkPath& path = pathPos.fPath;
481 const SkPoint pos = pathPos.fPosition;
482 // Transform the glyph to source space.
483 SkMatrix pathMatrix = strikeToSource;
484 pathMatrix.postTranslate(pos.x(), pos.y());
485
486 SkPath deviceOutline;
487 path.transform(pathMatrix, &deviceOutline);
488 deviceOutline.setIsVolatile(true);
489 canvas->drawPath(deviceOutline, runPaint);
490 }
491 }
492 }
493
494 // -- PathSubRun -----------------------------------------------------------------------------------
495 class PathSubRun final : public GrSubRun, public GrBlobSubRun {
496 public:
PathSubRun(PathOpSubmitter && pathDrawing)497 PathSubRun(PathOpSubmitter&& pathDrawing) : fPathDrawing(std::move(pathDrawing)) {}
498
Make(const SkZip<SkGlyphVariant,SkPoint> & accepted,bool isAntiAliased,SkScalar strikeToSourceScale,GrSubRunAllocator * alloc)499 static GrSubRunOwner Make(const SkZip<SkGlyphVariant, SkPoint>& accepted,
500 bool isAntiAliased,
501 SkScalar strikeToSourceScale,
502 GrSubRunAllocator* alloc) {
503 return alloc->makeUnique<PathSubRun>(
504 PathOpSubmitter::Make(accepted, isAntiAliased, strikeToSourceScale, alloc));
505 }
506
draw(SkCanvas * canvas,const GrClip * clip,const SkMatrixProvider & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,skgpu::v1::SurfaceDrawContext * sdc) const507 void draw(SkCanvas* canvas,
508 const GrClip* clip,
509 const SkMatrixProvider& viewMatrix,
510 SkPoint drawOrigin,
511 const SkPaint& paint,
512 skgpu::v1::SurfaceDrawContext* sdc) const override {
513 fPathDrawing.submitOps(canvas, clip, viewMatrix, drawOrigin, paint, sdc);
514 }
515
blobCast() const516 const GrBlobSubRun* blobCast() const override { return this; }
unflattenSize() const517 int unflattenSize() const override { return 0; }
518
canReuse(const SkPaint & paint,const SkMatrix & positionMatrix) const519 bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const override {
520 return true;
521 }
testingOnly_atlasSubRun() const522 const GrAtlasSubRun* testingOnly_atlasSubRun() const override { return nullptr; }
MakeFromBuffer(const GrTextReferenceFrame * referenceFrame,SkReadBuffer & buffer,GrSubRunAllocator * alloc,const SkStrikeClient * client)523 static GrSubRunOwner MakeFromBuffer(const GrTextReferenceFrame* referenceFrame,
524 SkReadBuffer& buffer,
525 GrSubRunAllocator* alloc,
526 const SkStrikeClient* client) {
527 return nullptr;
528 }
529
530 protected:
subRunType() const531 SubRunType subRunType() const override { return kPath; }
doFlatten(SkWriteBuffer & buffer) const532 void doFlatten(SkWriteBuffer& buffer) const override {
533 SK_ABORT("Not implemented.");
534 }
535
536 private:
537 PathOpSubmitter fPathDrawing;
538 };
539
540 // -- DrawableOpSubmitter --------------------------------------------------------------------------
541 // Shared code for submitting GPU ops for drawing glyphs as drawables.
542 class DrawableOpSubmitter {
543 struct DrawableAndPosition;
544 public:
545 DrawableOpSubmitter(bool isAntiAliased,
546 SkScalar strikeToSourceScale,
547 SkSpan<DrawableAndPosition> drawables,
548 std::unique_ptr<DrawableAndPosition[],
549 GrSubRunAllocator::ArrayDestroyer> drawableData);
550
551 DrawableOpSubmitter(DrawableOpSubmitter&& that);
552
553 static DrawableOpSubmitter Make(const SkZip<SkGlyphVariant, SkPoint>& accepted,
554 bool isAntiAliased,
555 SkScalar strikeToSourceScale,
556 GrSubRunAllocator* alloc);
557
558 void submitOps(SkCanvas*,
559 const GrClip* clip,
560 const SkMatrixProvider& viewMatrix,
561 SkPoint drawOrigin,
562 const SkPaint& paint,
563 skgpu::v1::SurfaceDrawContext* sdc) const;
564
565 private:
566 struct DrawableAndPosition {
567 sk_sp<SkDrawable> fDrawable;
568 SkPoint fPosition;
569 };
570 const bool fIsAntiAliased;
571 const SkScalar fStrikeToSourceScale;
572 const SkSpan<const DrawableAndPosition> fDrawables;
573 std::unique_ptr<DrawableAndPosition[], GrSubRunAllocator::ArrayDestroyer> fDrawableData;
574 };
575
DrawableOpSubmitter(bool isAntiAliased,SkScalar strikeToSourceScale,SkSpan<DrawableAndPosition> drawables,std::unique_ptr<DrawableAndPosition[],GrSubRunAllocator::ArrayDestroyer> drawableData)576 DrawableOpSubmitter::DrawableOpSubmitter(
577 bool isAntiAliased,
578 SkScalar strikeToSourceScale,
579 SkSpan<DrawableAndPosition> drawables,
580 std::unique_ptr<DrawableAndPosition[], GrSubRunAllocator::ArrayDestroyer> drawableData)
581 : fIsAntiAliased{isAntiAliased}
582 , fStrikeToSourceScale{strikeToSourceScale}
583 , fDrawables{drawables}
584 , fDrawableData{std::move(drawableData)} {
585 SkASSERT(!fDrawables.empty());
586 }
587
DrawableOpSubmitter(DrawableOpSubmitter && that)588 DrawableOpSubmitter::DrawableOpSubmitter(DrawableOpSubmitter&& that)
589 : fIsAntiAliased{that.fIsAntiAliased}
590 , fStrikeToSourceScale{that.fStrikeToSourceScale}
591 , fDrawables{that.fDrawables}
592 , fDrawableData{std::move(that.fDrawableData)} {}
593
Make(const SkZip<SkGlyphVariant,SkPoint> & accepted,bool isAntiAliased,SkScalar strikeToSourceScale,GrSubRunAllocator * alloc)594 DrawableOpSubmitter DrawableOpSubmitter::Make(const SkZip<SkGlyphVariant, SkPoint>& accepted,
595 bool isAntiAliased,
596 SkScalar strikeToSourceScale,
597 GrSubRunAllocator* alloc) {
598 auto drawableData = alloc->makeUniqueArray<DrawableAndPosition>(
599 accepted.size(),
600 [&](int i){
601 auto [variant, pos] = accepted[i];
602 return DrawableAndPosition{sk_ref_sp(variant.drawable()), pos};
603 });
604 SkSpan<DrawableAndPosition> drawables{drawableData.get(), accepted.size()};
605
606 return DrawableOpSubmitter{isAntiAliased, strikeToSourceScale,
607 drawables, std::move(drawableData)};
608 }
609
submitOps(SkCanvas * canvas,const GrClip * clip,const SkMatrixProvider & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,skgpu::v1::SurfaceDrawContext * sdc) const610 void DrawableOpSubmitter::submitOps(SkCanvas* canvas,
611 const GrClip* clip,
612 const SkMatrixProvider& viewMatrix,
613 SkPoint drawOrigin,
614 const SkPaint& paint,
615 skgpu::v1::SurfaceDrawContext* sdc) const {
616 // Calculate the matrix that maps the path glyphs from their size in the strike to
617 // the graphics source space.
618 SkMatrix strikeToSource = SkMatrix::Scale(fStrikeToSourceScale, fStrikeToSourceScale);
619 strikeToSource.postTranslate(drawOrigin.x(), drawOrigin.y());
620
621 // Transform the path to device because the deviceMatrix must be unchanged to
622 // draw effect, filter or shader paths.
623 for (const auto& pathPos : fDrawables) {
624 const sk_sp<SkDrawable>& drawable = pathPos.fDrawable;
625 const SkPoint pos = pathPos.fPosition;
626 // Transform the glyph to source space.
627 SkMatrix pathMatrix = strikeToSource;
628 pathMatrix.postTranslate(pos.x(), pos.y());
629
630 SkAutoCanvasRestore acr(canvas, false);
631 SkRect drawableBounds = drawable->getBounds();
632 pathMatrix.mapRect(&drawableBounds);
633 canvas->saveLayer(&drawableBounds, &paint);
634 drawable->draw(canvas, &pathMatrix);
635 }
636 }
637
638 template <typename SubRun>
make_drawable_sub_run(const SkZip<SkGlyphVariant,SkPoint> & drawables,bool isAntiAliased,SkScalar strikeToSourceScale,GrSubRunAllocator * alloc)639 GrSubRunOwner make_drawable_sub_run(const SkZip<SkGlyphVariant, SkPoint>& drawables,
640 bool isAntiAliased,
641 SkScalar strikeToSourceScale,
642 GrSubRunAllocator* alloc) {
643 return alloc->makeUnique<SubRun>(
644 DrawableOpSubmitter::Make(drawables, isAntiAliased, strikeToSourceScale, alloc));
645 }
646
647 // -- DrawableSubRunSlug ---------------------------------------------------------------------------
648 class DrawableSubRunSlug : public GrSubRun {
649 public:
DrawableSubRunSlug(DrawableOpSubmitter && drawingDrawing)650 DrawableSubRunSlug(DrawableOpSubmitter&& drawingDrawing)
651 : fDrawingDrawing(std::move(drawingDrawing)) {}
652
MakeFromBuffer(const GrTextReferenceFrame * referenceFrame,SkReadBuffer & buffer,GrSubRunAllocator * alloc,const SkStrikeClient * client)653 static GrSubRunOwner MakeFromBuffer(const GrTextReferenceFrame* referenceFrame,
654 SkReadBuffer& buffer,
655 GrSubRunAllocator* alloc,
656 const SkStrikeClient* client) {
657 return nullptr;
658 }
659
draw(SkCanvas * canvas,const GrClip * clip,const SkMatrixProvider & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,skgpu::v1::SurfaceDrawContext * sdc) const660 void draw(SkCanvas* canvas,
661 const GrClip* clip,
662 const SkMatrixProvider& viewMatrix,
663 SkPoint drawOrigin,
664 const SkPaint& paint,
665 skgpu::v1::SurfaceDrawContext* sdc) const override {
666 fDrawingDrawing.submitOps(canvas, clip, viewMatrix, drawOrigin, paint, sdc);
667 }
668
unflattenSize() const669 int unflattenSize() const override { return 0; }
670
671 protected:
subRunType() const672 SubRunType subRunType() const override { return kDrawable; }
doFlatten(SkWriteBuffer & buffer) const673 void doFlatten(SkWriteBuffer& buffer) const override {
674 SK_ABORT("Not implemented.");
675 }
676
677 private:
678 DrawableOpSubmitter fDrawingDrawing;
679 };
680
681 // -- DrawableSubRun -------------------------------------------------------------------------------
682 class DrawableSubRun final : public DrawableSubRunSlug, public GrBlobSubRun {
683 public:
684 using DrawableSubRunSlug::DrawableSubRunSlug;
blobCast() const685 const GrBlobSubRun* blobCast() const override { return this; }
unflattenSize() const686 int unflattenSize() const override { return 0; }
687
canReuse(const SkPaint & paint,const SkMatrix & positionMatrix) const688 bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const override {
689 return true;
690 }
testingOnly_atlasSubRun() const691 const GrAtlasSubRun* testingOnly_atlasSubRun() const override { return nullptr; }
692 };
693
694 // -- DirectMaskSubRun -----------------------------------------------------------------------------
695 class DirectMaskSubRun final : public GrSubRun, public GrBlobSubRun, public GrAtlasSubRun {
696 public:
697 using DevicePosition = skvx::Vec<2, int16_t>;
698
699 DirectMaskSubRun(const GrTextReferenceFrame* referenceFrame,
700 GrMaskFormat format,
701 const SkGlyphRect& deviceBounds,
702 SkSpan<const DevicePosition> devicePositions,
703 GrGlyphVector&& glyphs,
704 bool glyphsOutOfBounds,
705 bool supportBilerpAtlas);
706
707 static GrSubRunOwner Make(const GrTextBlob* blob,
708 const SkZip<SkGlyphVariant, SkPoint>& accepted,
709 sk_sp<SkStrike>&& strike,
710 GrMaskFormat format,
711 GrSubRunAllocator* alloc);
712
713 void draw(SkCanvas*,
714 const GrClip*,
715 const SkMatrixProvider& viewMatrix,
716 SkPoint drawOrigin,
717 const SkPaint& paint,
718 skgpu::v1::SurfaceDrawContext*) const override;
719
720 std::tuple<const GrClip*, GrOp::Owner>
721 makeAtlasTextOp(const GrClip* clip,
722 const SkMatrixProvider& viewMatrix,
723 SkPoint drawOrigin,
724 const SkPaint& paint,
725 skgpu::v1::SurfaceDrawContext* sdc,
726 GrAtlasSubRunOwner) const override;
727
blobCast() const728 const GrBlobSubRun* blobCast() const override { return this; }
unflattenSize() const729 int unflattenSize() const override { return 0; }
730
731 bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const override;
732
733 const GrAtlasSubRun* testingOnly_atlasSubRun() const override;
734
735 size_t vertexStride(const SkMatrix& drawMatrix) const override;
736
737 int glyphCount() const override;
738
739 void testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) const override;
740
741 std::tuple<bool, int>
742 regenerateAtlas(int begin, int end, GrMeshDrawTarget*) const override;
743
744 void fillVertexData(void* vertexDst, int offset, int count,
745 GrColor color,
746 const SkMatrix& drawMatrix, SkPoint drawOrigin,
747 SkIRect clip) const override;
748
749 protected:
subRunType() const750 SubRunType subRunType() const override { return kDirectMask; }
doFlatten(SkWriteBuffer & buffer) const751 void doFlatten(SkWriteBuffer& buffer) const override {
752 SK_ABORT("Not implemented.");
753 }
754
755 private:
756 // The rectangle that surrounds all the glyph bounding boxes in device space.
757 SkRect deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const;
758
759 const GrTextReferenceFrame* const fTextReferenceFrame;
760 const GrMaskFormat fMaskFormat;
761
762 // The union of all the glyph bounds in device space.
763 const SkGlyphRect fGlyphDeviceBounds;
764 const SkSpan<const DevicePosition> fLeftTopDevicePos;
765 const bool fSomeGlyphsExcluded;
766 const bool fSupportBilerpAtlas;
767
768 // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
769 // be single threaded.
770 mutable GrGlyphVector fGlyphs;
771 };
772
DirectMaskSubRun(const GrTextReferenceFrame * referenceFrame,GrMaskFormat format,const SkGlyphRect & deviceBounds,SkSpan<const DevicePosition> devicePositions,GrGlyphVector && glyphs,bool glyphsOutOfBounds,bool supportBilerpAtlas)773 DirectMaskSubRun::DirectMaskSubRun(const GrTextReferenceFrame* referenceFrame,
774 GrMaskFormat format,
775 const SkGlyphRect& deviceBounds,
776 SkSpan<const DevicePosition> devicePositions,
777 GrGlyphVector&& glyphs,
778 bool glyphsOutOfBounds,
779 bool supportBilerpAtlas)
780 : fTextReferenceFrame{referenceFrame}
781 , fMaskFormat{format}
782 , fGlyphDeviceBounds{deviceBounds}
783 , fLeftTopDevicePos{devicePositions}
784 , fSomeGlyphsExcluded{glyphsOutOfBounds}
785 , fSupportBilerpAtlas{supportBilerpAtlas}
786 , fGlyphs{std::move(glyphs)} {}
787
Make(const GrTextBlob * blob,const SkZip<SkGlyphVariant,SkPoint> & accepted,sk_sp<SkStrike> && strike,GrMaskFormat format,GrSubRunAllocator * alloc)788 GrSubRunOwner DirectMaskSubRun::Make(const GrTextBlob* blob,
789 const SkZip<SkGlyphVariant, SkPoint>& accepted,
790 sk_sp<SkStrike>&& strike,
791 GrMaskFormat format,
792 GrSubRunAllocator* alloc) {
793 auto glyphLeftTop = alloc->makePODArray<DevicePosition>(accepted.size());
794 auto glyphIDs = alloc->makePODArray<GrGlyphVector::Variant>(accepted.size());
795
796 // Because this is the direct case, the maximum width or height is the size that fits in the
797 // atlas. This boundary is checked below to ensure that the call to SkGlyphRect below will
798 // not overflow.
799 constexpr SkScalar kMaxPos =
800 std::numeric_limits<int16_t>::max() - SkStrikeCommon::kSkSideTooBigForAtlas;
801 SkGlyphRect runBounds = skglyph::empty_rect();
802 size_t goodPosCount = 0;
803 for (auto [variant, pos] : accepted) {
804 auto [x, y] = pos;
805 // Ensure that the .offset() call below does not overflow. And, at this point none of the
806 // rectangles are empty because they were culled before the run was created. Basically,
807 // cull all the glyphs that can't appear on the screen.
808 if (-kMaxPos < x && x < kMaxPos && -kMaxPos < y && y < kMaxPos) {
809 const SkGlyph* const skGlyph = variant;
810 const SkGlyphRect deviceBounds =
811 skGlyph->glyphRect().offset(SkScalarRoundToInt(x), SkScalarRoundToInt(y));
812 runBounds = skglyph::rect_union(runBounds, deviceBounds);
813 glyphLeftTop[goodPosCount] = deviceBounds.topLeft();
814 glyphIDs[goodPosCount].packedGlyphID = skGlyph->getPackedID();
815 goodPosCount += 1;
816 }
817 }
818
819 // Wow! no glyphs are in bounds and had non-empty bounds.
820 if (goodPosCount == 0) {
821 return nullptr;
822 }
823
824 // If some glyphs were excluded by the bounds, then this subrun can't be generally be used
825 // for other draws. Mark the subrun as not general.
826 bool glyphsExcluded = goodPosCount != accepted.size();
827 SkSpan<const DevicePosition> leftTop{glyphLeftTop, goodPosCount};
828 return alloc->makeUnique<DirectMaskSubRun>(
829 blob, format, runBounds, leftTop,
830 GrGlyphVector{std::move(strike), {glyphIDs, goodPosCount}},
831 glyphsExcluded,
832 blob->supportBilerpAtlas());
833 }
834
canReuse(const SkPaint & paint,const SkMatrix & positionMatrix) const835 bool DirectMaskSubRun::canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const {
836 auto [reuse, translation] =
837 can_use_direct(fTextReferenceFrame->initialPositionMatrix(), positionMatrix);
838
839 // If glyphs were excluded because of position bounds, then this subrun can only be reused if
840 // there is no change in position.
841 if (fSomeGlyphsExcluded) {
842 return translation.x() == 0 && translation.y() == 0;
843 }
844
845 return reuse;
846 }
847
vertexStride(const SkMatrix &) const848 size_t DirectMaskSubRun::vertexStride(const SkMatrix&) const {
849 if (fMaskFormat != kARGB_GrMaskFormat) {
850 return sizeof(Mask2DVertex);
851 } else {
852 return sizeof(ARGB2DVertex);
853 }
854 }
855
glyphCount() const856 int DirectMaskSubRun::glyphCount() const {
857 return SkCount(fGlyphs.glyphs());
858 }
859
draw(SkCanvas *,const GrClip * clip,const SkMatrixProvider & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,skgpu::v1::SurfaceDrawContext * sdc) const860 void DirectMaskSubRun::draw(SkCanvas*,
861 const GrClip* clip,
862 const SkMatrixProvider& viewMatrix,
863 SkPoint drawOrigin,
864 const SkPaint& paint,
865 skgpu::v1::SurfaceDrawContext* sdc) const{
866 auto[drawingClip, op] = this->makeAtlasTextOp(
867 clip, viewMatrix, drawOrigin, paint, sdc, nullptr);
868 if (op != nullptr) {
869 sdc->addDrawOp(drawingClip, std::move(op));
870 }
871 }
872
873 namespace {
874 enum ClipMethod {
875 kClippedOut,
876 kUnclipped,
877 kGPUClipped,
878 kGeometryClipped
879 };
880
881 std::tuple<ClipMethod, SkIRect>
calculate_clip(const GrClip * clip,SkRect deviceBounds,SkRect glyphBounds)882 calculate_clip(const GrClip* clip, SkRect deviceBounds, SkRect glyphBounds) {
883 if (clip == nullptr && !deviceBounds.intersects(glyphBounds)) {
884 return {kClippedOut, SkIRect::MakeEmpty()};
885 } else if (clip != nullptr) {
886 switch (auto result = clip->preApply(glyphBounds, GrAA::kNo); result.fEffect) {
887 case GrClip::Effect::kClippedOut:
888 return {kClippedOut, SkIRect::MakeEmpty()};
889 case GrClip::Effect::kUnclipped:
890 return {kUnclipped, SkIRect::MakeEmpty()};
891 case GrClip::Effect::kClipped: {
892 if (result.fIsRRect && result.fRRect.isRect()) {
893 SkRect r = result.fRRect.rect();
894 if (result.fAA == GrAA::kNo || GrClip::IsPixelAligned(r)) {
895 SkIRect clipRect = SkIRect::MakeEmpty();
896 // Clip geometrically during onPrepare using clipRect.
897 r.round(&clipRect);
898 if (clipRect.contains(glyphBounds)) {
899 // If fully within the clip, signal no clipping using the empty rect.
900 return {kUnclipped, SkIRect::MakeEmpty()};
901 }
902 // Use the clipRect to clip the geometry.
903 return {kGeometryClipped, clipRect};
904 }
905 // Partial pixel clipped at this point. Have the GPU handle it.
906 }
907 }
908 break;
909 }
910 }
911 return {kGPUClipped, SkIRect::MakeEmpty()};
912 }
913 } // namespace
914
915 std::tuple<const GrClip*, GrOp::Owner>
makeAtlasTextOp(const GrClip * clip,const SkMatrixProvider & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,skgpu::v1::SurfaceDrawContext * sdc,GrAtlasSubRunOwner) const916 DirectMaskSubRun::makeAtlasTextOp(const GrClip* clip,
917 const SkMatrixProvider& viewMatrix,
918 SkPoint drawOrigin,
919 const SkPaint& paint,
920 skgpu::v1::SurfaceDrawContext* sdc,
921 GrAtlasSubRunOwner) const {
922 SkASSERT(this->glyphCount() != 0);
923
924 const SkMatrix& drawMatrix = viewMatrix.localToDevice();
925
926 // We can clip geometrically using clipRect and ignore clip when an axis-aligned rectangular
927 // non-AA clip is used. If clipRect is empty, and clip is nullptr, then there is no clipping
928 // needed.
929 const SkRect subRunBounds = this->deviceRect(drawMatrix, drawOrigin);
930 const SkRect deviceBounds = SkRect::MakeWH(sdc->width(), sdc->height());
931 auto [clipMethod, clipRect] = calculate_clip(clip, deviceBounds, subRunBounds);
932
933 switch (clipMethod) {
934 case kClippedOut:
935 // Returning nullptr as op means skip this op.
936 return {nullptr, nullptr};
937 case kUnclipped:
938 case kGeometryClipped:
939 // GPU clip is not needed.
940 clip = nullptr;
941 break;
942 case kGPUClipped:
943 // Use the GPU clip; clipRect is ignored.
944 break;
945 }
946
947 if (!clipRect.isEmpty()) { SkASSERT(clip == nullptr); }
948
949 GrPaint grPaint;
950 const SkPMColor4f drawingColor =
951 calculate_colors(sdc, paint, viewMatrix, fMaskFormat, &grPaint);
952
953 auto geometry = AtlasTextOp::Geometry::MakeForBlob(*this,
954 drawMatrix,
955 drawOrigin,
956 clipRect,
957 sk_ref_sp(fTextReferenceFrame),
958 drawingColor,
959 sdc->arenaAlloc());
960
961 GrRecordingContext* const rContext = sdc->recordingContext();
962 GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext,
963 op_mask_type(fMaskFormat),
964 false,
965 this->glyphCount(),
966 subRunBounds,
967 geometry,
968 std::move(grPaint));
969
970 return {clip, std::move(op)};
971 }
972
testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache * cache) const973 void DirectMaskSubRun::testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) const {
974 fGlyphs.packedGlyphIDToGrGlyph(cache);
975 }
976
977 std::tuple<bool, int>
regenerateAtlas(int begin,int end,GrMeshDrawTarget * target) const978 DirectMaskSubRun::regenerateAtlas(int begin, int end, GrMeshDrawTarget* target) const {
979 int srcPadding = fSupportBilerpAtlas ? 1 : 0;
980 return fGlyphs.regenerateAtlas(
981 begin, end, fMaskFormat, srcPadding, target, fSupportBilerpAtlas);
982 }
983
984 // The 99% case. No clip. Non-color only.
direct_2D(SkZip<Mask2DVertex[4],const GrGlyph *,const DirectMaskSubRun::DevicePosition> quadData,GrColor color,SkPoint originOffset)985 void direct_2D(SkZip<Mask2DVertex[4],
986 const GrGlyph*,
987 const DirectMaskSubRun::DevicePosition> quadData,
988 GrColor color,
989 SkPoint originOffset) {
990 for (auto[quad, glyph, leftTop] : quadData) {
991 auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
992 SkScalar dl = leftTop[0] + originOffset.x(),
993 dt = leftTop[1] + originOffset.y(),
994 dr = dl + (ar - al),
995 db = dt + (ab - at);
996
997 quad[0] = {{dl, dt}, color, {al, at}}; // L,T
998 quad[1] = {{dl, db}, color, {al, ab}}; // L,B
999 quad[2] = {{dr, dt}, color, {ar, at}}; // R,T
1000 quad[3] = {{dr, db}, color, {ar, ab}}; // R,B
1001 }
1002 }
1003
1004 template <typename Rect>
ltbr(const Rect & r)1005 auto ltbr(const Rect& r) {
1006 return std::make_tuple(r.left(), r.top(), r.right(), r.bottom());
1007 }
1008
1009 // Handle any combination of BW or color and clip or no clip.
1010 template<typename Quad, typename VertexData>
generalized_direct_2D(SkZip<Quad,const GrGlyph *,const VertexData> quadData,GrColor color,SkPoint originOffset,SkIRect * clip=nullptr)1011 void generalized_direct_2D(SkZip<Quad, const GrGlyph*, const VertexData> quadData,
1012 GrColor color,
1013 SkPoint originOffset,
1014 SkIRect* clip = nullptr) {
1015 for (auto[quad, glyph, leftTop] : quadData) {
1016 auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
1017 uint16_t w = ar - al,
1018 h = ab - at;
1019 SkScalar l = (SkScalar)leftTop[0] + originOffset.x(),
1020 t = (SkScalar)leftTop[1] + originOffset.y();
1021 if (clip == nullptr) {
1022 auto[dl, dt, dr, db] = SkRect::MakeLTRB(l, t, l + w, t + h);
1023 quad[0] = {{dl, dt}, color, {al, at}}; // L,T
1024 quad[1] = {{dl, db}, color, {al, ab}}; // L,B
1025 quad[2] = {{dr, dt}, color, {ar, at}}; // R,T
1026 quad[3] = {{dr, db}, color, {ar, ab}}; // R,B
1027 } else {
1028 SkIRect devIRect = SkIRect::MakeLTRB(l, t, l + w, t + h);
1029 SkScalar dl, dt, dr, db;
1030 if (!clip->containsNoEmptyCheck(devIRect)) {
1031 if (SkIRect clipped; clipped.intersect(devIRect, *clip)) {
1032 al += clipped.left() - devIRect.left();
1033 at += clipped.top() - devIRect.top();
1034 ar += clipped.right() - devIRect.right();
1035 ab += clipped.bottom() - devIRect.bottom();
1036 std::tie(dl, dt, dr, db) = ltbr(clipped);
1037 } else {
1038 // TODO: omit generating any vertex data for fully clipped glyphs ?
1039 std::tie(dl, dt, dr, db) = std::make_tuple(0, 0, 0, 0);
1040 std::tie(al, at, ar, ab) = std::make_tuple(0, 0, 0, 0);
1041 }
1042 } else {
1043 std::tie(dl, dt, dr, db) = ltbr(devIRect);
1044 }
1045 quad[0] = {{dl, dt}, color, {al, at}}; // L,T
1046 quad[1] = {{dl, db}, color, {al, ab}}; // L,B
1047 quad[2] = {{dr, dt}, color, {ar, at}}; // R,T
1048 quad[3] = {{dr, db}, color, {ar, ab}}; // R,B
1049 }
1050 }
1051 }
1052
fillVertexData(void * vertexDst,int offset,int count,GrColor color,const SkMatrix & drawMatrix,SkPoint drawOrigin,SkIRect clip) const1053 void DirectMaskSubRun::fillVertexData(void* vertexDst, int offset, int count,
1054 GrColor color,
1055 const SkMatrix& drawMatrix, SkPoint drawOrigin,
1056 SkIRect clip) const {
1057 const SkMatrix positionMatrix = position_matrix(drawMatrix, drawOrigin);
1058 auto quadData = [&](auto dst) {
1059 return SkMakeZip(dst,
1060 fGlyphs.glyphs().subspan(offset, count),
1061 fLeftTopDevicePos.subspan(offset, count));
1062 };
1063
1064 SkPoint originOffset =
1065 positionMatrix.mapOrigin() - fTextReferenceFrame->initialPositionMatrix().mapOrigin();
1066
1067 if (clip.isEmpty()) {
1068 if (fMaskFormat != kARGB_GrMaskFormat) {
1069 using Quad = Mask2DVertex[4];
1070 SkASSERT(sizeof(Mask2DVertex) == this->vertexStride(positionMatrix));
1071 direct_2D(quadData((Quad*)vertexDst), color, originOffset);
1072 } else {
1073 using Quad = ARGB2DVertex[4];
1074 SkASSERT(sizeof(ARGB2DVertex) == this->vertexStride(positionMatrix));
1075 generalized_direct_2D(quadData((Quad*)vertexDst), color, originOffset);
1076 }
1077 } else {
1078 if (fMaskFormat != kARGB_GrMaskFormat) {
1079 using Quad = Mask2DVertex[4];
1080 SkASSERT(sizeof(Mask2DVertex) == this->vertexStride(positionMatrix));
1081 generalized_direct_2D(quadData((Quad*)vertexDst), color, originOffset, &clip);
1082 } else {
1083 using Quad = ARGB2DVertex[4];
1084 SkASSERT(sizeof(ARGB2DVertex) == this->vertexStride(positionMatrix));
1085 generalized_direct_2D(quadData((Quad*)vertexDst), color, originOffset, &clip);
1086 }
1087 }
1088 }
1089
deviceRect(const SkMatrix & drawMatrix,SkPoint drawOrigin) const1090 SkRect DirectMaskSubRun::deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const {
1091 SkIRect outBounds = fGlyphDeviceBounds.iRect();
1092
1093 // Calculate the offset from the initial device origin to the current device origin.
1094 SkVector offset = drawMatrix.mapPoint(drawOrigin) -
1095 fTextReferenceFrame->initialPositionMatrix().mapOrigin();
1096
1097 // The offset should be integer, but make sure.
1098 SkIVector iOffset = {SkScalarRoundToInt(offset.x()), SkScalarRoundToInt(offset.y())};
1099
1100 return SkRect::Make(outBounds.makeOffset(iOffset));
1101 }
1102
testingOnly_atlasSubRun() const1103 const GrAtlasSubRun* DirectMaskSubRun::testingOnly_atlasSubRun() const {
1104 return this;
1105 }
1106
1107 // -- TransformedMaskSubRun ------------------------------------------------------------------------
1108 class TransformedMaskSubRun final : public GrSubRun, public GrBlobSubRun, public GrAtlasSubRun {
1109 public:
1110 using VertexData = TransformedMaskVertexFiller::PositionAndExtent;
1111
1112 TransformedMaskSubRun(const GrTextReferenceFrame* referenceFrame,
1113 GrMaskFormat format,
1114 SkScalar strikeToSourceScale,
1115 const SkRect& bounds,
1116 SkSpan<const VertexData> vertexData,
1117 GrGlyphVector&& glyphs);
1118
1119 static GrSubRunOwner Make(const GrTextReferenceFrame* referenceFrame,
1120 const SkZip<SkGlyphVariant, SkPoint>& accepted,
1121 sk_sp<SkStrike>&& strike,
1122 SkScalar strikeToSourceScale,
1123 GrMaskFormat format,
1124 GrSubRunAllocator* alloc);
1125
MakeFromBuffer(const GrTextReferenceFrame * referenceFrame,SkReadBuffer & buffer,GrSubRunAllocator * alloc,const SkStrikeClient * client)1126 static GrSubRunOwner MakeFromBuffer(const GrTextReferenceFrame* referenceFrame,
1127 SkReadBuffer& buffer,
1128 GrSubRunAllocator* alloc,
1129 const SkStrikeClient* client) {
1130 return nullptr;
1131 }
1132
1133 void draw(SkCanvas*,
1134 const GrClip*,
1135 const SkMatrixProvider& viewMatrix,
1136 SkPoint drawOrigin,
1137 const SkPaint& paint,
1138 skgpu::v1::SurfaceDrawContext*) const override;
1139
1140 std::tuple<const GrClip*, GrOp::Owner>
1141 makeAtlasTextOp(const GrClip*,
1142 const SkMatrixProvider& viewMatrix,
1143 SkPoint drawOrigin,
1144 const SkPaint&,
1145 skgpu::v1::SurfaceDrawContext*,
1146 GrAtlasSubRunOwner) const override;
1147
blobCast() const1148 const GrBlobSubRun* blobCast() const override { return this; }
unflattenSize() const1149 int unflattenSize() const override { return 0; }
1150
1151 bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const override;
1152
1153 const GrAtlasSubRun* testingOnly_atlasSubRun() const override;
1154
1155 void testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) const override;
1156
1157 std::tuple<bool, int> regenerateAtlas(int begin, int end, GrMeshDrawTarget*) const override;
1158
1159 void fillVertexData(
1160 void* vertexDst, int offset, int count,
1161 GrColor color,
1162 const SkMatrix& drawMatrix, SkPoint drawOrigin,
1163 SkIRect clip) const override;
1164
1165 size_t vertexStride(const SkMatrix& drawMatrix) const override;
1166 int glyphCount() const override;
1167
1168 protected:
subRunType() const1169 SubRunType subRunType() const override { return kTransformMask; }
doFlatten(SkWriteBuffer & buffer) const1170 void doFlatten(SkWriteBuffer& buffer) const override {
1171 SK_ABORT("Not implemented.");
1172 }
1173
1174 private:
1175 // The rectangle that surrounds all the glyph bounding boxes in device space.
1176 SkRect deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const;
1177
1178 const TransformedMaskVertexFiller fVertexFiller;
1179
1180 const GrTextReferenceFrame* const fReferenceFrame;
1181
1182 // The bounds in source space. The bounds are the joined rectangles of all the glyphs.
1183 const SkRect fVertexBounds;
1184 const SkSpan<const VertexData> fVertexData;
1185
1186 // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
1187 // be single threaded.
1188 mutable GrGlyphVector fGlyphs;
1189 };
1190
TransformedMaskSubRun(const GrTextReferenceFrame * referenceFrame,GrMaskFormat format,SkScalar strikeToSourceScale,const SkRect & bounds,SkSpan<const VertexData> vertexData,GrGlyphVector && glyphs)1191 TransformedMaskSubRun::TransformedMaskSubRun(const GrTextReferenceFrame* referenceFrame,
1192 GrMaskFormat format,
1193 SkScalar strikeToSourceScale,
1194 const SkRect& bounds,
1195 SkSpan<const VertexData> vertexData,
1196 GrGlyphVector&& glyphs)
1197 : fVertexFiller{format, 0, strikeToSourceScale}
1198 , fReferenceFrame{referenceFrame}
1199 , fVertexBounds{bounds}
1200 , fVertexData{vertexData}
1201 , fGlyphs{std::move(glyphs)} { }
1202
Make(const GrTextReferenceFrame * referenceFrame,const SkZip<SkGlyphVariant,SkPoint> & accepted,sk_sp<SkStrike> && strike,SkScalar strikeToSourceScale,GrMaskFormat format,GrSubRunAllocator * alloc)1203 GrSubRunOwner TransformedMaskSubRun::Make(const GrTextReferenceFrame* referenceFrame,
1204 const SkZip<SkGlyphVariant, SkPoint>& accepted,
1205 sk_sp<SkStrike>&& strike,
1206 SkScalar strikeToSourceScale,
1207 GrMaskFormat format,
1208 GrSubRunAllocator* alloc) {
1209 SkRect bounds = SkRectPriv::MakeLargestInverted();
1210
1211 SkSpan<VertexData> vertexData = alloc->makePODArray<VertexData>(
1212 accepted,
1213 [&](auto e) {
1214 auto [variant, pos] = e;
1215 const SkGlyph* skGlyph = variant;
1216 int16_t l = skGlyph->left(),
1217 t = skGlyph->top(),
1218 r = l + skGlyph->width(),
1219 b = t + skGlyph->height();
1220 SkPoint lt = SkPoint::Make(l, t) * strikeToSourceScale + pos,
1221 rb = SkPoint::Make(r, b) * strikeToSourceScale + pos;
1222
1223 bounds.joinPossiblyEmptyRect(SkRect::MakeLTRB(lt.x(), lt.y(), rb.x(), rb.y()));
1224 return VertexData{pos, {l, t, r, b}};
1225 });
1226
1227 return alloc->makeUnique<TransformedMaskSubRun>(
1228 referenceFrame, format, strikeToSourceScale, bounds, vertexData,
1229 GrGlyphVector::Make(std::move(strike), accepted.get<0>(), alloc));
1230 }
1231
draw(SkCanvas *,const GrClip * clip,const SkMatrixProvider & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,skgpu::v1::SurfaceDrawContext * sdc) const1232 void TransformedMaskSubRun::draw(SkCanvas*,
1233 const GrClip* clip,
1234 const SkMatrixProvider& viewMatrix,
1235 SkPoint drawOrigin,
1236 const SkPaint& paint,
1237 skgpu::v1::SurfaceDrawContext* sdc) const {
1238 auto[drawingClip, op] = this->makeAtlasTextOp(
1239 clip, viewMatrix, drawOrigin, paint, sdc, nullptr);
1240 if (op != nullptr) {
1241 sdc->addDrawOp(drawingClip, std::move(op));
1242 }
1243 }
1244
1245 std::tuple<const GrClip*, GrOp::Owner>
makeAtlasTextOp(const GrClip * clip,const SkMatrixProvider & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,skgpu::v1::SurfaceDrawContext * sdc,GrAtlasSubRunOwner) const1246 TransformedMaskSubRun::makeAtlasTextOp(const GrClip* clip,
1247 const SkMatrixProvider& viewMatrix,
1248 SkPoint drawOrigin,
1249 const SkPaint& paint,
1250 skgpu::v1::SurfaceDrawContext* sdc,
1251 GrAtlasSubRunOwner) const {
1252 SkASSERT(this->glyphCount() != 0);
1253
1254 const SkMatrix& drawMatrix = viewMatrix.localToDevice();
1255
1256 GrPaint grPaint;
1257 SkPMColor4f drawingColor = calculate_colors(
1258 sdc, paint, viewMatrix, fVertexFiller.grMaskType(), &grPaint);
1259
1260 auto geometry = AtlasTextOp::Geometry::MakeForBlob(*this,
1261 drawMatrix,
1262 drawOrigin,
1263 SkIRect::MakeEmpty(),
1264 sk_ref_sp(fReferenceFrame),
1265 drawingColor,
1266 sdc->arenaAlloc());
1267
1268 GrRecordingContext* const rContext = sdc->recordingContext();
1269 GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext,
1270 fVertexFiller.opMaskType(),
1271 true,
1272 this->glyphCount(),
1273 this->deviceRect(drawMatrix, drawOrigin),
1274 geometry,
1275 std::move(grPaint));
1276 return {clip, std::move(op)};
1277 }
1278
1279 // If we are not scaling the cache entry to be larger, than a cache with smaller glyphs may be
1280 // better.
canReuse(const SkPaint & paint,const SkMatrix & positionMatrix) const1281 bool TransformedMaskSubRun::canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const {
1282 if (fReferenceFrame->initialPositionMatrix().getMaxScale() < 1) {
1283 return false;
1284 }
1285 return true;
1286 }
1287
testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache * cache) const1288 void TransformedMaskSubRun::testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) const {
1289 fGlyphs.packedGlyphIDToGrGlyph(cache);
1290 }
1291
regenerateAtlas(int begin,int end,GrMeshDrawTarget * target) const1292 std::tuple<bool, int> TransformedMaskSubRun::regenerateAtlas(int begin, int end,
1293 GrMeshDrawTarget* target) const {
1294 return fGlyphs.regenerateAtlas(begin, end, fVertexFiller.grMaskType(), 1, target, true);
1295 }
1296
fillVertexData(void * vertexDst,int offset,int count,GrColor color,const SkMatrix & drawMatrix,SkPoint drawOrigin,SkIRect clip) const1297 void TransformedMaskSubRun::fillVertexData(void* vertexDst, int offset, int count,
1298 GrColor color,
1299 const SkMatrix& drawMatrix, SkPoint drawOrigin,
1300 SkIRect clip) const {
1301 const SkMatrix positionMatrix = position_matrix(drawMatrix, drawOrigin);
1302 fVertexFiller.fillVertexData(fGlyphs.glyphs().subspan(offset, count),
1303 fVertexData.subspan(offset, count),
1304 color,
1305 positionMatrix,
1306 clip,
1307 vertexDst);
1308 }
1309
vertexStride(const SkMatrix & drawMatrix) const1310 size_t TransformedMaskSubRun::vertexStride(const SkMatrix& drawMatrix) const {
1311 return fVertexFiller.vertexStride(drawMatrix);
1312 }
1313
glyphCount() const1314 int TransformedMaskSubRun::glyphCount() const {
1315 return SkCount(fVertexData);
1316 }
1317
deviceRect(const SkMatrix & drawMatrix,SkPoint drawOrigin) const1318 SkRect TransformedMaskSubRun::deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const {
1319 SkRect outBounds = fVertexBounds;
1320 outBounds.offset(drawOrigin);
1321 return drawMatrix.mapRect(outBounds);
1322 }
1323
testingOnly_atlasSubRun() const1324 const GrAtlasSubRun* TransformedMaskSubRun::testingOnly_atlasSubRun() const {
1325 return this;
1326 }
1327
1328 // -- SDFTSubRun -----------------------------------------------------------------------------------
1329 class SDFTSubRun final : public GrSubRun, public GrBlobSubRun, public GrAtlasSubRun {
1330 public:
1331 using VertexData = TransformedMaskVertexFiller::PositionAndExtent;
1332
1333 SDFTSubRun(const GrTextReferenceFrame* referenceFrame,
1334 SkScalar strikeToSource,
1335 SkRect vertexBounds,
1336 SkSpan<const VertexData> vertexData,
1337 GrGlyphVector&& glyphs,
1338 bool useLCDText,
1339 bool antiAliased,
1340 const GrSDFTMatrixRange& matrixRange);
1341
1342 static GrSubRunOwner Make(const GrTextReferenceFrame* referenceFrame,
1343 const SkZip<SkGlyphVariant, SkPoint>& accepted,
1344 const SkFont& runFont,
1345 sk_sp<SkStrike>&& strike,
1346 SkScalar strikeToSourceScale,
1347 const GrSDFTMatrixRange& matrixRange,
1348 GrSubRunAllocator* alloc);
1349
MakeFromBuffer(const GrTextReferenceFrame * referenceFrame,SkReadBuffer & buffer,GrSubRunAllocator * alloc,const SkStrikeClient * client)1350 static GrSubRunOwner MakeFromBuffer(const GrTextReferenceFrame* referenceFrame,
1351 SkReadBuffer& buffer,
1352 GrSubRunAllocator* alloc,
1353 const SkStrikeClient* client) {
1354 return nullptr;
1355 }
1356
1357 void draw(SkCanvas*,
1358 const GrClip*,
1359 const SkMatrixProvider& viewMatrix,
1360 SkPoint drawOrigin,
1361 const SkPaint&,
1362 skgpu::v1::SurfaceDrawContext*) const override;
1363
1364 std::tuple<const GrClip*, GrOp::Owner>
1365 makeAtlasTextOp(const GrClip*,
1366 const SkMatrixProvider& viewMatrix,
1367 SkPoint drawOrigin,
1368 const SkPaint&,
1369 skgpu::v1::SurfaceDrawContext*,
1370 GrAtlasSubRunOwner) const override;
1371
blobCast() const1372 const GrBlobSubRun* blobCast() const override { return this; }
unflattenSize() const1373 int unflattenSize() const override { return 0; }
1374
1375 bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const override;
1376
1377 const GrAtlasSubRun* testingOnly_atlasSubRun() const override;
1378
1379 void testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) const override;
1380
1381 std::tuple<bool, int> regenerateAtlas(int begin, int end, GrMeshDrawTarget*) const override;
1382
1383 void fillVertexData(
1384 void* vertexDst, int offset, int count,
1385 GrColor color,
1386 const SkMatrix& drawMatrix, SkPoint drawOrigin,
1387 SkIRect clip) const override;
1388
1389 size_t vertexStride(const SkMatrix& drawMatrix) const override;
1390 int glyphCount() const override;
1391
1392 protected:
subRunType() const1393 SubRunType subRunType() const override { return kSDFT; }
doFlatten(SkWriteBuffer & buffer) const1394 void doFlatten(SkWriteBuffer& buffer) const override {
1395 SK_ABORT("Not implemented.");
1396 }
1397
1398 private:
1399 // The rectangle that surrounds all the glyph bounding boxes in device space.
1400 SkRect deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const;
1401
1402 const GrTextReferenceFrame* const fReferenceFrame;
1403
1404 const TransformedMaskVertexFiller fVertexFiller;
1405
1406 // The bounds in source space. The bounds are the joined rectangles of all the glyphs.
1407 const SkRect fVertexBounds;
1408 const SkSpan<const VertexData> fVertexData;
1409
1410 // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
1411 // be single threaded.
1412 mutable GrGlyphVector fGlyphs;
1413
1414 const bool fUseLCDText;
1415 const bool fAntiAliased;
1416 const GrSDFTMatrixRange fMatrixRange;
1417 };
1418
SDFTSubRun(const GrTextReferenceFrame * referenceFrame,SkScalar strikeToSource,SkRect vertexBounds,SkSpan<const VertexData> vertexData,GrGlyphVector && glyphs,bool useLCDText,bool antiAliased,const GrSDFTMatrixRange & matrixRange)1419 SDFTSubRun::SDFTSubRun(const GrTextReferenceFrame* referenceFrame,
1420 SkScalar strikeToSource,
1421 SkRect vertexBounds,
1422 SkSpan<const VertexData> vertexData,
1423 GrGlyphVector&& glyphs,
1424 bool useLCDText,
1425 bool antiAliased,
1426 const GrSDFTMatrixRange& matrixRange)
1427 : fReferenceFrame{referenceFrame}
1428 , fVertexFiller{kA8_GrMaskFormat, SK_DistanceFieldInset, strikeToSource}
1429 , fVertexBounds{vertexBounds}
1430 , fVertexData{vertexData}
1431 , fGlyphs{std::move(glyphs)}
1432 , fUseLCDText{useLCDText}
1433 , fAntiAliased{antiAliased}
1434 , fMatrixRange{matrixRange} {}
1435
has_some_antialiasing(const SkFont & font)1436 bool has_some_antialiasing(const SkFont& font ) {
1437 SkFont::Edging edging = font.getEdging();
1438 return edging == SkFont::Edging::kAntiAlias
1439 || edging == SkFont::Edging::kSubpixelAntiAlias;
1440 }
1441
Make(const GrTextReferenceFrame * referenceFrame,const SkZip<SkGlyphVariant,SkPoint> & accepted,const SkFont & runFont,sk_sp<SkStrike> && strike,SkScalar strikeToSourceScale,const GrSDFTMatrixRange & matrixRange,GrSubRunAllocator * alloc)1442 GrSubRunOwner SDFTSubRun::Make(const GrTextReferenceFrame* referenceFrame,
1443 const SkZip<SkGlyphVariant, SkPoint>& accepted,
1444 const SkFont& runFont,
1445 sk_sp<SkStrike>&& strike,
1446 SkScalar strikeToSourceScale,
1447 const GrSDFTMatrixRange& matrixRange,
1448 GrSubRunAllocator* alloc) {
1449 SkRect bounds = SkRectPriv::MakeLargestInverted();
1450 auto mapper = [&](const auto& d) {
1451 auto& [variant, pos] = d;
1452 const SkGlyph* skGlyph = variant;
1453 int16_t l = skGlyph->left(),
1454 t = skGlyph->top(),
1455 r = l + skGlyph->width(),
1456 b = t + skGlyph->height();
1457 SkPoint lt = SkPoint::Make(l, t) * strikeToSourceScale + pos,
1458 rb = SkPoint::Make(r, b) * strikeToSourceScale + pos;
1459
1460 bounds.joinPossiblyEmptyRect(SkRect::MakeLTRB(lt.x(), lt.y(), rb.x(), rb.y()));
1461 return VertexData{pos, {l, t, r, b}};
1462 };
1463
1464 SkSpan<VertexData> vertexData = alloc->makePODArray<VertexData>(accepted, mapper);
1465
1466 return alloc->makeUnique<SDFTSubRun>(
1467 referenceFrame,
1468 strikeToSourceScale,
1469 bounds,
1470 vertexData,
1471 GrGlyphVector::Make(std::move(strike), accepted.get<0>(), alloc),
1472 runFont.getEdging() == SkFont::Edging::kSubpixelAntiAlias,
1473 has_some_antialiasing(runFont),
1474 matrixRange);
1475 }
1476
draw(SkCanvas *,const GrClip * clip,const SkMatrixProvider & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,skgpu::v1::SurfaceDrawContext * sdc) const1477 void SDFTSubRun::draw(SkCanvas*,
1478 const GrClip* clip,
1479 const SkMatrixProvider& viewMatrix,
1480 SkPoint drawOrigin,
1481 const SkPaint& paint,
1482 skgpu::v1::SurfaceDrawContext* sdc) const {
1483 auto[drawingClip, op] = this->makeAtlasTextOp(
1484 clip, viewMatrix, drawOrigin, paint, sdc, nullptr);
1485 if (op != nullptr) {
1486 sdc->addDrawOp(drawingClip, std::move(op));
1487 }
1488 }
1489
calculate_sdf_parameters(const skgpu::v1::SurfaceDrawContext & sdc,const SkMatrix & drawMatrix,bool useLCDText,bool isAntiAliased)1490 static std::tuple<AtlasTextOp::MaskType, uint32_t, bool> calculate_sdf_parameters(
1491 const skgpu::v1::SurfaceDrawContext& sdc,
1492 const SkMatrix& drawMatrix,
1493 bool useLCDText,
1494 bool isAntiAliased) {
1495 const GrColorInfo& colorInfo = sdc.colorInfo();
1496 const SkSurfaceProps& props = sdc.surfaceProps();
1497 bool isBGR = SkPixelGeometryIsBGR(props.pixelGeometry());
1498 bool isLCD = useLCDText && SkPixelGeometryIsH(props.pixelGeometry());
1499 using MT = AtlasTextOp::MaskType;
1500 MT maskType = !isAntiAliased ? MT::kAliasedDistanceField
1501 : isLCD ? (isBGR ? MT::kLCDBGRDistanceField
1502 : MT::kLCDDistanceField)
1503 : MT::kGrayscaleDistanceField;
1504
1505 bool useGammaCorrectDistanceTable = colorInfo.isLinearlyBlended();
1506 uint32_t DFGPFlags = drawMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
1507 DFGPFlags |= drawMatrix.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0;
1508 DFGPFlags |= useGammaCorrectDistanceTable ? kGammaCorrect_DistanceFieldEffectFlag : 0;
1509 DFGPFlags |= MT::kAliasedDistanceField == maskType ? kAliased_DistanceFieldEffectFlag : 0;
1510
1511 if (isLCD) {
1512 DFGPFlags |= kUseLCD_DistanceFieldEffectFlag;
1513 DFGPFlags |= MT::kLCDBGRDistanceField == maskType ? kBGR_DistanceFieldEffectFlag : 0;
1514 }
1515 return {maskType, DFGPFlags, useGammaCorrectDistanceTable};
1516 }
1517
1518 std::tuple<const GrClip*, GrOp::Owner >
makeAtlasTextOp(const GrClip * clip,const SkMatrixProvider & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,skgpu::v1::SurfaceDrawContext * sdc,GrAtlasSubRunOwner) const1519 SDFTSubRun::makeAtlasTextOp(const GrClip* clip,
1520 const SkMatrixProvider& viewMatrix,
1521 SkPoint drawOrigin,
1522 const SkPaint& paint,
1523 skgpu::v1::SurfaceDrawContext* sdc,
1524 GrAtlasSubRunOwner) const {
1525 SkASSERT(this->glyphCount() != 0);
1526 SkASSERT(!viewMatrix.localToDevice().hasPerspective());
1527
1528 const SkMatrix& drawMatrix = viewMatrix.localToDevice();
1529
1530 GrPaint grPaint;
1531 SkPMColor4f drawingColor = calculate_colors(sdc, paint, viewMatrix, kA8_GrMaskFormat, &grPaint);
1532
1533 auto [maskType, DFGPFlags, useGammaCorrectDistanceTable] =
1534 calculate_sdf_parameters(*sdc, drawMatrix, fUseLCDText, fAntiAliased);
1535
1536 auto geometry = AtlasTextOp::Geometry::MakeForBlob(*this,
1537 drawMatrix,
1538 drawOrigin,
1539 SkIRect::MakeEmpty(),
1540 sk_ref_sp(fReferenceFrame),
1541 drawingColor,
1542 sdc->arenaAlloc());
1543
1544 GrRecordingContext* const rContext = sdc->recordingContext();
1545 GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext,
1546 maskType,
1547 true,
1548 this->glyphCount(),
1549 this->deviceRect(drawMatrix, drawOrigin),
1550 SkPaintPriv::ComputeLuminanceColor(paint),
1551 useGammaCorrectDistanceTable,
1552 DFGPFlags,
1553 geometry,
1554 std::move(grPaint));
1555
1556 return {clip, std::move(op)};
1557 }
1558
canReuse(const SkPaint & paint,const SkMatrix & positionMatrix) const1559 bool SDFTSubRun::canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const {
1560 return fMatrixRange.matrixInRange(positionMatrix);
1561 }
1562
testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache * cache) const1563 void SDFTSubRun::testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) const {
1564 fGlyphs.packedGlyphIDToGrGlyph(cache);
1565 }
1566
regenerateAtlas(int begin,int end,GrMeshDrawTarget * target) const1567 std::tuple<bool, int> SDFTSubRun::regenerateAtlas(
1568 int begin, int end, GrMeshDrawTarget *target) const {
1569 return fGlyphs.regenerateAtlas(begin, end, kA8_GrMaskFormat, SK_DistanceFieldInset, target);
1570 }
1571
vertexStride(const SkMatrix & drawMatrix) const1572 size_t SDFTSubRun::vertexStride(const SkMatrix& drawMatrix) const {
1573 return sizeof(Mask2DVertex);
1574 }
1575
fillVertexData(void * vertexDst,int offset,int count,GrColor color,const SkMatrix & drawMatrix,SkPoint drawOrigin,SkIRect clip) const1576 void SDFTSubRun::fillVertexData(
1577 void *vertexDst, int offset, int count,
1578 GrColor color,
1579 const SkMatrix& drawMatrix, SkPoint drawOrigin,
1580 SkIRect clip) const {
1581 const SkMatrix positionMatrix = position_matrix(drawMatrix, drawOrigin);
1582
1583 fVertexFiller.fillVertexData(fGlyphs.glyphs().subspan(offset, count),
1584 fVertexData.subspan(offset, count),
1585 color,
1586 positionMatrix,
1587 clip,
1588 vertexDst);
1589 }
1590
glyphCount() const1591 int SDFTSubRun::glyphCount() const {
1592 return SkCount(fVertexData);
1593 }
1594
deviceRect(const SkMatrix & drawMatrix,SkPoint drawOrigin) const1595 SkRect SDFTSubRun::deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const {
1596 SkRect outBounds = fVertexBounds;
1597 outBounds.offset(drawOrigin);
1598 return drawMatrix.mapRect(outBounds);
1599 }
1600
testingOnly_atlasSubRun() const1601 const GrAtlasSubRun* SDFTSubRun::testingOnly_atlasSubRun() const {
1602 return this;
1603 }
1604
1605 template<typename AddSingleMaskFormat>
add_multi_mask_format(AddSingleMaskFormat addSingleMaskFormat,const SkZip<SkGlyphVariant,SkPoint> & accepted,sk_sp<SkStrike> && strike)1606 void add_multi_mask_format(
1607 AddSingleMaskFormat addSingleMaskFormat,
1608 const SkZip<SkGlyphVariant, SkPoint>& accepted,
1609 sk_sp<SkStrike>&& strike) {
1610 if (accepted.empty()) { return; }
1611
1612 auto glyphSpan = accepted.get<0>();
1613 const SkGlyph* glyph = glyphSpan[0];
1614 GrMaskFormat format = GrGlyph::FormatFromSkGlyph(glyph->maskFormat());
1615 size_t startIndex = 0;
1616 for (size_t i = 1; i < accepted.size(); i++) {
1617 glyph = glyphSpan[i];
1618 GrMaskFormat nextFormat = GrGlyph::FormatFromSkGlyph(glyph->maskFormat());
1619 if (format != nextFormat) {
1620 auto glyphsWithSameFormat = accepted.subspan(startIndex, i - startIndex);
1621 // Take a ref on the strike. This should rarely happen.
1622 addSingleMaskFormat(glyphsWithSameFormat, format, sk_sp<SkStrike>(strike));
1623 format = nextFormat;
1624 startIndex = i;
1625 }
1626 }
1627 auto glyphsWithSameFormat = accepted.last(accepted.size() - startIndex);
1628 addSingleMaskFormat(glyphsWithSameFormat, format, std::move(strike));
1629 }
1630
1631 } // namespace
1632
1633 // -- GrTextBlob::Key ------------------------------------------------------------------------------
1634
compute_canonical_color(const SkPaint & paint,bool lcd)1635 static SkColor compute_canonical_color(const SkPaint& paint, bool lcd) {
1636 SkColor canonicalColor = SkPaintPriv::ComputeLuminanceColor(paint);
1637 if (lcd) {
1638 // This is the correct computation for canonicalColor, but there are tons of cases where LCD
1639 // can be modified. For now we just regenerate if any run in a textblob has LCD.
1640 // TODO figure out where all of these modifications are and see if we can incorporate that
1641 // logic at a higher level *OR* use sRGB
1642 //canonicalColor = SkMaskGamma::CanonicalColor(canonicalColor);
1643
1644 // TODO we want to figure out a way to be able to use the canonical color on LCD text,
1645 // see the note above. We pick a placeholder value for LCD text to ensure we always match
1646 // the same key
1647 return SK_ColorTRANSPARENT;
1648 } else {
1649 // A8, though can have mixed BMP text but it shouldn't matter because BMP text won't have
1650 // gamma corrected masks anyways, nor color
1651 U8CPU lum = SkComputeLuminance(SkColorGetR(canonicalColor),
1652 SkColorGetG(canonicalColor),
1653 SkColorGetB(canonicalColor));
1654 // reduce to our finite number of bits
1655 canonicalColor = SkMaskGamma::CanonicalColor(SkColorSetRGB(lum, lum, lum));
1656 }
1657 return canonicalColor;
1658 }
1659
Make(const SkGlyphRunList & glyphRunList,const SkPaint & paint,const SkSurfaceProps & surfaceProps,const GrColorInfo & colorInfo,const SkMatrix & drawMatrix,const GrSDFTControl & control)1660 auto GrTextBlob::Key::Make(const SkGlyphRunList& glyphRunList,
1661 const SkPaint& paint,
1662 const SkSurfaceProps& surfaceProps,
1663 const GrColorInfo& colorInfo,
1664 const SkMatrix& drawMatrix,
1665 const GrSDFTControl& control) -> std::tuple<bool, Key> {
1666 SkMaskFilterBase::BlurRec blurRec;
1667 // It might be worth caching these things, but its not clear at this time
1668 // TODO for animated mask filters, this will fill up our cache. We need a safeguard here
1669 const SkMaskFilter* maskFilter = paint.getMaskFilter();
1670 bool canCache = glyphRunList.canCache() &&
1671 !(paint.getPathEffect() ||
1672 (maskFilter && !as_MFB(maskFilter)->asABlur(&blurRec)));
1673
1674 // If we're doing linear blending, then we can disable the gamma hacks.
1675 // Otherwise, leave them on. In either case, we still want the contrast boost:
1676 // TODO: Can we be even smarter about mask gamma based on the dest transfer function?
1677 SkScalerContextFlags scalerContextFlags = colorInfo.isLinearlyBlended()
1678 ? SkScalerContextFlags::kBoostContrast
1679 : SkScalerContextFlags::kFakeGammaAndBoostContrast;
1680
1681 GrTextBlob::Key key;
1682 if (canCache) {
1683 bool hasLCD = glyphRunList.anyRunsLCD();
1684
1685 // We canonicalize all non-lcd draws to use kUnknown_SkPixelGeometry
1686 SkPixelGeometry pixelGeometry =
1687 hasLCD ? surfaceProps.pixelGeometry() : kUnknown_SkPixelGeometry;
1688
1689 GrColor canonicalColor = compute_canonical_color(paint, hasLCD);
1690
1691 key.fPixelGeometry = pixelGeometry;
1692 key.fUniqueID = glyphRunList.uniqueID();
1693 key.fStyle = paint.getStyle();
1694 if (key.fStyle != SkPaint::kFill_Style) {
1695 key.fFrameWidth = paint.getStrokeWidth();
1696 key.fMiterLimit = paint.getStrokeMiter();
1697 key.fJoin = paint.getStrokeJoin();
1698 }
1699 key.fHasBlur = maskFilter != nullptr;
1700 if (key.fHasBlur) {
1701 key.fBlurRec = blurRec;
1702 }
1703 key.fCanonicalColor = canonicalColor;
1704 key.fScalerContextFlags = scalerContextFlags;
1705
1706 // Do any runs use direct drawing types?.
1707 key.fHasSomeDirectSubRuns = false;
1708 for (auto& run : glyphRunList) {
1709 SkScalar approximateDeviceTextSize =
1710 SkFontPriv::ApproximateTransformedTextSize(run.font(), drawMatrix);
1711 key.fHasSomeDirectSubRuns |= control.isDirect(approximateDeviceTextSize, paint);
1712 }
1713
1714 if (key.fHasSomeDirectSubRuns) {
1715 // Store the fractional offset of the position. We know that the matrix can't be
1716 // perspective at this point.
1717 SkPoint mappedOrigin = drawMatrix.mapOrigin();
1718 key.fPositionMatrix = drawMatrix;
1719 key.fPositionMatrix.setTranslateX(
1720 mappedOrigin.x() - SkScalarFloorToScalar(mappedOrigin.x()));
1721 key.fPositionMatrix.setTranslateY(
1722 mappedOrigin.y() - SkScalarFloorToScalar(mappedOrigin.y()));
1723 } else {
1724 // For path and SDFT, the matrix doesn't matter.
1725 key.fPositionMatrix = SkMatrix::I();
1726 }
1727 }
1728
1729 return {canCache, key};
1730 }
1731
operator ==(const GrTextBlob::Key & that) const1732 bool GrTextBlob::Key::operator==(const GrTextBlob::Key& that) const {
1733 if (fUniqueID != that.fUniqueID) { return false; }
1734 if (fCanonicalColor != that.fCanonicalColor) { return false; }
1735 if (fStyle != that.fStyle) { return false; }
1736 if (fStyle != SkPaint::kFill_Style) {
1737 if (fFrameWidth != that.fFrameWidth ||
1738 fMiterLimit != that.fMiterLimit ||
1739 fJoin != that.fJoin) {
1740 return false;
1741 }
1742 }
1743 if (fPixelGeometry != that.fPixelGeometry) { return false; }
1744 if (fHasBlur != that.fHasBlur) { return false; }
1745 if (fHasBlur) {
1746 if (fBlurRec.fStyle != that.fBlurRec.fStyle || fBlurRec.fSigma != that.fBlurRec.fSigma) {
1747 return false;
1748 }
1749 }
1750 if (fScalerContextFlags != that.fScalerContextFlags) { return false; }
1751
1752 // Just punt on perspective.
1753 if (fPositionMatrix.hasPerspective()) {
1754 return false;
1755 }
1756
1757 if (fHasSomeDirectSubRuns != that.fHasSomeDirectSubRuns) {
1758 return false;
1759 }
1760
1761 if (fHasSomeDirectSubRuns) {
1762 auto [compatible, _] = can_use_direct(fPositionMatrix, that.fPositionMatrix);
1763 return compatible;
1764 }
1765
1766 return true;
1767 }
1768
1769 // -- GrTextBlob -----------------------------------------------------------------------------------
operator delete(void * p)1770 void GrTextBlob::operator delete(void* p) { ::operator delete(p); }
operator new(size_t)1771 void* GrTextBlob::operator new(size_t) { SK_ABORT("All blobs are created by placement new."); }
operator new(size_t,void * p)1772 void* GrTextBlob::operator new(size_t, void* p) { return p; }
1773
1774 GrTextBlob::~GrTextBlob() = default;
1775
Make(const SkGlyphRunList & glyphRunList,const SkPaint & paint,const SkMatrix & positionMatrix,bool supportBilerpAtlas,const GrSDFTControl & control,SkGlyphRunListPainter * painter)1776 sk_sp<GrTextBlob> GrTextBlob::Make(const SkGlyphRunList& glyphRunList,
1777 const SkPaint& paint,
1778 const SkMatrix& positionMatrix,
1779 bool supportBilerpAtlas,
1780 const GrSDFTControl& control,
1781 SkGlyphRunListPainter* painter) {
1782 // The difference in alignment from the per-glyph data to the SubRun;
1783 constexpr size_t alignDiff =
1784 alignof(DirectMaskSubRun) - alignof(DirectMaskSubRun::DevicePosition);
1785 constexpr size_t vertexDataToSubRunPadding = alignDiff > 0 ? alignDiff : 0;
1786 size_t totalGlyphCount = glyphRunList.totalGlyphCount();
1787
1788 // The neededForSubRun is optimized for DirectMaskSubRun which is by far the most common case.
1789 size_t bytesNeededForSubRun = GrBagOfBytes::PlatformMinimumSizeWithOverhead(
1790 totalGlyphCount * sizeof(DirectMaskSubRun::DevicePosition)
1791 + GrGlyphVector::GlyphVectorSize(totalGlyphCount)
1792 + glyphRunList.runCount() * (sizeof(DirectMaskSubRun) + vertexDataToSubRunPadding),
1793 alignof(GrTextBlob));
1794
1795 size_t allocationSize = sizeof(GrTextBlob) + bytesNeededForSubRun;
1796
1797 void* allocation = ::operator new (allocationSize);
1798
1799 SkColor initialLuminance = SkPaintPriv::ComputeLuminanceColor(paint);
1800 sk_sp<GrTextBlob> blob{
1801 new (allocation) GrTextBlob(
1802 bytesNeededForSubRun, supportBilerpAtlas, positionMatrix, initialLuminance)};
1803
1804 const uint64_t uniqueID = glyphRunList.uniqueID();
1805 for (auto& glyphRun : glyphRunList) {
1806 painter->processGlyphRun(blob.get(),
1807 glyphRun,
1808 positionMatrix,
1809 paint,
1810 control,
1811 "GrTextBlob",
1812 uniqueID);
1813 }
1814
1815 return blob;
1816 }
1817
addKey(const Key & key)1818 void GrTextBlob::addKey(const Key& key) {
1819 fKey = key;
1820 }
1821
hasPerspective() const1822 bool GrTextBlob::hasPerspective() const { return fInitialPositionMatrix.hasPerspective(); }
1823
canReuse(const SkPaint & paint,const SkMatrix & positionMatrix) const1824 bool GrTextBlob::canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const {
1825 // A singular matrix will create a GrTextBlob with no SubRuns, but unknown glyphs can
1826 // also cause empty runs. If there are no subRuns or some glyphs were excluded or perspective,
1827 // then regenerate when the matrices don't match.
1828 if ((fSubRunList.isEmpty() || fSomeGlyphsExcluded || hasPerspective()) &&
1829 fInitialPositionMatrix != positionMatrix)
1830 {
1831 return false;
1832 }
1833
1834 // If we have LCD text then our canonical color will be set to transparent, in this case we have
1835 // to regenerate the blob on any color change
1836 // We use the grPaint to get any color filter effects
1837 if (fKey.fCanonicalColor == SK_ColorTRANSPARENT &&
1838 fInitialLuminance != SkPaintPriv::ComputeLuminanceColor(paint)) {
1839 return false;
1840 }
1841
1842 for (const GrSubRun& subRun : fSubRunList) {
1843 if (!subRun.blobCast()->canReuse(paint, positionMatrix)) {
1844 return false;
1845 }
1846 }
1847
1848 return true;
1849 }
1850
key() const1851 const GrTextBlob::Key& GrTextBlob::key() const { return fKey; }
size() const1852 size_t GrTextBlob::size() const { return fSize; }
1853
draw(SkCanvas * canvas,const GrClip * clip,const SkMatrixProvider & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,skgpu::v1::SurfaceDrawContext * sdc)1854 void GrTextBlob::draw(SkCanvas* canvas,
1855 const GrClip* clip,
1856 const SkMatrixProvider& viewMatrix,
1857 SkPoint drawOrigin,
1858 const SkPaint& paint,
1859 skgpu::v1::SurfaceDrawContext* sdc) {
1860 for (const GrSubRun& subRun : fSubRunList) {
1861 subRun.draw(canvas, clip, viewMatrix, drawOrigin, paint, sdc);
1862 }
1863 }
1864
testingOnlyFirstSubRun() const1865 const GrAtlasSubRun* GrTextBlob::testingOnlyFirstSubRun() const {
1866 if (fSubRunList.isEmpty()) {
1867 return nullptr;
1868 }
1869
1870 return fSubRunList.front().blobCast()->testingOnly_atlasSubRun();
1871 }
1872
GrTextBlob(int allocSize,bool supportBilerpAtlas,const SkMatrix & positionMatrix,SkColor initialLuminance)1873 GrTextBlob::GrTextBlob(int allocSize,
1874 bool supportBilerpAtlas,
1875 const SkMatrix& positionMatrix,
1876 SkColor initialLuminance)
1877 : fAlloc{SkTAddOffset<char>(this, sizeof(GrTextBlob)), allocSize, allocSize/2}
1878 , fSize{allocSize}
1879 , fSupportBilerpAtlas{supportBilerpAtlas}
1880 , fInitialPositionMatrix{positionMatrix}
1881 , fInitialLuminance{initialLuminance} { }
1882
processDeviceMasks(const SkZip<SkGlyphVariant,SkPoint> & accepted,sk_sp<SkStrike> && strike)1883 void GrTextBlob::processDeviceMasks(
1884 const SkZip<SkGlyphVariant, SkPoint>& accepted, sk_sp<SkStrike>&& strike) {
1885 SkASSERT(strike != nullptr);
1886 auto addGlyphsWithSameFormat = [&] (const SkZip<SkGlyphVariant, SkPoint>& accepted,
1887 GrMaskFormat format,
1888 sk_sp<SkStrike>&& runStrike) {
1889 GrSubRunOwner subRun = DirectMaskSubRun::Make(
1890 this, accepted, std::move(runStrike), format, &fAlloc);
1891 if (subRun != nullptr) {
1892 fSubRunList.append(std::move(subRun));
1893 } else {
1894 fSomeGlyphsExcluded = true;
1895 }
1896 };
1897 add_multi_mask_format(addGlyphsWithSameFormat, accepted, std::move(strike));
1898 }
1899
processSourcePaths(const SkZip<SkGlyphVariant,SkPoint> & accepted,const SkFont & runFont,SkScalar strikeToSourceScale)1900 void GrTextBlob::processSourcePaths(const SkZip<SkGlyphVariant, SkPoint>& accepted,
1901 const SkFont& runFont,
1902 SkScalar strikeToSourceScale) {
1903 fSubRunList.append(PathSubRun::Make(
1904 accepted, has_some_antialiasing(runFont), strikeToSourceScale, &fAlloc));
1905 }
1906
processSourceDrawables(const SkZip<SkGlyphVariant,SkPoint> & accepted,const SkFont & runFont,SkScalar strikeToSourceScale)1907 void GrTextBlob::processSourceDrawables(const SkZip<SkGlyphVariant, SkPoint>& accepted,
1908 const SkFont& runFont,
1909 SkScalar strikeToSourceScale) {
1910 fSubRunList.append(make_drawable_sub_run<DrawableSubRun>(
1911 accepted, has_some_antialiasing(runFont), strikeToSourceScale, &fAlloc));
1912 }
1913
processSourceSDFT(const SkZip<SkGlyphVariant,SkPoint> & accepted,sk_sp<SkStrike> && strike,SkScalar strikeToSourceScale,const SkFont & runFont,const GrSDFTMatrixRange & matrixRange)1914 void GrTextBlob::processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& accepted,
1915 sk_sp<SkStrike>&& strike,
1916 SkScalar strikeToSourceScale,
1917 const SkFont& runFont,
1918 const GrSDFTMatrixRange& matrixRange) {
1919 fSubRunList.append(SDFTSubRun::Make(
1920 this, accepted, runFont, std::move(strike), strikeToSourceScale, matrixRange, &fAlloc));
1921 }
1922
processSourceMasks(const SkZip<SkGlyphVariant,SkPoint> & accepted,sk_sp<SkStrike> && strike,SkScalar strikeToSourceScale)1923 void GrTextBlob::processSourceMasks(const SkZip<SkGlyphVariant, SkPoint>& accepted,
1924 sk_sp<SkStrike>&& strike,
1925 SkScalar strikeToSourceScale) {
1926 auto addGlyphsWithSameFormat = [&] (const SkZip<SkGlyphVariant, SkPoint>& accepted,
1927 GrMaskFormat format,
1928 sk_sp<SkStrike>&& runStrike) {
1929 GrSubRunOwner subRun = TransformedMaskSubRun::Make(
1930 this, accepted, std::move(runStrike), strikeToSourceScale, format, &fAlloc);
1931 if (subRun != nullptr) {
1932 fSubRunList.append(std::move(subRun));
1933 } else {
1934 fSomeGlyphsExcluded = true;
1935 }
1936 };
1937 add_multi_mask_format(addGlyphsWithSameFormat, accepted, std::move(strike));
1938 }
1939
1940 // ----------------------------- Begin no cache implementation -------------------------------------
1941 namespace {
1942 // -- DirectMaskSubRunNoCache ----------------------------------------------------------------------
1943 class DirectMaskSubRunNoCache final : public GrAtlasSubRun {
1944 public:
1945 using DevicePosition = skvx::Vec<2, int16_t>;
1946
1947 DirectMaskSubRunNoCache(GrMaskFormat format,
1948 bool supportBilerpAtlas,
1949 const SkRect& bounds,
1950 SkSpan<const DevicePosition> devicePositions,
1951 GrGlyphVector&& glyphs);
1952
1953 static GrAtlasSubRunOwner Make(const SkZip<SkGlyphVariant, SkPoint>& accepted,
1954 sk_sp<SkStrike>&& strike,
1955 GrMaskFormat format,
1956 bool supportBilerpAtlas,
1957 GrSubRunAllocator* alloc);
1958
1959 size_t vertexStride(const SkMatrix& drawMatrix) const override;
1960
1961 int glyphCount() const override;
1962
1963 std::tuple<const GrClip*, GrOp::Owner>
1964 makeAtlasTextOp(const GrClip*,
1965 const SkMatrixProvider& viewMatrix,
1966 SkPoint,
1967 const SkPaint&,
1968 skgpu::v1::SurfaceDrawContext*,
1969 GrAtlasSubRunOwner) const override;
1970
1971 void testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) const override;
1972
1973 std::tuple<bool, int>
1974 regenerateAtlas(int begin, int end, GrMeshDrawTarget*) const override;
1975
1976 void fillVertexData(void* vertexDst, int offset, int count,
1977 GrColor color,
1978 const SkMatrix& drawMatrix, SkPoint drawOrigin,
1979 SkIRect clip) const override;
1980
1981 private:
1982 const GrMaskFormat fMaskFormat;
1983
1984 // Support bilerping from the atlas.
1985 const bool fSupportBilerpAtlas;
1986
1987 // The vertex bounds in device space. The bounds are the joined rectangles of all the glyphs.
1988 const SkRect fGlyphDeviceBounds;
1989 const SkSpan<const DevicePosition> fLeftTopDevicePos;
1990
1991 // Space for geometry
1992 alignas(alignof(AtlasTextOp::Geometry)) char fGeom[sizeof(AtlasTextOp::Geometry)];
1993
1994 // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
1995 // be single threaded.
1996 mutable GrGlyphVector fGlyphs;
1997 };
1998
DirectMaskSubRunNoCache(GrMaskFormat format,bool supportBilerpAtlas,const SkRect & deviceBounds,SkSpan<const DevicePosition> devicePositions,GrGlyphVector && glyphs)1999 DirectMaskSubRunNoCache::DirectMaskSubRunNoCache(GrMaskFormat format,
2000 bool supportBilerpAtlas,
2001 const SkRect& deviceBounds,
2002 SkSpan<const DevicePosition> devicePositions,
2003 GrGlyphVector&& glyphs)
2004 : fMaskFormat{format}
2005 , fSupportBilerpAtlas{supportBilerpAtlas}
2006 , fGlyphDeviceBounds{deviceBounds}
2007 , fLeftTopDevicePos{devicePositions}
2008 , fGlyphs{std::move(glyphs)} { }
2009
Make(const SkZip<SkGlyphVariant,SkPoint> & accepted,sk_sp<SkStrike> && strike,GrMaskFormat format,bool supportBilerpAtlas,GrSubRunAllocator * alloc)2010 GrAtlasSubRunOwner DirectMaskSubRunNoCache::Make(const SkZip<SkGlyphVariant, SkPoint>& accepted,
2011 sk_sp<SkStrike>&& strike,
2012 GrMaskFormat format,
2013 bool supportBilerpAtlas,
2014 GrSubRunAllocator* alloc) {
2015 auto glyphLeftTop = alloc->makePODArray<DevicePosition>(accepted.size());
2016 auto glyphIDs = alloc->makePODArray<GrGlyphVector::Variant>(accepted.size());
2017
2018 // Because this is the direct case, the maximum width or height is the size that fits in the
2019 // atlas. This boundary is checked below to ensure that the call to SkGlyphRect below will
2020 // not overflow.
2021 constexpr SkScalar kMaxPos =
2022 std::numeric_limits<int16_t>::max() - SkStrikeCommon::kSkSideTooBigForAtlas;
2023 SkGlyphRect runBounds = skglyph::empty_rect();
2024 size_t goodPosCount = 0;
2025 for (auto [variant, pos] : accepted) {
2026 auto [x, y] = pos;
2027 // Ensure that the .offset() call below does not overflow. And, at this point none of the
2028 // rectangles are empty because they were culled before the run was created. Basically,
2029 // cull all the glyphs that can't appear on the screen.
2030 if (-kMaxPos < x && x < kMaxPos && -kMaxPos < y && y < kMaxPos) {
2031 const SkGlyph* const skGlyph = variant;
2032 const SkGlyphRect deviceBounds =
2033 skGlyph->glyphRect().offset(SkScalarRoundToInt(x), SkScalarRoundToInt(y));
2034 runBounds = skglyph::rect_union(runBounds, deviceBounds);
2035 glyphLeftTop[goodPosCount] = deviceBounds.topLeft();
2036 glyphIDs[goodPosCount].packedGlyphID = skGlyph->getPackedID();
2037 goodPosCount += 1;
2038 }
2039 }
2040
2041 // Wow! no glyphs are in bounds and had non-empty bounds.
2042 if (goodPosCount == 0) {
2043 return nullptr;
2044 }
2045
2046 SkSpan<const DevicePosition> leftTop{glyphLeftTop, goodPosCount};
2047 return alloc->makeUnique<DirectMaskSubRunNoCache>(
2048 format, supportBilerpAtlas, runBounds.rect(), leftTop,
2049 GrGlyphVector{std::move(strike), {glyphIDs, goodPosCount}});
2050 }
2051
vertexStride(const SkMatrix &) const2052 size_t DirectMaskSubRunNoCache::vertexStride(const SkMatrix&) const {
2053 if (fMaskFormat != kARGB_GrMaskFormat) {
2054 return sizeof(Mask2DVertex);
2055 } else {
2056 return sizeof(ARGB2DVertex);
2057 }
2058 }
2059
glyphCount() const2060 int DirectMaskSubRunNoCache::glyphCount() const {
2061 return SkCount(fGlyphs.glyphs());
2062 }
2063
2064 std::tuple<const GrClip*, GrOp::Owner>
makeAtlasTextOp(const GrClip * clip,const SkMatrixProvider & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,skgpu::v1::SurfaceDrawContext * sdc,GrAtlasSubRunOwner subRunOwner) const2065 DirectMaskSubRunNoCache::makeAtlasTextOp(const GrClip* clip,
2066 const SkMatrixProvider& viewMatrix,
2067 SkPoint drawOrigin,
2068 const SkPaint& paint,
2069 skgpu::v1::SurfaceDrawContext* sdc,
2070 GrAtlasSubRunOwner subRunOwner) const {
2071 SkASSERT(this->glyphCount() != 0);
2072
2073 const SkMatrix& drawMatrix = viewMatrix.localToDevice();
2074
2075 // We can clip geometrically using clipRect and ignore clip when an axis-aligned rectangular
2076 // non-AA clip is used. If clipRect is empty, and clip is nullptr, then there is no clipping
2077 // needed.
2078 const SkRect deviceBounds = SkRect::MakeWH(sdc->width(), sdc->height());
2079 auto [clipMethod, clipRect] = calculate_clip(clip, deviceBounds, fGlyphDeviceBounds);
2080
2081 switch (clipMethod) {
2082 case kClippedOut:
2083 // Returning nullptr as op means skip this op.
2084 return {nullptr, nullptr};
2085 case kUnclipped:
2086 case kGeometryClipped:
2087 // GPU clip is not needed.
2088 clip = nullptr;
2089 break;
2090 case kGPUClipped:
2091 // Use the the GPU clip; clipRect is ignored.
2092 break;
2093 }
2094
2095 if (!clipRect.isEmpty()) { SkASSERT(clip == nullptr); }
2096
2097 GrPaint grPaint;
2098 const SkPMColor4f drawingColor =
2099 calculate_colors(sdc, paint, viewMatrix, fMaskFormat, &grPaint);
2100
2101 GrRecordingContext* const rContext = sdc->recordingContext();
2102
2103 auto geometry = new ((void*)fGeom) AtlasTextOp::Geometry{
2104 *this,
2105 drawMatrix,
2106 drawOrigin,
2107 clipRect,
2108 nullptr,
2109 std::move(subRunOwner),
2110 drawingColor
2111 };
2112
2113 GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext,
2114 op_mask_type(fMaskFormat),
2115 false,
2116 this->glyphCount(),
2117 fGlyphDeviceBounds,
2118 geometry,
2119 std::move(grPaint));
2120
2121 return {clip, std::move(op)};
2122 }
2123
testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache * cache) const2124 void DirectMaskSubRunNoCache::testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) const {
2125 fGlyphs.packedGlyphIDToGrGlyph(cache);
2126 }
2127
2128 std::tuple<bool, int>
regenerateAtlas(int begin,int end,GrMeshDrawTarget * target) const2129 DirectMaskSubRunNoCache::regenerateAtlas(int begin, int end, GrMeshDrawTarget* target) const {
2130 if (fSupportBilerpAtlas) {
2131 return fGlyphs.regenerateAtlas(begin, end, fMaskFormat, 1, target, true);
2132 } else {
2133 return fGlyphs.regenerateAtlas(begin, end, fMaskFormat, 0, target, false);
2134 }
2135 }
2136
2137 // The 99% case. No clip. Non-color only.
direct_2D2(SkZip<Mask2DVertex[4],const GrGlyph *,const DirectMaskSubRunNoCache::DevicePosition> quadData,GrColor color)2138 void direct_2D2(SkZip<Mask2DVertex[4],
2139 const GrGlyph*,
2140 const DirectMaskSubRunNoCache::DevicePosition> quadData,
2141 GrColor color) {
2142 for (auto[quad, glyph, leftTop] : quadData) {
2143 auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
2144 SkScalar dl = leftTop[0],
2145 dt = leftTop[1],
2146 dr = dl + (ar - al),
2147 db = dt + (ab - at);
2148
2149 quad[0] = {{dl, dt}, color, {al, at}}; // L,T
2150 quad[1] = {{dl, db}, color, {al, ab}}; // L,B
2151 quad[2] = {{dr, dt}, color, {ar, at}}; // R,T
2152 quad[3] = {{dr, db}, color, {ar, ab}}; // R,B
2153 }
2154 }
2155
fillVertexData(void * vertexDst,int offset,int count,GrColor color,const SkMatrix & drawMatrix,SkPoint drawOrigin,SkIRect clip) const2156 void DirectMaskSubRunNoCache::fillVertexData(void* vertexDst, int offset, int count,
2157 GrColor color,
2158 const SkMatrix& drawMatrix, SkPoint drawOrigin,
2159 SkIRect clip) const {
2160 auto quadData = [&](auto dst) {
2161 return SkMakeZip(dst,
2162 fGlyphs.glyphs().subspan(offset, count),
2163 fLeftTopDevicePos.subspan(offset, count));
2164 };
2165
2166 // Notice that no matrix manipulation is needed because all the rectangles are already mapped
2167 // to device space.
2168 if (clip.isEmpty()) {
2169 if (fMaskFormat != kARGB_GrMaskFormat) {
2170 using Quad = Mask2DVertex[4];
2171 SkASSERT(sizeof(Mask2DVertex) == this->vertexStride(SkMatrix::I()));
2172 direct_2D2(quadData((Quad*)vertexDst), color);
2173 } else {
2174 using Quad = ARGB2DVertex[4];
2175 SkASSERT(sizeof(ARGB2DVertex) == this->vertexStride(SkMatrix::I()));
2176 generalized_direct_2D(quadData((Quad*)vertexDst), color, {0,0});
2177 }
2178 } else {
2179 if (fMaskFormat != kARGB_GrMaskFormat) {
2180 using Quad = Mask2DVertex[4];
2181 SkASSERT(sizeof(Mask2DVertex) == this->vertexStride(SkMatrix::I()));
2182 generalized_direct_2D(quadData((Quad*)vertexDst), color, {0,0}, &clip);
2183 } else {
2184 using Quad = ARGB2DVertex[4];
2185 SkASSERT(sizeof(ARGB2DVertex) == this->vertexStride(SkMatrix::I()));
2186 generalized_direct_2D(quadData((Quad*)vertexDst), color, {0,0}, &clip);
2187 }
2188 }
2189 }
2190
2191 // -- TransformedMaskSubRunNoCache -----------------------------------------------------------------
2192 class TransformedMaskSubRunNoCache final : public GrAtlasSubRun {
2193 public:
2194 using VertexData = TransformedMaskVertexFiller::PositionAndExtent;
2195
2196 TransformedMaskSubRunNoCache(GrMaskFormat format,
2197 SkScalar strikeToSourceScale,
2198 const SkRect& bounds,
2199 SkSpan<const VertexData> vertexData,
2200 GrGlyphVector&& glyphs);
2201
2202 static GrAtlasSubRunOwner Make(const SkZip<SkGlyphVariant, SkPoint>& accepted,
2203 sk_sp<SkStrike>&& strike,
2204 SkScalar strikeToSourceScale,
2205 GrMaskFormat format,
2206 GrSubRunAllocator* alloc);
2207
2208 std::tuple<const GrClip*, GrOp::Owner>
2209 makeAtlasTextOp(const GrClip*,
2210 const SkMatrixProvider& viewMatrix,
2211 SkPoint drawOrigin,
2212 const SkPaint&,
2213 skgpu::v1::SurfaceDrawContext*,
2214 GrAtlasSubRunOwner) const override;
2215
2216 void testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) const override;
2217
2218 std::tuple<bool, int> regenerateAtlas(int begin, int end, GrMeshDrawTarget*) const override;
2219
2220 void fillVertexData(
2221 void* vertexDst, int offset, int count,
2222 GrColor color,
2223 const SkMatrix& drawMatrix, SkPoint drawOrigin,
2224 SkIRect clip) const override;
2225
2226 size_t vertexStride(const SkMatrix& drawMatrix) const override;
2227 int glyphCount() const override;
2228
2229 private:
2230 // The rectangle that surrounds all the glyph bounding boxes in device space.
2231 SkRect deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const;
2232
2233 const TransformedMaskVertexFiller fVertexFiller;
2234
2235 // The bounds in source space. The bounds are the joined rectangles of all the glyphs.
2236 const SkRect fVertexBounds;
2237 const SkSpan<const VertexData> fVertexData;
2238
2239 // Space for geometry
2240 alignas(alignof(AtlasTextOp::Geometry)) char fGeom[sizeof(AtlasTextOp::Geometry)];
2241
2242 // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
2243 // be single threaded.
2244 mutable GrGlyphVector fGlyphs;
2245 };
2246
TransformedMaskSubRunNoCache(GrMaskFormat format,SkScalar strikeToSourceScale,const SkRect & bounds,SkSpan<const VertexData> vertexData,GrGlyphVector && glyphs)2247 TransformedMaskSubRunNoCache::TransformedMaskSubRunNoCache(GrMaskFormat format,
2248 SkScalar strikeToSourceScale,
2249 const SkRect& bounds,
2250 SkSpan<const VertexData> vertexData,
2251 GrGlyphVector&& glyphs)
2252 : fVertexFiller{format, 0, strikeToSourceScale}
2253 , fVertexBounds{bounds}
2254 , fVertexData{vertexData}
2255 , fGlyphs{std::move(glyphs)} {}
2256
Make(const SkZip<SkGlyphVariant,SkPoint> & accepted,sk_sp<SkStrike> && strike,SkScalar strikeToSourceScale,GrMaskFormat format,GrSubRunAllocator * alloc)2257 GrAtlasSubRunOwner TransformedMaskSubRunNoCache::Make(
2258 const SkZip<SkGlyphVariant, SkPoint>& accepted,
2259 sk_sp<SkStrike>&& strike,
2260 SkScalar strikeToSourceScale,
2261 GrMaskFormat format,
2262 GrSubRunAllocator* alloc) {
2263 SkRect bounds = SkRectPriv::MakeLargestInverted();
2264 auto initializer = [&](auto acceptedGlyph) {
2265 auto [variant, pos] = acceptedGlyph;
2266 const SkGlyph* skGlyph = variant;
2267 int16_t l = skGlyph->left(),
2268 t = skGlyph->top(),
2269 r = l + skGlyph->width(),
2270 b = t + skGlyph->height();
2271 SkPoint lt = SkPoint::Make(l, t) * strikeToSourceScale + pos,
2272 rb = SkPoint::Make(r, b) * strikeToSourceScale + pos;
2273
2274 bounds.joinPossiblyEmptyRect(SkRect::MakeLTRB(lt.x(), lt.y(), rb.x(), rb.y()));
2275 return VertexData{pos, {l, t, r, b}};
2276 };
2277
2278 SkSpan<VertexData> vertexData = alloc->makePODArray<VertexData>(accepted, initializer);
2279
2280 return alloc->makeUnique<TransformedMaskSubRunNoCache>(
2281 format, strikeToSourceScale, bounds, vertexData,
2282 GrGlyphVector::Make(std::move(strike), accepted.get<0>(), alloc));
2283 }
2284
2285 std::tuple<const GrClip*, GrOp::Owner>
makeAtlasTextOp(const GrClip * clip,const SkMatrixProvider & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,skgpu::v1::SurfaceDrawContext * sdc,GrAtlasSubRunOwner subRunOwner) const2286 TransformedMaskSubRunNoCache::makeAtlasTextOp(const GrClip* clip,
2287 const SkMatrixProvider& viewMatrix,
2288 SkPoint drawOrigin,
2289 const SkPaint& paint,
2290 skgpu::v1::SurfaceDrawContext* sdc,
2291 GrAtlasSubRunOwner subRunOwner) const {
2292 SkASSERT(this->glyphCount() != 0);
2293
2294 const SkMatrix& drawMatrix = viewMatrix.localToDevice();
2295
2296 GrPaint grPaint;
2297 SkPMColor4f drawingColor = calculate_colors(
2298 sdc, paint, viewMatrix, fVertexFiller.grMaskType(), &grPaint);
2299
2300 // We can clip geometrically using clipRect and ignore clip if we're not using SDFs or
2301 // transformed glyphs, and we have an axis-aligned rectangular non-AA clip.
2302 auto geometry = new ((void*)fGeom) AtlasTextOp::Geometry{
2303 *this,
2304 drawMatrix,
2305 drawOrigin,
2306 SkIRect::MakeEmpty(),
2307 nullptr,
2308 std::move(subRunOwner),
2309 drawingColor
2310 };
2311
2312 GrRecordingContext* rContext = sdc->recordingContext();
2313 GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext,
2314 fVertexFiller.opMaskType(),
2315 true,
2316 this->glyphCount(),
2317 this->deviceRect(drawMatrix, drawOrigin),
2318 geometry,
2319 std::move(grPaint));
2320 return {clip, std::move(op)};
2321 }
2322
testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache * cache) const2323 void TransformedMaskSubRunNoCache::testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) const {
2324 fGlyphs.packedGlyphIDToGrGlyph(cache);
2325 }
2326
regenerateAtlas(int begin,int end,GrMeshDrawTarget * target) const2327 std::tuple<bool, int> TransformedMaskSubRunNoCache::regenerateAtlas(
2328 int begin, int end, GrMeshDrawTarget* target) const {
2329 return fGlyphs.regenerateAtlas(begin, end, fVertexFiller.grMaskType(), 1, target, true);
2330 }
2331
fillVertexData(void * vertexDst,int offset,int count,GrColor color,const SkMatrix & drawMatrix,SkPoint drawOrigin,SkIRect clip) const2332 void TransformedMaskSubRunNoCache::fillVertexData(
2333 void* vertexDst, int offset, int count,
2334 GrColor color,
2335 const SkMatrix& drawMatrix, SkPoint drawOrigin,
2336 SkIRect clip) const {
2337 const SkMatrix positionMatrix = position_matrix(drawMatrix, drawOrigin);
2338 fVertexFiller.fillVertexData(fGlyphs.glyphs().subspan(offset, count),
2339 fVertexData.subspan(offset, count),
2340 color,
2341 positionMatrix,
2342 clip,
2343 vertexDst);
2344 }
2345
vertexStride(const SkMatrix & drawMatrix) const2346 size_t TransformedMaskSubRunNoCache::vertexStride(const SkMatrix& drawMatrix) const {
2347 return fVertexFiller.vertexStride(drawMatrix);
2348 }
2349
glyphCount() const2350 int TransformedMaskSubRunNoCache::glyphCount() const {
2351 return SkCount(fVertexData);
2352 }
2353
deviceRect(const SkMatrix & drawMatrix,SkPoint drawOrigin) const2354 SkRect TransformedMaskSubRunNoCache::deviceRect(
2355 const SkMatrix& drawMatrix, SkPoint drawOrigin) const {
2356 SkRect outBounds = fVertexBounds;
2357 outBounds.offset(drawOrigin);
2358 return drawMatrix.mapRect(outBounds);
2359 }
2360
2361 // -- SDFTSubRunNoCache ----------------------------------------------------------------------------
2362 class SDFTSubRunNoCache final : public GrAtlasSubRun {
2363 public:
2364 struct VertexData {
2365 const SkPoint pos;
2366 // The rectangle of the glyphs in strike space.
2367 GrIRect16 rect;
2368 };
2369
2370 SDFTSubRunNoCache(GrMaskFormat format,
2371 SkScalar strikeToSourceScale,
2372 SkRect vertexBounds,
2373 SkSpan<const VertexData> vertexData,
2374 GrGlyphVector&& glyphs,
2375 bool useLCDText,
2376 bool antiAliased);
2377
2378 static GrAtlasSubRunOwner Make(const SkZip<SkGlyphVariant, SkPoint>& accepted,
2379 const SkFont& runFont,
2380 sk_sp<SkStrike>&& strike,
2381 SkScalar strikeToSourceScale,
2382 GrSubRunAllocator* alloc);
2383
2384 std::tuple<const GrClip*, GrOp::Owner>
2385 makeAtlasTextOp(const GrClip*,
2386 const SkMatrixProvider& viewMatrix,
2387 SkPoint drawOrigin,
2388 const SkPaint&,
2389 skgpu::v1::SurfaceDrawContext*,
2390 GrAtlasSubRunOwner) const override;
2391
2392 void testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) const override;
2393
2394 std::tuple<bool, int> regenerateAtlas(int begin, int end, GrMeshDrawTarget*) const override;
2395
2396 void fillVertexData(
2397 void* vertexDst, int offset, int count,
2398 GrColor color,
2399 const SkMatrix& drawMatrix, SkPoint drawOrigin,
2400 SkIRect clip) const override;
2401
2402 size_t vertexStride(const SkMatrix& drawMatrix) const override;
2403 int glyphCount() const override;
2404
2405 private:
2406 // The rectangle that surrounds all the glyph bounding boxes in device space.
2407 SkRect deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const;
2408
2409 const GrMaskFormat fMaskFormat;
2410
2411 // The scale factor between the strike size, and the source size.
2412 const SkScalar fStrikeToSourceScale;
2413
2414 // The bounds in source space. The bounds are the joined rectangles of all the glyphs.
2415 const SkRect fVertexBounds;
2416 const SkSpan<const VertexData> fVertexData;
2417
2418 // Space for geometry
2419 alignas(alignof(AtlasTextOp::Geometry)) char fGeom[sizeof(AtlasTextOp::Geometry)];
2420
2421 // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
2422 // be single threaded.
2423 mutable GrGlyphVector fGlyphs;
2424
2425 const bool fUseLCDText;
2426 const bool fAntiAliased;
2427 };
2428
SDFTSubRunNoCache(GrMaskFormat format,SkScalar strikeToSourceScale,SkRect vertexBounds,SkSpan<const VertexData> vertexData,GrGlyphVector && glyphs,bool useLCDText,bool antiAliased)2429 SDFTSubRunNoCache::SDFTSubRunNoCache(GrMaskFormat format,
2430 SkScalar strikeToSourceScale,
2431 SkRect vertexBounds,
2432 SkSpan<const VertexData> vertexData,
2433 GrGlyphVector&& glyphs,
2434 bool useLCDText,
2435 bool antiAliased)
2436 : fMaskFormat{format}
2437 , fStrikeToSourceScale{strikeToSourceScale}
2438 , fVertexBounds{vertexBounds}
2439 , fVertexData{vertexData}
2440 , fGlyphs{std::move(glyphs)}
2441 , fUseLCDText{useLCDText}
2442 , fAntiAliased{antiAliased} {}
2443
2444
Make(const SkZip<SkGlyphVariant,SkPoint> & accepted,const SkFont & runFont,sk_sp<SkStrike> && strike,SkScalar strikeToSourceScale,GrSubRunAllocator * alloc)2445 GrAtlasSubRunOwner SDFTSubRunNoCache::Make(
2446 const SkZip<SkGlyphVariant, SkPoint>& accepted,
2447 const SkFont& runFont,
2448 sk_sp<SkStrike>&& strike,
2449 SkScalar strikeToSourceScale,
2450 GrSubRunAllocator* alloc) {
2451
2452 SkRect bounds = SkRectPriv::MakeLargestInverted();
2453 auto initializer = [&](auto acceptedGlyph) {
2454 auto [variant, pos] = acceptedGlyph;
2455 const SkGlyph* skGlyph = variant;
2456 int16_t l = skGlyph->left(),
2457 t = skGlyph->top(),
2458 r = l + skGlyph->width(),
2459 b = t + skGlyph->height();
2460 SkPoint lt = SkPoint::Make(l, t) * strikeToSourceScale + pos,
2461 rb = SkPoint::Make(r, b) * strikeToSourceScale + pos;
2462
2463 bounds.joinPossiblyEmptyRect(SkRect::MakeLTRB(lt.x(), lt.y(), rb.x(), rb.y()));
2464 return VertexData{pos, {l, t, r, b}};
2465 };
2466
2467 SkSpan<VertexData> vertexData = alloc->makePODArray<VertexData>(accepted, initializer);
2468
2469 return alloc->makeUnique<SDFTSubRunNoCache>(
2470 kA8_GrMaskFormat,
2471 strikeToSourceScale,
2472 bounds,
2473 vertexData,
2474 GrGlyphVector::Make(std::move(strike), accepted.get<0>(), alloc),
2475 runFont.getEdging() == SkFont::Edging::kSubpixelAntiAlias,
2476 has_some_antialiasing(runFont));
2477 }
2478
2479 std::tuple<const GrClip*, GrOp::Owner>
makeAtlasTextOp(const GrClip * clip,const SkMatrixProvider & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,skgpu::v1::SurfaceDrawContext * sdc,GrAtlasSubRunOwner subRunOwner) const2480 SDFTSubRunNoCache::makeAtlasTextOp(const GrClip* clip,
2481 const SkMatrixProvider& viewMatrix,
2482 SkPoint drawOrigin,
2483 const SkPaint& paint,
2484 skgpu::v1::SurfaceDrawContext* sdc,
2485 GrAtlasSubRunOwner subRunOwner) const {
2486 SkASSERT(this->glyphCount() != 0);
2487
2488 const SkMatrix& drawMatrix = viewMatrix.localToDevice();
2489
2490 GrPaint grPaint;
2491 SkPMColor4f drawingColor = calculate_colors(sdc, paint, viewMatrix, fMaskFormat, &grPaint);
2492
2493 auto [maskType, DFGPFlags, useGammaCorrectDistanceTable] =
2494 calculate_sdf_parameters(*sdc, drawMatrix, fUseLCDText, fAntiAliased);
2495
2496 auto geometry = new ((void*)fGeom) AtlasTextOp::Geometry {
2497 *this,
2498 drawMatrix,
2499 drawOrigin,
2500 SkIRect::MakeEmpty(),
2501 nullptr,
2502 std::move(subRunOwner),
2503 drawingColor
2504 };
2505
2506 GrRecordingContext* rContext = sdc->recordingContext();
2507 GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext,
2508 maskType,
2509 true,
2510 this->glyphCount(),
2511 this->deviceRect(drawMatrix, drawOrigin),
2512 SkPaintPriv::ComputeLuminanceColor(paint),
2513 useGammaCorrectDistanceTable,
2514 DFGPFlags,
2515 geometry,
2516 std::move(grPaint));
2517
2518 return {clip, std::move(op)};
2519 }
2520
testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache * cache) const2521 void SDFTSubRunNoCache::testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) const {
2522 fGlyphs.packedGlyphIDToGrGlyph(cache);
2523 }
2524
regenerateAtlas(int begin,int end,GrMeshDrawTarget * target) const2525 std::tuple<bool, int> SDFTSubRunNoCache::regenerateAtlas(
2526 int begin, int end, GrMeshDrawTarget *target) const {
2527
2528 return fGlyphs.regenerateAtlas(begin, end, fMaskFormat, SK_DistanceFieldInset, target);
2529 }
2530
vertexStride(const SkMatrix & drawMatrix) const2531 size_t SDFTSubRunNoCache::vertexStride(const SkMatrix& drawMatrix) const {
2532 return sizeof(Mask2DVertex);
2533 }
2534
fillVertexData(void * vertexDst,int offset,int count,GrColor color,const SkMatrix & drawMatrix,SkPoint drawOrigin,SkIRect clip) const2535 void SDFTSubRunNoCache::fillVertexData(
2536 void *vertexDst, int offset, int count,
2537 GrColor color,
2538 const SkMatrix& drawMatrix, SkPoint drawOrigin,
2539 SkIRect clip) const {
2540 using Quad = Mask2DVertex[4];
2541
2542 const SkMatrix positionMatrix = position_matrix(drawMatrix, drawOrigin);
2543
2544 SkASSERT(sizeof(Mask2DVertex) == this->vertexStride(positionMatrix));
2545 fill_transformed_vertices_2D(
2546 SkMakeZip((Quad*)vertexDst,
2547 fGlyphs.glyphs().subspan(offset, count),
2548 fVertexData.subspan(offset, count)),
2549 SK_DistanceFieldInset,
2550 fStrikeToSourceScale,
2551 color,
2552 positionMatrix);
2553 }
2554
glyphCount() const2555 int SDFTSubRunNoCache::glyphCount() const {
2556 return SkCount(fVertexData);
2557 }
2558
deviceRect(const SkMatrix & drawMatrix,SkPoint drawOrigin) const2559 SkRect SDFTSubRunNoCache::deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const {
2560 SkRect outBounds = fVertexBounds;
2561 outBounds.offset(drawOrigin);
2562 return drawMatrix.mapRect(outBounds);
2563 }
2564 } // namespace
2565
GrSubRunNoCachePainter(SkCanvas * canvas,skgpu::v1::SurfaceDrawContext * sdc,GrSubRunAllocator * alloc,const GrClip * clip,const SkMatrixProvider & viewMatrix,const SkGlyphRunList & glyphRunList,const SkPaint & paint)2566 GrSubRunNoCachePainter::GrSubRunNoCachePainter(SkCanvas* canvas,
2567 skgpu::v1::SurfaceDrawContext* sdc,
2568 GrSubRunAllocator* alloc,
2569 const GrClip* clip,
2570 const SkMatrixProvider& viewMatrix,
2571 const SkGlyphRunList& glyphRunList,
2572 const SkPaint& paint)
2573 : fCanvas{canvas}
2574 , fSDC{sdc}
2575 , fAlloc{alloc}
2576 , fClip{clip}
2577 , fViewMatrix{viewMatrix}
2578 , fGlyphRunList{glyphRunList}
2579 , fPaint {paint} {}
2580
processDeviceMasks(const SkZip<SkGlyphVariant,SkPoint> & accepted,sk_sp<SkStrike> && strike)2581 void GrSubRunNoCachePainter::processDeviceMasks(
2582 const SkZip<SkGlyphVariant, SkPoint>& accepted, sk_sp<SkStrike>&& strike) {
2583 auto addGlyphsWithSameFormat = [&] (const SkZip<SkGlyphVariant, SkPoint>& accepted,
2584 GrMaskFormat format,
2585 sk_sp<SkStrike>&& runStrike) {
2586 const bool padAtlas =
2587 fSDC->recordingContext()->priv().options().fSupportBilerpFromGlyphAtlas;
2588 this->draw(DirectMaskSubRunNoCache::Make(
2589 accepted, std::move(runStrike), format, padAtlas, fAlloc));
2590 };
2591
2592 add_multi_mask_format(addGlyphsWithSameFormat, accepted, std::move(strike));
2593 }
2594
processSourceMasks(const SkZip<SkGlyphVariant,SkPoint> & accepted,sk_sp<SkStrike> && strike,SkScalar strikeToSourceScale)2595 void GrSubRunNoCachePainter::processSourceMasks(const SkZip<SkGlyphVariant, SkPoint>& accepted,
2596 sk_sp<SkStrike>&& strike,
2597 SkScalar strikeToSourceScale) {
2598 auto addGlyphsWithSameFormat = [&] (const SkZip<SkGlyphVariant, SkPoint>& accepted,
2599 GrMaskFormat format,
2600 sk_sp<SkStrike>&& runStrike) {
2601 this->draw(TransformedMaskSubRunNoCache::Make(
2602 accepted, std::move(runStrike), strikeToSourceScale, format, fAlloc));
2603 };
2604 add_multi_mask_format(addGlyphsWithSameFormat, accepted, std::move(strike));
2605 }
2606
processSourcePaths(const SkZip<SkGlyphVariant,SkPoint> & accepted,const SkFont & runFont,SkScalar strikeToSourceScale)2607 void GrSubRunNoCachePainter::processSourcePaths(const SkZip<SkGlyphVariant, SkPoint>& accepted,
2608 const SkFont& runFont,
2609 SkScalar strikeToSourceScale) {
2610 PathOpSubmitter pathDrawing =
2611 PathOpSubmitter::Make(accepted,
2612 has_some_antialiasing(runFont),
2613 strikeToSourceScale,
2614 fAlloc);
2615
2616 pathDrawing.submitOps(fCanvas, fClip, fViewMatrix, fGlyphRunList.origin(), fPaint, fSDC);
2617 }
2618
processSourceDrawables(const SkZip<SkGlyphVariant,SkPoint> & accepted,const SkFont & runFont,SkScalar strikeToSourceScale)2619 void GrSubRunNoCachePainter::processSourceDrawables(const SkZip<SkGlyphVariant, SkPoint>& accepted,
2620 const SkFont& runFont,
2621 SkScalar strikeToSourceScale) {
2622 DrawableOpSubmitter drawableDrawing =
2623 DrawableOpSubmitter::Make(accepted,
2624 has_some_antialiasing(runFont),
2625 strikeToSourceScale,
2626 fAlloc);
2627
2628 drawableDrawing.submitOps(fCanvas, fClip, fViewMatrix, fGlyphRunList.origin(), fPaint, fSDC);
2629 }
2630
processSourceSDFT(const SkZip<SkGlyphVariant,SkPoint> & accepted,sk_sp<SkStrike> && strike,SkScalar strikeToSourceScale,const SkFont & runFont,const GrSDFTMatrixRange &)2631 void GrSubRunNoCachePainter::processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& accepted,
2632 sk_sp<SkStrike>&& strike,
2633 SkScalar strikeToSourceScale,
2634 const SkFont& runFont,
2635 const GrSDFTMatrixRange&) {
2636 if (accepted.empty()) {
2637 return;
2638 }
2639 this->draw(SDFTSubRunNoCache::Make(
2640 accepted, runFont, std::move(strike), strikeToSourceScale, fAlloc));
2641 }
2642
draw(GrAtlasSubRunOwner subRun)2643 void GrSubRunNoCachePainter::draw(GrAtlasSubRunOwner subRun) {
2644 if (subRun == nullptr) {
2645 return;
2646 }
2647 GrAtlasSubRun* subRunPtr = subRun.get();
2648 auto [drawingClip, op] = subRunPtr->makeAtlasTextOp(
2649 fClip, fViewMatrix, fGlyphRunList.origin(), fPaint, fSDC, std::move(subRun));
2650 if (op != nullptr) {
2651 fSDC->addDrawOp(drawingClip, std::move(op));
2652 }
2653 }
2654
2655 namespace {
2656 // -- Slug -----------------------------------------------------------------------------------------
2657 class Slug final : public GrSlug, public SkGlyphRunPainterInterface {
2658 public:
2659 Slug(SkRect sourceBounds,
2660 const SkPaint& paint,
2661 const SkMatrix& positionMatrix,
2662 SkPoint origin,
2663 int allocSize);
2664 ~Slug() override = default;
2665
2666 static sk_sp<Slug> Make(const SkMatrixProvider& viewMatrix,
2667 const SkGlyphRunList& glyphRunList,
2668 const SkPaint& paint,
2669 const GrSDFTControl& control,
2670 SkGlyphRunListPainter* painter);
2671 static sk_sp<GrSlug> MakeFromBuffer(SkReadBuffer& buffer,
2672 const SkStrikeClient* client);
2673
2674 void surfaceDraw(SkCanvas*,
2675 const GrClip* clip,
2676 const SkMatrixProvider& viewMatrix,
2677 skgpu::v1::SurfaceDrawContext* sdc);
2678
2679 void flatten(SkWriteBuffer& buffer) const override;
sourceBounds() const2680 SkRect sourceBounds() const override { return fSourceBounds; }
paint() const2681 const SkPaint& paint() const override { return fPaint; }
2682
2683 // SkGlyphRunPainterInterface
2684 void processDeviceMasks(
2685 const SkZip<SkGlyphVariant, SkPoint>& accepted, sk_sp<SkStrike>&& strike) override;
2686 void processSourceMasks(
2687 const SkZip<SkGlyphVariant, SkPoint>& accepted, sk_sp<SkStrike>&& strike,
2688 SkScalar strikeToSourceScale) override;
2689 void processSourcePaths(
2690 const SkZip<SkGlyphVariant, SkPoint>& accepted, const SkFont& runFont,
2691 SkScalar strikeToSourceScale) override;
2692 void processSourceDrawables(
2693 const SkZip<SkGlyphVariant, SkPoint>& drawables, const SkFont& runFont,
2694 SkScalar strikeToSourceScale) override;
2695 void processSourceSDFT(
2696 const SkZip<SkGlyphVariant, SkPoint>& accepted, sk_sp<SkStrike>&& strike,
2697 SkScalar strikeToSourceScale, const SkFont& runFont,
2698 const GrSDFTMatrixRange& matrixRange) override;
2699
initialPositionMatrix() const2700 const SkMatrix& initialPositionMatrix() const override { return fInitialPositionMatrix; }
origin() const2701 SkPoint origin() const { return fOrigin; }
2702
2703 // Change memory management to handle the data after Slug, but in the same allocation
2704 // of memory. Only allow placement new.
operator delete(void * p)2705 void operator delete(void* p) { ::operator delete(p); }
operator new(size_t)2706 void* operator new(size_t) { SK_ABORT("All slugs are created by placement new."); }
operator new(size_t,void * p)2707 void* operator new(size_t, void* p) { return p; }
2708
subRunCountAndUnflattenSizeHint() const2709 std::tuple<int, int> subRunCountAndUnflattenSizeHint() const {
2710 int unflattenSizeHint = 0;
2711 int subRunCount = 0;
2712 for (auto& subrun : fSubRuns) {
2713 subRunCount += 1;
2714 unflattenSizeHint += subrun.unflattenSize();
2715 }
2716 return {subRunCount, unflattenSizeHint};
2717 }
2718
2719 private:
2720 // The allocator must come first because it needs to be destroyed last. Other fields of this
2721 // structure may have pointers into it.
2722 GrSubRunAllocator fAlloc;
2723 const SkRect fSourceBounds;
2724 const SkPaint fPaint;
2725 const SkMatrix fInitialPositionMatrix;
2726 const SkPoint fOrigin;
2727 GrSubRunList fSubRuns;
2728 };
2729
Slug(SkRect sourceBounds,const SkPaint & paint,const SkMatrix & positionMatrix,SkPoint origin,int allocSize)2730 Slug::Slug(SkRect sourceBounds,
2731 const SkPaint& paint,
2732 const SkMatrix& positionMatrix,
2733 SkPoint origin,
2734 int allocSize)
2735 : fAlloc {SkTAddOffset<char>(this, sizeof(Slug)), allocSize, allocSize/2}
2736 , fSourceBounds{sourceBounds}
2737 , fPaint{paint}
2738 , fInitialPositionMatrix{positionMatrix}
2739 , fOrigin{origin} { }
2740
surfaceDraw(SkCanvas * canvas,const GrClip * clip,const SkMatrixProvider & viewMatrix,skgpu::v1::SurfaceDrawContext * sdc)2741 void Slug::surfaceDraw(SkCanvas* canvas, const GrClip* clip, const SkMatrixProvider& viewMatrix,
2742 skgpu::v1::SurfaceDrawContext* sdc) {
2743 for (const GrSubRun& subRun : fSubRuns) {
2744 subRun.draw(canvas, clip, viewMatrix, fOrigin, fPaint, sdc);
2745 }
2746 }
2747
flatten(SkWriteBuffer & buffer) const2748 void Slug::flatten(SkWriteBuffer& buffer) const {
2749 buffer.writeRect(fSourceBounds);
2750 SkPaintPriv::Flatten(fPaint, buffer);
2751 buffer.writeMatrix(fInitialPositionMatrix);
2752 buffer.writePoint(fOrigin);
2753 auto [subRunCount, subRunsUnflattenSizeHint] = this->subRunCountAndUnflattenSizeHint();
2754 buffer.writeInt(subRunCount);
2755 buffer.writeInt(subRunsUnflattenSizeHint);
2756 for (auto& subRun : fSubRuns) {
2757 subRun.flatten(buffer);
2758 }
2759 }
2760
MakeFromBuffer(SkReadBuffer & buffer,const SkStrikeClient * client)2761 sk_sp<GrSlug> Slug::MakeFromBuffer(SkReadBuffer& buffer, const SkStrikeClient* client) {
2762 SkRect sourceBounds = buffer.readRect();
2763 if (!buffer.validate(!sourceBounds.isEmpty())) { return nullptr; }
2764
2765 SkPaint paint = buffer.readPaint();
2766 SkMatrix positionMatrix;
2767 buffer.readMatrix(&positionMatrix);
2768 SkPoint origin = buffer.readPoint();
2769 int subRunCount = buffer.readInt();
2770 if (!buffer.validate(subRunCount != 0)) { return nullptr; }
2771 int subRunsUnflattenSizeHint = buffer.readInt();
2772
2773 sk_sp<Slug> slug{new (::operator new (sizeof(Slug) + subRunsUnflattenSizeHint))
2774 Slug(sourceBounds,
2775 paint,
2776 positionMatrix,
2777 origin,
2778 subRunsUnflattenSizeHint)};
2779 for (int i = 0; i < subRunCount; ++i) {
2780 auto subRun = GrSubRun::MakeFromBuffer(slug.get(), buffer, &slug->fAlloc, client);
2781 if (!buffer.validate(subRun != nullptr)) { return nullptr; }
2782 slug->fSubRuns.append(std::move(subRun));
2783 }
2784
2785 // Something went wrong while reading.
2786 if (!buffer.isValid()) { return nullptr;}
2787
2788 return std::move(slug);
2789 }
2790
2791 // -- DirectMaskSubRunSlug -------------------------------------------------------------------------
2792 class DirectMaskSubRunSlug final : public GrSubRun, public GrAtlasSubRun {
2793 public:
2794 using DevicePosition = skvx::Vec<2, int16_t>;
2795
2796 DirectMaskSubRunSlug(const GrTextReferenceFrame* referenceFrame,
2797 GrMaskFormat format,
2798 SkGlyphRect deviceBounds,
2799 SkSpan<const DevicePosition> devicePositions,
2800 GrGlyphVector&& glyphs);
2801
2802 static GrSubRunOwner Make(const GrTextReferenceFrame* referenceFrame,
2803 const SkZip<SkGlyphVariant, SkPoint>& accepted,
2804 sk_sp<SkStrike>&& strike,
2805 GrMaskFormat format,
2806 GrSubRunAllocator* alloc);
2807
2808 static GrSubRunOwner MakeFromBuffer(const GrTextReferenceFrame* referenceFrame,
2809 SkReadBuffer& buffer,
2810 GrSubRunAllocator* alloc,
2811 const SkStrikeClient* client);
2812
draw(SkCanvas *,const GrClip * clip,const SkMatrixProvider & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,skgpu::v1::SurfaceDrawContext * sdc) const2813 void draw(SkCanvas*,
2814 const GrClip* clip,
2815 const SkMatrixProvider& viewMatrix,
2816 SkPoint drawOrigin,
2817 const SkPaint& paint,
2818 skgpu::v1::SurfaceDrawContext* sdc) const override {
2819 auto [drawingClip, op] = this->makeAtlasTextOp(
2820 clip, viewMatrix, drawOrigin, paint, sdc, nullptr);
2821 if (op != nullptr) {
2822 sdc->addDrawOp(drawingClip, std::move(op));
2823 }
2824 }
2825
2826 int unflattenSize() const override;
2827
2828 size_t vertexStride(const SkMatrix& drawMatrix) const override;
2829
2830 int glyphCount() const override;
2831
2832 std::tuple<const GrClip*, GrOp::Owner>
2833 makeAtlasTextOp(const GrClip*,
2834 const SkMatrixProvider& viewMatrix,
2835 SkPoint,
2836 const SkPaint&,
2837 skgpu::v1::SurfaceDrawContext*,
2838 GrAtlasSubRunOwner) const override;
2839
2840 void testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) const override;
2841
2842 std::tuple<bool, int>
2843 regenerateAtlas(int begin, int end, GrMeshDrawTarget*) const override;
2844
2845 void fillVertexData(void* vertexDst, int offset, int count,
2846 GrColor color,
2847 const SkMatrix& drawMatrix, SkPoint drawOrigin,
2848 SkIRect clip) const override;
2849
2850 protected:
subRunType() const2851 SubRunType subRunType() const override { return kDirectMask; }
2852 void doFlatten(SkWriteBuffer& buffer) const override;
2853
2854 private:
2855 // Return true if the positionMatrix represents an integer translation. Return the device
2856 // bounding box of all the glyphs. If the bounding box is empty, then something went singular
2857 // and this operation should be dropped.
2858 std::tuple<bool, SkRect> deviceRectAndCheckTransform(const SkMatrix& positionMatrix) const;
2859
2860 const GrTextReferenceFrame* const fReferenceFrame;
2861 const GrMaskFormat fMaskFormat;
2862
2863 // The vertex bounds in device space. The bounds are the joined rectangles of all the glyphs.
2864 const SkGlyphRect fGlyphDeviceBounds;
2865 const SkSpan<const DevicePosition> fLeftTopDevicePos;
2866
2867 // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
2868 // be single threaded.
2869 mutable GrGlyphVector fGlyphs;
2870 };
2871
DirectMaskSubRunSlug(const GrTextReferenceFrame * referenceFrame,GrMaskFormat format,SkGlyphRect deviceBounds,SkSpan<const DevicePosition> devicePositions,GrGlyphVector && glyphs)2872 DirectMaskSubRunSlug::DirectMaskSubRunSlug(const GrTextReferenceFrame* referenceFrame,
2873 GrMaskFormat format,
2874 SkGlyphRect deviceBounds,
2875 SkSpan<const DevicePosition> devicePositions,
2876 GrGlyphVector&& glyphs)
2877 : fReferenceFrame{referenceFrame}
2878 , fMaskFormat{format}
2879 , fGlyphDeviceBounds{deviceBounds}
2880 , fLeftTopDevicePos{devicePositions}
2881 , fGlyphs{std::move(glyphs)} { }
2882
Make(const GrTextReferenceFrame * referenceFrame,const SkZip<SkGlyphVariant,SkPoint> & accepted,sk_sp<SkStrike> && strike,GrMaskFormat format,GrSubRunAllocator * alloc)2883 GrSubRunOwner DirectMaskSubRunSlug::Make(const GrTextReferenceFrame* referenceFrame,
2884 const SkZip<SkGlyphVariant, SkPoint>& accepted,
2885 sk_sp<SkStrike>&& strike,
2886 GrMaskFormat format,
2887 GrSubRunAllocator* alloc) {
2888 auto glyphLeftTop = alloc->makePODArray<DevicePosition>(accepted.size());
2889 auto glyphIDs = alloc->makePODArray<GrGlyphVector::Variant>(accepted.size());
2890
2891 // Because this is the direct case, the maximum width or height is the size that fits in the
2892 // atlas. This boundary is checked below to ensure that the call to SkGlyphRect below will
2893 // not overflow.
2894 constexpr SkScalar kMaxPos =
2895 std::numeric_limits<int16_t>::max() - SkStrikeCommon::kSkSideTooBigForAtlas;
2896 SkGlyphRect runBounds = skglyph::empty_rect();
2897 size_t goodPosCount = 0;
2898 for (auto [variant, pos] : accepted) {
2899 auto [x, y] = pos;
2900 // Ensure that the .offset() call below does not overflow. And, at this point none of the
2901 // rectangles are empty because they were culled before the run was created. Basically,
2902 // cull all the glyphs that can't appear on the screen.
2903 if (-kMaxPos < x && x < kMaxPos && -kMaxPos < y && y < kMaxPos) {
2904 const SkGlyph* const skGlyph = variant;
2905 const SkGlyphRect deviceBounds =
2906 skGlyph->glyphRect().offset(SkScalarRoundToInt(x), SkScalarRoundToInt(y));
2907 runBounds = skglyph::rect_union(runBounds, deviceBounds);
2908 glyphLeftTop[goodPosCount] = deviceBounds.topLeft();
2909 glyphIDs[goodPosCount].packedGlyphID = skGlyph->getPackedID();
2910 goodPosCount += 1;
2911 }
2912 }
2913
2914 // Wow! no glyphs are in bounds and had non-empty bounds.
2915 if (goodPosCount == 0) {
2916 return nullptr;
2917 }
2918
2919 SkSpan<const DevicePosition> leftTop{glyphLeftTop, goodPosCount};
2920 return alloc->makeUnique<DirectMaskSubRunSlug>(
2921 referenceFrame, format, runBounds, leftTop,
2922 GrGlyphVector{std::move(strike), {glyphIDs, goodPosCount}});
2923 }
2924
2925 template <typename T>
pun_read(SkReadBuffer & buffer,T * dst)2926 static bool pun_read(SkReadBuffer& buffer, T* dst) {
2927 return buffer.readPad32(dst, sizeof(T));
2928 }
2929
MakeFromBuffer(const GrTextReferenceFrame * referenceFrame,SkReadBuffer & buffer,GrSubRunAllocator * alloc,const SkStrikeClient *)2930 GrSubRunOwner DirectMaskSubRunSlug::MakeFromBuffer(const GrTextReferenceFrame* referenceFrame,
2931 SkReadBuffer& buffer,
2932 GrSubRunAllocator* alloc,
2933 const SkStrikeClient*) {
2934
2935 GrMaskFormat format = (GrMaskFormat)buffer.readInt();
2936 SkGlyphRect runBounds;
2937 pun_read(buffer, &runBounds);
2938
2939 int glyphCount = buffer.readInt();
2940 SkASSERT(0 < glyphCount);
2941 if (glyphCount <= 0) { return nullptr; }
2942 DevicePosition* positionsData = alloc->makePODArray<DevicePosition>(glyphCount);
2943 for (int i = 0; i < glyphCount; ++i) {
2944 pun_read(buffer, &positionsData[i]);
2945 }
2946 SkSpan<DevicePosition> positions(positionsData, glyphCount);
2947
2948 auto glyphVector = GrGlyphVector::MakeFromBuffer(buffer, alloc);
2949 SkASSERT(glyphVector.has_value());
2950 if (!glyphVector) { return nullptr; }
2951 SkASSERT(SkTo<int>(glyphVector->glyphs().size()) == glyphCount);
2952 if (SkTo<int>(glyphVector->glyphs().size()) != glyphCount) { return nullptr; }
2953 return alloc->makeUnique<DirectMaskSubRunSlug>(
2954 referenceFrame, format, runBounds, positions, std::move(glyphVector.value()));
2955 }
2956
2957 template <typename T>
pun_write(SkWriteBuffer & buffer,const T & src)2958 static void pun_write(SkWriteBuffer& buffer, const T& src) {
2959 buffer.writePad32(&src, sizeof(T));
2960 }
2961
doFlatten(SkWriteBuffer & buffer) const2962 void DirectMaskSubRunSlug::doFlatten(SkWriteBuffer& buffer) const {
2963 buffer.writeInt(fMaskFormat);
2964 pun_write(buffer, fGlyphDeviceBounds);
2965 int glyphCount = SkTo<int>(fLeftTopDevicePos.size());
2966 buffer.writeInt(glyphCount);
2967 for (auto pos : fLeftTopDevicePos) {
2968 pun_write(buffer, pos);
2969 }
2970 fGlyphs.flatten(buffer);
2971 }
2972
unflattenSize() const2973 int DirectMaskSubRunSlug::unflattenSize() const {
2974 return sizeof(DirectMaskSubRunSlug) +
2975 fGlyphs.unflattenSize() +
2976 sizeof(DevicePosition) * fGlyphs.glyphs().size();
2977 }
2978
vertexStride(const SkMatrix & positionMatrix) const2979 size_t DirectMaskSubRunSlug::vertexStride(const SkMatrix& positionMatrix) const {
2980 if (!positionMatrix.hasPerspective()) {
2981 if (fMaskFormat != kARGB_GrMaskFormat) {
2982 return sizeof(Mask2DVertex);
2983 } else {
2984 return sizeof(ARGB2DVertex);
2985 }
2986 } else {
2987 if (fMaskFormat != kARGB_GrMaskFormat) {
2988 return sizeof(Mask3DVertex);
2989 } else {
2990 return sizeof(ARGB3DVertex);
2991 }
2992 }
2993 }
2994
glyphCount() const2995 int DirectMaskSubRunSlug::glyphCount() const {
2996 return SkCount(fGlyphs.glyphs());
2997 }
2998
2999 std::tuple<const GrClip*, GrOp::Owner>
makeAtlasTextOp(const GrClip * clip,const SkMatrixProvider & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,skgpu::v1::SurfaceDrawContext * sdc,GrAtlasSubRunOwner subRunOwner) const3000 DirectMaskSubRunSlug::makeAtlasTextOp(const GrClip* clip,
3001 const SkMatrixProvider& viewMatrix,
3002 SkPoint drawOrigin,
3003 const SkPaint& paint,
3004 skgpu::v1::SurfaceDrawContext* sdc,
3005 GrAtlasSubRunOwner subRunOwner) const {
3006 SkASSERT(this->glyphCount() != 0);
3007 const SkMatrix& drawMatrix = viewMatrix.localToDevice();
3008 const SkMatrix& positionMatrix = position_matrix(drawMatrix, drawOrigin);
3009
3010 auto [integerTranslate, subRunDeviceBounds] = this->deviceRectAndCheckTransform(positionMatrix);
3011 if (subRunDeviceBounds.isEmpty()) {
3012 return {nullptr, nullptr};
3013 }
3014 // Rect for optimized bounds clipping when doing an integer translate.
3015 SkIRect geometricClipRect = SkIRect::MakeEmpty();
3016 if (integerTranslate) {
3017 // We can clip geometrically using clipRect and ignore clip when an axis-aligned rectangular
3018 // non-AA clip is used. If clipRect is empty, and clip is nullptr, then there is no clipping
3019 // needed.
3020 const SkRect deviceBounds = SkRect::MakeWH(sdc->width(), sdc->height());
3021 auto [clipMethod, clipRect] = calculate_clip(clip, deviceBounds, subRunDeviceBounds);
3022
3023 switch (clipMethod) {
3024 case kClippedOut:
3025 // Returning nullptr as op means skip this op.
3026 return {nullptr, nullptr};
3027 case kUnclipped:
3028 case kGeometryClipped:
3029 // GPU clip is not needed.
3030 clip = nullptr;
3031 break;
3032 case kGPUClipped:
3033 // Use th GPU clip; clipRect is ignored.
3034 break;
3035 }
3036 geometricClipRect = clipRect;
3037
3038 if (!geometricClipRect.isEmpty()) { SkASSERT(clip == nullptr); }
3039 }
3040
3041 GrPaint grPaint;
3042 const SkPMColor4f drawingColor =
3043 calculate_colors(sdc, paint, viewMatrix, fMaskFormat, &grPaint);
3044
3045 auto geometry = AtlasTextOp::Geometry::MakeForBlob(*this,
3046 drawMatrix,
3047 drawOrigin,
3048 geometricClipRect,
3049 sk_ref_sp(fReferenceFrame),
3050 drawingColor,
3051 sdc->arenaAlloc());
3052
3053 GrRecordingContext* const rContext = sdc->recordingContext();
3054 GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext,
3055 op_mask_type(fMaskFormat),
3056 !integerTranslate,
3057 this->glyphCount(),
3058 subRunDeviceBounds,
3059 geometry,
3060 std::move(grPaint));
3061 return {clip, std::move(op)};
3062 }
3063
testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache * cache) const3064 void DirectMaskSubRunSlug::testingOnly_packedGlyphIDToGrGlyph(GrStrikeCache *cache) const {
3065 fGlyphs.packedGlyphIDToGrGlyph(cache);
3066 }
3067
3068 std::tuple<bool, int>
regenerateAtlas(int begin,int end,GrMeshDrawTarget * target) const3069 DirectMaskSubRunSlug::regenerateAtlas(int begin, int end, GrMeshDrawTarget* target) const {
3070 return fGlyphs.regenerateAtlas(begin, end, fMaskFormat, 1, target, true);
3071 }
3072
3073 // The 99% case. No clip. Non-color only.
direct_2D3(SkZip<Mask2DVertex[4],const GrGlyph *,const DirectMaskSubRunNoCache::DevicePosition> quadData,GrColor color,SkPoint originOffset)3074 void direct_2D3(SkZip<Mask2DVertex[4],
3075 const GrGlyph*,
3076 const DirectMaskSubRunNoCache::DevicePosition> quadData,
3077 GrColor color,
3078 SkPoint originOffset) {
3079 for (auto[quad, glyph, leftTop] : quadData) {
3080 auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
3081 SkScalar dl = leftTop[0] + originOffset.x(),
3082 dt = leftTop[1] + originOffset.y(),
3083 dr = dl + (ar - al),
3084 db = dt + (ab - at);
3085
3086 quad[0] = {{dl, dt}, color, {al, at}}; // L,T
3087 quad[1] = {{dl, db}, color, {al, ab}}; // L,B
3088 quad[2] = {{dr, dt}, color, {ar, at}}; // R,T
3089 quad[3] = {{dr, db}, color, {ar, ab}}; // R,B
3090 }
3091 }
3092
3093 template<typename Quad, typename VertexData>
transformed_direct_2D(SkZip<Quad,const GrGlyph *,const VertexData> quadData,GrColor color,const SkMatrix & matrix)3094 void transformed_direct_2D(SkZip<Quad, const GrGlyph*, const VertexData> quadData,
3095 GrColor color,
3096 const SkMatrix& matrix) {
3097 for (auto[quad, glyph, leftTop] : quadData) {
3098 auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
3099 SkScalar dl = leftTop[0],
3100 dt = leftTop[1],
3101 dr = dl + (ar - al),
3102 db = dt + (ab - at);
3103 SkPoint lt = matrix.mapXY(dl, dt),
3104 lb = matrix.mapXY(dl, db),
3105 rt = matrix.mapXY(dr, dt),
3106 rb = matrix.mapXY(dr, db);
3107 quad[0] = {lt, color, {al, at}}; // L,T
3108 quad[1] = {lb, color, {al, ab}}; // L,B
3109 quad[2] = {rt, color, {ar, at}}; // R,T
3110 quad[3] = {rb, color, {ar, ab}}; // R,B
3111 }
3112 }
3113
3114 template<typename Quad, typename VertexData>
transformed_direct_3D(SkZip<Quad,const GrGlyph *,const VertexData> quadData,GrColor color,const SkMatrix & matrix)3115 void transformed_direct_3D(SkZip<Quad, const GrGlyph*, const VertexData> quadData,
3116 GrColor color,
3117 const SkMatrix& matrix) {
3118 auto mapXYZ = [&](SkScalar x, SkScalar y) {
3119 SkPoint pt{x, y};
3120 SkPoint3 result;
3121 matrix.mapHomogeneousPoints(&result, &pt, 1);
3122 return result;
3123 };
3124 for (auto[quad, glyph, leftTop] : quadData) {
3125 auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
3126 SkScalar dl = leftTop[0],
3127 dt = leftTop[1],
3128 dr = dl + (ar - al),
3129 db = dt + (ab - at);
3130 SkPoint3 lt = mapXYZ(dl, dt),
3131 lb = mapXYZ(dl, db),
3132 rt = mapXYZ(dr, dt),
3133 rb = mapXYZ(dr, db);
3134 quad[0] = {lt, color, {al, at}}; // L,T
3135 quad[1] = {lb, color, {al, ab}}; // L,B
3136 quad[2] = {rt, color, {ar, at}}; // R,T
3137 quad[3] = {rb, color, {ar, ab}}; // R,B
3138 }
3139 }
3140
fillVertexData(void * vertexDst,int offset,int count,GrColor color,const SkMatrix & drawMatrix,SkPoint drawOrigin,SkIRect clip) const3141 void DirectMaskSubRunSlug::fillVertexData(void* vertexDst, int offset, int count,
3142 GrColor color,
3143 const SkMatrix& drawMatrix, SkPoint drawOrigin,
3144 SkIRect clip) const {
3145 auto quadData = [&](auto dst) {
3146 return SkMakeZip(dst,
3147 fGlyphs.glyphs().subspan(offset, count),
3148 fLeftTopDevicePos.subspan(offset, count));
3149 };
3150
3151 const SkMatrix positionMatrix = position_matrix(drawMatrix, drawOrigin);
3152 auto [noTransformNeeded, originOffset] =
3153 can_use_direct(fReferenceFrame->initialPositionMatrix(), positionMatrix);
3154
3155 if (noTransformNeeded) {
3156 if (clip.isEmpty()) {
3157 if (fMaskFormat != kARGB_GrMaskFormat) {
3158 using Quad = Mask2DVertex[4];
3159 SkASSERT(sizeof(Mask2DVertex) == this->vertexStride(SkMatrix::I()));
3160 direct_2D3(quadData((Quad*)vertexDst), color, originOffset);
3161 } else {
3162 using Quad = ARGB2DVertex[4];
3163 SkASSERT(sizeof(ARGB2DVertex) == this->vertexStride(SkMatrix::I()));
3164 generalized_direct_2D(quadData((Quad*)vertexDst), color, originOffset);
3165 }
3166 } else {
3167 if (fMaskFormat != kARGB_GrMaskFormat) {
3168 using Quad = Mask2DVertex[4];
3169 SkASSERT(sizeof(Mask2DVertex) == this->vertexStride(SkMatrix::I()));
3170 generalized_direct_2D(quadData((Quad*)vertexDst), color, originOffset, &clip);
3171 } else {
3172 using Quad = ARGB2DVertex[4];
3173 SkASSERT(sizeof(ARGB2DVertex) == this->vertexStride(SkMatrix::I()));
3174 generalized_direct_2D(quadData((Quad*)vertexDst), color, originOffset, &clip);
3175 }
3176 }
3177 } else if (SkMatrix inverse; fReferenceFrame->initialPositionMatrix().invert(&inverse)) {
3178 SkMatrix viewDifference = SkMatrix::Concat(positionMatrix, inverse);
3179 if (!viewDifference.hasPerspective()) {
3180 if (fMaskFormat != kARGB_GrMaskFormat) {
3181 using Quad = Mask2DVertex[4];
3182 SkASSERT(sizeof(Mask2DVertex) == this->vertexStride(positionMatrix));
3183 transformed_direct_2D(quadData((Quad*)vertexDst), color, viewDifference);
3184 } else {
3185 using Quad = ARGB2DVertex[4];
3186 SkASSERT(sizeof(ARGB2DVertex) == this->vertexStride(positionMatrix));
3187 transformed_direct_2D(quadData((Quad*)vertexDst), color, viewDifference);
3188 }
3189 } else {
3190 if (fMaskFormat != kARGB_GrMaskFormat) {
3191 using Quad = Mask3DVertex[4];
3192 SkASSERT(sizeof(Mask3DVertex) == this->vertexStride(positionMatrix));
3193 transformed_direct_3D(quadData((Quad*)vertexDst), color, viewDifference);
3194 } else {
3195 using Quad = ARGB3DVertex[4];
3196 SkASSERT(sizeof(ARGB3DVertex) == this->vertexStride(positionMatrix));
3197 transformed_direct_3D(quadData((Quad*)vertexDst), color, viewDifference);
3198 }
3199 }
3200 }
3201 }
3202
3203 // true if only need to translate by integer amount, device rect.
3204 std::tuple<bool, SkRect>
deviceRectAndCheckTransform(const SkMatrix & positionMatrix) const3205 DirectMaskSubRunSlug::deviceRectAndCheckTransform(const SkMatrix& positionMatrix) const {
3206 SkPoint offset =
3207 positionMatrix.mapOrigin() - fReferenceFrame->initialPositionMatrix().mapOrigin();
3208 if (positionMatrix.isTranslate() && SkScalarIsInt(offset.x()) && SkScalarIsInt(offset.y())) {
3209 // Handle the integer offset case.
3210 // The offset should be integer, but make sure.
3211 SkIVector iOffset = {SkScalarRoundToInt(offset.x()), SkScalarRoundToInt(offset.y())};
3212
3213 SkIRect outBounds = fGlyphDeviceBounds.iRect();
3214 return {true, SkRect::Make(outBounds.makeOffset(iOffset))};
3215 } else if (SkMatrix inverse; fReferenceFrame->initialPositionMatrix().invert(&inverse)) {
3216 SkMatrix viewDifference = SkMatrix::Concat(positionMatrix, inverse);
3217 return {false, viewDifference.mapRect(fGlyphDeviceBounds.rect())};
3218 }
3219
3220 // initialPositionMatrix is singular. Do nothing.
3221 return {false, SkRect::MakeEmpty()};
3222 }
3223
processDeviceMasks(const SkZip<SkGlyphVariant,SkPoint> & accepted,sk_sp<SkStrike> && strike)3224 void Slug::processDeviceMasks(
3225 const SkZip<SkGlyphVariant, SkPoint>& accepted, sk_sp<SkStrike>&& strike) {
3226 auto addGlyphsWithSameFormat = [&] (const SkZip<SkGlyphVariant, SkPoint>& accepted,
3227 GrMaskFormat format,
3228 sk_sp<SkStrike>&& runStrike) {
3229 GrSubRunOwner subRun = DirectMaskSubRunSlug::Make(
3230 this, accepted, std::move(runStrike), format, &fAlloc);
3231 if (subRun != nullptr) {
3232 fSubRuns.append(std::move(subRun));
3233 }
3234 };
3235
3236 add_multi_mask_format(addGlyphsWithSameFormat, accepted, std::move(strike));
3237 }
3238
Make(const SkMatrixProvider & viewMatrix,const SkGlyphRunList & glyphRunList,const SkPaint & paint,const GrSDFTControl & control,SkGlyphRunListPainter * painter)3239 sk_sp<Slug> Slug::Make(const SkMatrixProvider& viewMatrix,
3240 const SkGlyphRunList& glyphRunList,
3241 const SkPaint& paint,
3242 const GrSDFTControl& control,
3243 SkGlyphRunListPainter* painter) {
3244 // The difference in alignment from the per-glyph data to the SubRun;
3245 constexpr size_t alignDiff =
3246 alignof(DirectMaskSubRun) - alignof(DirectMaskSubRun::DevicePosition);
3247 constexpr size_t vertexDataToSubRunPadding = alignDiff > 0 ? alignDiff : 0;
3248 size_t totalGlyphCount = glyphRunList.totalGlyphCount();
3249 // The bytesNeededForSubRun is optimized for DirectMaskSubRun which is by far the most
3250 // common case.
3251 size_t bytesNeededForSubRun = GrBagOfBytes::PlatformMinimumSizeWithOverhead(
3252 totalGlyphCount * sizeof(DirectMaskSubRunSlug::DevicePosition)
3253 + GrGlyphVector::GlyphVectorSize(totalGlyphCount)
3254 + glyphRunList.runCount() * (sizeof(DirectMaskSubRunSlug) + vertexDataToSubRunPadding),
3255 alignof(Slug));
3256
3257 size_t allocationSize = sizeof(GrTextBlob) + bytesNeededForSubRun;
3258
3259 const SkMatrix positionMatrix =
3260 position_matrix(viewMatrix.localToDevice(), glyphRunList.origin());
3261
3262 sk_sp<Slug> slug{new (::operator new (allocationSize))
3263 Slug(glyphRunList.sourceBounds(),
3264 paint,
3265 positionMatrix,
3266 glyphRunList.origin(),
3267 bytesNeededForSubRun)};
3268
3269 const uint64_t uniqueID = glyphRunList.uniqueID();
3270 for (auto& glyphRun : glyphRunList) {
3271 painter->processGlyphRun(slug.get(),
3272 glyphRun,
3273 positionMatrix,
3274 paint,
3275 control,
3276 "Make Slug",
3277 uniqueID);
3278 }
3279
3280 // There is nothing to draw here. This is particularly a problem with RSX form blobs where a
3281 // single space becomes a run with no glyphs.
3282 if (slug->fSubRuns.isEmpty()) { return nullptr; }
3283
3284 return slug;
3285 }
3286
processSourcePaths(const SkZip<SkGlyphVariant,SkPoint> & accepted,const SkFont & runFont,SkScalar strikeToSourceScale)3287 void Slug::processSourcePaths(const SkZip<SkGlyphVariant,
3288 SkPoint>& accepted,
3289 const SkFont& runFont,
3290 SkScalar strikeToSourceScale) {
3291 fSubRuns.append(PathSubRun::Make(
3292 accepted, has_some_antialiasing(runFont), strikeToSourceScale, &fAlloc));
3293 }
3294
processSourceDrawables(const SkZip<SkGlyphVariant,SkPoint> & accepted,const SkFont & runFont,SkScalar strikeToSourceScale)3295 void Slug::processSourceDrawables(const SkZip<SkGlyphVariant, SkPoint>& accepted,
3296 const SkFont& runFont,
3297 SkScalar strikeToSourceScale) {
3298 fSubRuns.append(make_drawable_sub_run<DrawableSubRunSlug>(
3299 accepted, has_some_antialiasing(runFont), strikeToSourceScale, &fAlloc));
3300 }
3301
processSourceSDFT(const SkZip<SkGlyphVariant,SkPoint> & accepted,sk_sp<SkStrike> && strike,SkScalar strikeToSourceScale,const SkFont & runFont,const GrSDFTMatrixRange & matrixRange)3302 void Slug::processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& accepted,
3303 sk_sp<SkStrike>&& strike,
3304 SkScalar strikeToSourceScale,
3305 const SkFont& runFont,
3306 const GrSDFTMatrixRange& matrixRange) {
3307 fSubRuns.append(SDFTSubRun::Make(
3308 this, accepted, runFont, std::move(strike), strikeToSourceScale, matrixRange, &fAlloc));
3309 }
3310
processSourceMasks(const SkZip<SkGlyphVariant,SkPoint> & accepted,sk_sp<SkStrike> && strike,SkScalar strikeToSourceScale)3311 void Slug::processSourceMasks(const SkZip<SkGlyphVariant, SkPoint>& accepted,
3312 sk_sp<SkStrike>&& strike,
3313 SkScalar strikeToSourceScale) {
3314
3315 auto addGlyphsWithSameFormat = [&] (const SkZip<SkGlyphVariant, SkPoint>& accepted,
3316 GrMaskFormat format,
3317 sk_sp<SkStrike>&& runStrike) {
3318 GrSubRunOwner subRun = TransformedMaskSubRun::Make(
3319 this, accepted, std::move(runStrike), strikeToSourceScale, format, &fAlloc);
3320 if (subRun != nullptr) {
3321 fSubRuns.append(std::move(subRun));
3322 }
3323 };
3324
3325 add_multi_mask_format(addGlyphsWithSameFormat, accepted, std::move(strike));
3326 }
3327 } // namespace
3328
3329 namespace skgpu::v1 {
3330 sk_sp<GrSlug>
convertGlyphRunListToSlug(const SkGlyphRunList & glyphRunList,const SkPaint & paint)3331 Device::convertGlyphRunListToSlug(const SkGlyphRunList& glyphRunList, const SkPaint& paint) {
3332 return fSurfaceDrawContext->convertGlyphRunListToSlug(
3333 this->asMatrixProvider(), glyphRunList, paint);
3334 }
3335
drawSlug(SkCanvas * canvas,GrSlug * slug)3336 void Device::drawSlug(SkCanvas* canvas, GrSlug* slug) {
3337 fSurfaceDrawContext->drawSlug(canvas, this->clip(), this->asMatrixProvider(), slug);
3338 }
3339
3340 sk_sp<GrSlug>
convertGlyphRunListToSlug(const SkMatrixProvider & viewMatrix,const SkGlyphRunList & glyphRunList,const SkPaint & paint)3341 SurfaceDrawContext::convertGlyphRunListToSlug(const SkMatrixProvider& viewMatrix,
3342 const SkGlyphRunList& glyphRunList,
3343 const SkPaint& paint) {
3344 SkASSERT(fContext->priv().options().fSupportBilerpFromGlyphAtlas);
3345
3346 GrSDFTControl control =
3347 this->recordingContext()->priv().getSDFTControl(
3348 this->surfaceProps().isUseDeviceIndependentFonts());
3349
3350 return Slug::Make(viewMatrix, glyphRunList, paint, control, &fGlyphPainter);
3351 }
3352
drawSlug(SkCanvas * canvas,const GrClip * clip,const SkMatrixProvider & viewMatrix,GrSlug * slugPtr)3353 void SurfaceDrawContext::drawSlug(SkCanvas* canvas,
3354 const GrClip* clip,
3355 const SkMatrixProvider& viewMatrix,
3356 GrSlug* slugPtr) {
3357 Slug* slug = static_cast<Slug*>(slugPtr);
3358
3359 slug->surfaceDraw(canvas, clip, viewMatrix, this);
3360 }
3361
MakeSlug(const SkMatrixProvider & drawMatrix,const SkGlyphRunList & glyphRunList,const SkPaint & paint,const GrSDFTControl & control,SkGlyphRunListPainter * painter)3362 sk_sp<GrSlug> MakeSlug(const SkMatrixProvider& drawMatrix,
3363 const SkGlyphRunList& glyphRunList,
3364 const SkPaint& paint,
3365 const GrSDFTControl& control,
3366 SkGlyphRunListPainter* painter) {
3367 return Slug::Make(drawMatrix, glyphRunList, paint, control, painter);
3368 }
3369 } // namespace skgpu::v1
3370
3371 // -- GrSubRun -------------------------------------------------------------------------------------
flatten(SkWriteBuffer & buffer) const3372 void GrSubRun::flatten(SkWriteBuffer& buffer) const {
3373 buffer.writeInt(this->subRunType());
3374 this->doFlatten(buffer);
3375 }
3376
MakeFromBuffer(const GrTextReferenceFrame * referenceFrame,SkReadBuffer & buffer,GrSubRunAllocator * alloc,const SkStrikeClient * client)3377 GrSubRunOwner GrSubRun::MakeFromBuffer(const GrTextReferenceFrame* referenceFrame,
3378 SkReadBuffer& buffer,
3379 GrSubRunAllocator* alloc,
3380 const SkStrikeClient* client) {
3381 using Maker = GrSubRunOwner (*)(const GrTextReferenceFrame*,
3382 SkReadBuffer&,
3383 GrSubRunAllocator*,
3384 const SkStrikeClient*);
3385
3386 /* The makers will be populated in the next CL. */
3387 static Maker makers[kSubRunTypeCount] = {
3388 nullptr, // 0 index is bad.
3389 DirectMaskSubRunSlug::MakeFromBuffer,
3390 SDFTSubRun::MakeFromBuffer,
3391 TransformedMaskSubRun::MakeFromBuffer,
3392 PathSubRun::MakeFromBuffer,
3393 DrawableSubRunSlug::MakeFromBuffer,
3394 };
3395 int subRunTypeInt = buffer.readInt();
3396 SkASSERT(kBad < subRunTypeInt && subRunTypeInt < kSubRunTypeCount);
3397 if (!buffer.validate(kBad < subRunTypeInt && subRunTypeInt < kSubRunTypeCount)) {
3398 return nullptr;
3399 }
3400 auto maker = makers[subRunTypeInt];
3401 if (!buffer.validate(maker != nullptr)) { return nullptr; }
3402 return maker(referenceFrame, buffer, alloc, client);
3403 }
3404
SkMakeSlugFromBuffer(SkReadBuffer & buffer,const SkStrikeClient * client)3405 sk_sp<GrSlug> SkMakeSlugFromBuffer(SkReadBuffer& buffer, const SkStrikeClient* client) {
3406 return Slug::MakeFromBuffer(buffer, client);
3407 }
3408