1 /*
2 * Copyright 2022 Google LLC
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 "src/text/gpu/SubRunContainer.h"
9
10 #include "include/core/SkMatrix.h"
11 #include "include/core/SkScalar.h"
12 #include "include/core/SkTypes.h"
13 #include "include/private/base/SkOnce.h"
14 #include "include/private/chromium/SkChromeRemoteGlyphCache.h"
15 #include "src/core/SkDescriptor.h"
16 #include "src/core/SkDistanceFieldGen.h"
17 #include "src/core/SkEnumerate.h"
18 #include "src/core/SkGlyph.h"
19 #include "src/core/SkGlyphBuffer.h"
20 #include "src/core/SkReadBuffer.h"
21 #include "src/core/SkRectPriv.h"
22 #include "src/core/SkStrike.h"
23 #include "src/core/SkStrikeCache.h"
24 #include "src/gpu/AtlasTypes.h"
25 #include "src/text/GlyphRun.h"
26 #include "src/text/StrikeForGPU.h"
27 #include "src/text/gpu/Glyph.h"
28 #include "src/text/gpu/GlyphVector.h"
29 #include "src/text/gpu/SubRunAllocator.h"
30
31 #if defined(SK_GANESH) // Ganesh Support
32 #include "src/gpu/ganesh/GrClip.h"
33 #include "src/gpu/ganesh/GrStyle.h"
34 #include "src/gpu/ganesh/SkGr.h"
35 #include "src/gpu/ganesh/SurfaceDrawContext.h"
36 #include "src/gpu/ganesh/ops/AtlasTextOp.h"
37 using AtlasTextOp = skgpu::ganesh::AtlasTextOp;
38 #endif // defined(SK_GANESH)
39
40 #if defined(SK_GRAPHITE)
41 #include "src/gpu/graphite/Device.h"
42 #include "src/gpu/graphite/DrawWriter.h"
43 #include "src/gpu/graphite/Renderer.h"
44 #include "src/gpu/graphite/RendererProvider.h"
45 #endif
46
47 #include <cinttypes>
48 #include <cmath>
49 #include <optional>
50
51 using namespace skglyph;
52
53 // -- GPU Text -------------------------------------------------------------------------------------
54 // Naming conventions
55 // * drawMatrix - the CTM from the canvas.
56 // * drawOrigin - the x, y location of the drawTextBlob call.
57 // * positionMatrix - this is the combination of the drawMatrix and the drawOrigin:
58 // positionMatrix = drawMatrix * TranslationMatrix(drawOrigin.x, drawOrigin.y);
59 //
60 // Note:
61 // In order to transform Slugs, you need to set the fSupportBilerpFromGlyphAtlas on
62 // GrContextOptions.
63
64 namespace sktext::gpu {
65 // -- SubRunType -----------------------------------------------------------------------------------
66 enum SubRun::SubRunType : int {
67 kBad = 0, // Make this 0 to line up with errors from readInt.
68 kDirectMask,
69 #if !defined(SK_DISABLE_SDF_TEXT)
70 kSDFT,
71 #endif
72 kTransformMask,
73 kPath,
74 kDrawable,
75 kSubRunTypeCount,
76 };
77
78 #if defined(SK_GRAPHITE)
79 // AtlasSubRun provides a draw() function that grants the anonymous subclasses access to
80 // Device::drawAtlasSubRun.
draw(skgpu::graphite::Device * device,SkPoint drawOrigin,const SkPaint & paint,sk_sp<SkRefCnt> subRunStorage) const81 void AtlasSubRun::draw(skgpu::graphite::Device* device,
82 SkPoint drawOrigin,
83 const SkPaint& paint,
84 sk_sp<SkRefCnt> subRunStorage) const {
85 device->drawAtlasSubRun(this, drawOrigin, paint, std::move(subRunStorage));
86 }
87 #endif
88
89 } // namespace sktext::gpu
90
91 using MaskFormat = skgpu::MaskFormat;
92
93 using namespace sktext;
94 using namespace sktext::gpu;
95
96 #if defined(SK_GRAPHITE)
97 namespace gr = skgpu::graphite;
98
99 using BindBufferInfo = gr::BindBufferInfo;
100 using BufferType = gr::BufferType;
101 using Device = gr::Device;
102 using DrawWriter = gr::DrawWriter;
103 using Recorder = gr::Recorder;
104 using Renderer = gr::Renderer;
105 using RendererProvider = gr::RendererProvider;
106 using TextureProxy = gr::TextureProxy;
107 using Transform = gr::Transform;
108 #endif
109
110 namespace {
111 // Returns the empty span if there is a problem reading the positions.
make_points_from_buffer(SkReadBuffer & buffer,SubRunAllocator * alloc)112 SkSpan<SkPoint> make_points_from_buffer(SkReadBuffer& buffer, SubRunAllocator* alloc) {
113 uint32_t glyphCount = buffer.getArrayCount();
114
115 // Zero indicates a problem with serialization.
116 if (!buffer.validate(glyphCount != 0)) { return {}; }
117
118 // Check that the count will not overflow the arena.
119 if (!buffer.validate(glyphCount <= INT_MAX &&
120 BagOfBytes::WillCountFit<SkPoint>(glyphCount))) { return {}; }
121
122 SkPoint* positionsData = alloc->makePODArray<SkPoint>(glyphCount);
123 if (!buffer.readPointArray(positionsData, glyphCount)) { return {}; }
124 return {positionsData, glyphCount};
125 }
126
127 // -- TransformedMaskVertexFiller ------------------------------------------------------------------
128 // The TransformedMaskVertexFiller assumes that all points, glyph atlas entries, and bounds are
129 // created with respect to the CreationMatrix. This assumes that mapping any point, mask or
130 // bounds through the CreationMatrix will result in the proper device position. In order to draw
131 // using an arbitrary PositionMatrix, calculate a
132 //
133 // viewDifference = [PositionMatrix] * [CreationMatrix] ^ -1.
134 //
135 // The viewDifference is used to map all points, masks and bounds to position to the device
136 // respecting the PositionMatrix.
137 class TransformedMaskVertexFiller {
138 public:
TransformedMaskVertexFiller(MaskFormat maskFormat,const SkMatrix & creationMatrix,SkRect creationBounds,SkSpan<const SkPoint> leftTop)139 TransformedMaskVertexFiller(MaskFormat maskFormat,
140 const SkMatrix& creationMatrix,
141 SkRect creationBounds,
142 SkSpan<const SkPoint> leftTop)
143 : fMaskType{maskFormat}
144 , fCreationMatrix{creationMatrix}
145 , fCreationBounds{creationBounds}
146 , fLeftTop{leftTop} {}
147
Make(MaskFormat maskType,const SkMatrix & creationMatrix,SkRect creationBounds,const SkZip<SkPackedGlyphID,SkPoint> & accepted,SubRunAllocator * alloc)148 static TransformedMaskVertexFiller Make(MaskFormat maskType,
149 const SkMatrix& creationMatrix,
150 SkRect creationBounds,
151 const SkZip<SkPackedGlyphID, SkPoint>& accepted,
152 SubRunAllocator* alloc) {
153 SkSpan<SkPoint> leftTop = alloc->makePODArray<SkPoint>(
154 accepted,
155 [&](auto e) -> SkPoint {
156 auto [variant, pos] = e;
157 return pos;
158 });
159 return TransformedMaskVertexFiller{maskType, creationMatrix, creationBounds, leftTop};
160 }
161
162 static std::optional<TransformedMaskVertexFiller> MakeFromBuffer(
163 SkReadBuffer& buffer, SubRunAllocator* alloc);
164
165 int unflattenSize() const;
166 void flatten(SkWriteBuffer& buffer) const;
167
viewDifference(const SkMatrix & positionMatrix) const168 SkMatrix viewDifference(const SkMatrix& positionMatrix) const {
169 if (SkMatrix inverse; fCreationMatrix.invert(&inverse)) {
170 return SkMatrix::Concat(positionMatrix, inverse);
171 }
172 return SkMatrix::I();
173 }
174
175 #if defined(SK_GANESH)
vertexStride(const SkMatrix & matrix) const176 size_t vertexStride(const SkMatrix& matrix) const {
177 if (fMaskType != MaskFormat::kARGB) {
178 // For formats MaskFormat::kA565 and MaskFormat::kA8 where A8 include SDF.
179 return matrix.hasPerspective() ? sizeof(Mask3DVertex) : sizeof(Mask2DVertex);
180 } else {
181 // For format MaskFormat::kARGB
182 return matrix.hasPerspective() ? sizeof(ARGB3DVertex) : sizeof(ARGB2DVertex);
183 }
184 }
185
186 void fillVertexData(int offset, int count,
187 SkSpan<const Glyph*> glyphs,
188 GrColor color,
189 const SkMatrix& positionMatrix,
190 SkIRect clip,
191 void* vertexBuffer) const;
192
193 AtlasTextOp::MaskType opMaskType() const;
194 #endif // defined(SK_GANESH)
195
196 #if defined(SK_GRAPHITE)
197 void fillVertexData(DrawWriter* dw,
198 int offset, int count,
199 int ssboIndex,
200 SkSpan<const Glyph*> glyphs,
201 SkScalar depth,
202 const skgpu::graphite::Transform& toDevice) const;
203 void fillInstanceData(DrawWriter* dw,
204 int offset, int count,
205 unsigned short flags,
206 int ssboIndex,
207 SkSpan<const Glyph*> glyphs,
208 SkScalar depth) const;
209 #endif
210 SkRect deviceRect(const SkMatrix& positionMatrix) const;
creationBounds() const211 SkRect creationBounds() const { return fCreationBounds; }
grMaskType() const212 MaskFormat grMaskType() const { return fMaskType; }
count() const213 int count() const { return SkCount(fLeftTop); }
214
215 private:
216 struct AtlasPt {
217 uint16_t u;
218 uint16_t v;
219 };
220
221 #if defined(SK_GANESH)
222 // Normal text mask, SDFT, or color.
223 struct Mask2DVertex {
224 SkPoint devicePos;
225 GrColor color;
226 AtlasPt atlasPos;
227 };
228
229 struct ARGB2DVertex {
ARGB2DVertex__anon459911e60111::TransformedMaskVertexFiller::ARGB2DVertex230 ARGB2DVertex(SkPoint d, GrColor, AtlasPt a) : devicePos{d}, atlasPos{a} {}
231
232 SkPoint devicePos;
233 AtlasPt atlasPos;
234 };
235
236 // Perspective SDFT or SDFT forced to 3D or perspective color.
237 struct Mask3DVertex {
238 SkPoint3 devicePos;
239 GrColor color;
240 AtlasPt atlasPos;
241 };
242
243 struct ARGB3DVertex {
ARGB3DVertex__anon459911e60111::TransformedMaskVertexFiller::ARGB3DVertex244 ARGB3DVertex(SkPoint3 d, GrColor, AtlasPt a) : devicePos{d}, atlasPos{a} {}
245
246 SkPoint3 devicePos;
247 AtlasPt atlasPos;
248 };
249
250 template<typename Quad, typename VertexData>
251 void fill2D(SkZip<Quad, const Glyph*, const VertexData> quadData,
252 GrColor color,
253 const SkMatrix& viewDifference) const;
254
255 template<typename Quad, typename VertexData>
256 void fill3D(SkZip<Quad, const Glyph*, const VertexData> quadData,
257 GrColor color,
258 const SkMatrix& viewDifference) const;
259 #endif // defined(SK_GANESH)
260
261 const MaskFormat fMaskType;
262 const SkMatrix fCreationMatrix;
263 const SkRect fCreationBounds;
264 const SkSpan<const SkPoint> fLeftTop;
265 };
266
MakeFromBuffer(SkReadBuffer & buffer,SubRunAllocator * alloc)267 std::optional<TransformedMaskVertexFiller> TransformedMaskVertexFiller::MakeFromBuffer(
268 SkReadBuffer& buffer, SubRunAllocator* alloc) {
269 int checkingMaskType = buffer.readInt();
270 if (!buffer.validate(0 <= checkingMaskType && checkingMaskType < skgpu::kMaskFormatCount)) {
271 return std::nullopt;
272 }
273 MaskFormat maskType = (MaskFormat)checkingMaskType;
274
275 SkMatrix creationMatrix;
276 buffer.readMatrix(&creationMatrix);
277
278 SkRect creationBounds = buffer.readRect();
279
280 SkSpan<SkPoint> leftTop = make_points_from_buffer(buffer, alloc);
281 if (leftTop.empty()) { return std::nullopt; }
282
283 SkASSERT(buffer.isValid());
284 return TransformedMaskVertexFiller{maskType, creationMatrix, creationBounds, leftTop};
285 }
286
flatten(SkWriteBuffer & buffer) const287 void TransformedMaskVertexFiller::flatten(SkWriteBuffer& buffer) const {
288 buffer.writeInt(static_cast<int>(fMaskType));
289 buffer.writeMatrix(fCreationMatrix);
290 buffer.writeRect(fCreationBounds);
291 buffer.writePointArray(fLeftTop.data(), SkCount(fLeftTop));
292 }
293
deviceRect(const SkMatrix & positionMatrix) const294 SkRect TransformedMaskVertexFiller::deviceRect(const SkMatrix& positionMatrix) const {
295 SkMatrix viewDiff = this->viewDifference(positionMatrix);
296 return viewDiff.mapRect(fCreationBounds);
297 }
298
unflattenSize() const299 int TransformedMaskVertexFiller::unflattenSize() const {
300 return fLeftTop.size_bytes();
301 }
302
303 #if defined(SK_GANESH)
fillVertexData(int offset,int count,SkSpan<const Glyph * > glyphs,GrColor color,const SkMatrix & positionMatrix,SkIRect clip,void * vertexBuffer) const304 void TransformedMaskVertexFiller::fillVertexData(int offset, int count,
305 SkSpan<const Glyph*> glyphs,
306 GrColor color,
307 const SkMatrix& positionMatrix,
308 SkIRect clip,
309 void* vertexBuffer) const {
310 auto quadData = [&](auto dst) {
311 return SkMakeZip(dst,
312 glyphs.subspan(offset, count),
313 fLeftTop.subspan(offset, count));
314 };
315
316 SkMatrix viewDifference = this->viewDifference(positionMatrix);
317
318 if (!positionMatrix.hasPerspective()) {
319 if (fMaskType == MaskFormat::kARGB) {
320 using Quad = ARGB2DVertex[4];
321 SkASSERT(sizeof(ARGB2DVertex) == this->vertexStride(positionMatrix));
322 this->fill2D(quadData((Quad*)vertexBuffer), color, viewDifference);
323 } else {
324 using Quad = Mask2DVertex[4];
325 SkASSERT(sizeof(Mask2DVertex) == this->vertexStride(positionMatrix));
326 this->fill2D(quadData((Quad*)vertexBuffer), color, viewDifference);
327 }
328 } else {
329 if (fMaskType == MaskFormat::kARGB) {
330 using Quad = ARGB3DVertex[4];
331 SkASSERT(sizeof(ARGB3DVertex) == this->vertexStride(positionMatrix));
332 this->fill3D(quadData((Quad*)vertexBuffer), color, viewDifference);
333 } else {
334 using Quad = Mask3DVertex[4];
335 SkASSERT(sizeof(Mask3DVertex) == this->vertexStride(positionMatrix));
336 this->fill3D(quadData((Quad*)vertexBuffer), color, viewDifference);
337 }
338 }
339 }
340
341 template <typename Quad, typename VertexData>
fill2D(SkZip<Quad,const Glyph *,const VertexData> quadData,GrColor color,const SkMatrix & viewDifference) const342 void TransformedMaskVertexFiller::fill2D(SkZip<Quad, const Glyph*, const VertexData> quadData,
343 GrColor color,
344 const SkMatrix& viewDifference) const {
345 for (auto [quad, glyph, leftTop] : quadData) {
346 auto [l, t] = leftTop;
347 auto [r, b] = leftTop + glyph->fAtlasLocator.widthHeight();
348 SkPoint lt = viewDifference.mapXY(l, t),
349 lb = viewDifference.mapXY(l, b),
350 rt = viewDifference.mapXY(r, t),
351 rb = viewDifference.mapXY(r, b);
352 auto [al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
353 quad[0] = {lt, color, {al, at}}; // L,T
354 quad[1] = {lb, color, {al, ab}}; // L,B
355 quad[2] = {rt, color, {ar, at}}; // R,T
356 quad[3] = {rb, color, {ar, ab}}; // R,B
357 }
358 }
359
360 template <typename Quad, typename VertexData>
fill3D(SkZip<Quad,const Glyph *,const VertexData> quadData,GrColor color,const SkMatrix & viewDifference) const361 void TransformedMaskVertexFiller::fill3D(SkZip<Quad, const Glyph*, const VertexData> quadData,
362 GrColor color,
363 const SkMatrix& viewDifference) const {
364 auto mapXYZ = [&](SkScalar x, SkScalar y) {
365 SkPoint pt{x, y};
366 SkPoint3 result;
367 viewDifference.mapHomogeneousPoints(&result, &pt, 1);
368 return result;
369 };
370 for (auto [quad, glyph, leftTop] : quadData) {
371 auto [l, t] = leftTop;
372 auto [r, b] = leftTop + glyph->fAtlasLocator.widthHeight();
373 SkPoint3 lt = mapXYZ(l, t),
374 lb = mapXYZ(l, b),
375 rt = mapXYZ(r, t),
376 rb = mapXYZ(r, b);
377 auto [al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
378 quad[0] = {lt, color, {al, at}}; // L,T
379 quad[1] = {lb, color, {al, ab}}; // L,B
380 quad[2] = {rt, color, {ar, at}}; // R,T
381 quad[3] = {rb, color, {ar, ab}}; // R,B
382 }
383 }
384
opMaskType() const385 AtlasTextOp::MaskType TransformedMaskVertexFiller::opMaskType() const {
386 switch (fMaskType) {
387 case MaskFormat::kA8: return AtlasTextOp::MaskType::kGrayscaleCoverage;
388 case MaskFormat::kA565: return AtlasTextOp::MaskType::kLCDCoverage;
389 case MaskFormat::kARGB: return AtlasTextOp::MaskType::kColorBitmap;
390 }
391 SkUNREACHABLE;
392 }
393 #endif // defined(SK_GANESH)
394
395 #if defined(SK_GRAPHITE)
fillVertexData(DrawWriter * dw,int offset,int count,int ssboIndex,SkSpan<const Glyph * > glyphs,SkScalar depth,const Transform & toDevice) const396 void TransformedMaskVertexFiller::fillVertexData(DrawWriter* dw,
397 int offset, int count,
398 int ssboIndex,
399 SkSpan<const Glyph*> glyphs,
400 SkScalar depth,
401 const Transform& toDevice) const {
402 auto quadData = [&]() {
403 return SkMakeZip(glyphs.subspan(offset, count),
404 fLeftTop.subspan(offset, count));
405 };
406
407 // TODO: can't handle perspective right now
408 if (toDevice.type() == Transform::Type::kProjection) {
409 return;
410 }
411
412 DrawWriter::Vertices verts{*dw};
413 verts.reserve(6*count);
414 for (auto [glyph, leftTop]: quadData()) {
415 auto [al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
416 auto [l, t] = leftTop;
417 auto [r, b] = leftTop + glyph->fAtlasLocator.widthHeight();
418 SkV2 localCorners[4] = {{l, t}, {r, t}, {r, b}, {l, b}};
419 SkV4 devOut[4];
420 toDevice.mapPoints(localCorners, devOut, 4);
421 // TODO: Ganesh uses indices but that's not available with dynamic vertex data
422 // TODO: we should really use instances as well.
423 verts.append(6) << SkPoint{devOut[0].x, devOut[0].y} << depth << AtlasPt{al, at} // L,T
424 << ssboIndex
425 << SkPoint{devOut[3].x, devOut[3].y} << depth << AtlasPt{al, ab} // L,B
426 << ssboIndex
427 << SkPoint{devOut[1].x, devOut[1].y} << depth << AtlasPt{ar, at} // R,T
428 << ssboIndex
429 << SkPoint{devOut[3].x, devOut[3].y} << depth << AtlasPt{al, ab} // L,B
430 << ssboIndex
431 << SkPoint{devOut[2].x, devOut[2].y} << depth << AtlasPt{ar, ab} // R,B
432 << ssboIndex
433 << SkPoint{devOut[1].x, devOut[1].y} << depth << AtlasPt{ar, at} // R,T
434 << ssboIndex;
435 }
436 }
437
fillInstanceData(DrawWriter * dw,int offset,int count,unsigned short flags,int ssboIndex,SkSpan<const Glyph * > glyphs,SkScalar depth) const438 void TransformedMaskVertexFiller::fillInstanceData(DrawWriter* dw,
439 int offset, int count,
440 unsigned short flags,
441 int ssboIndex,
442 SkSpan<const Glyph*> glyphs,
443 SkScalar depth) const {
444 auto quadData = [&]() {
445 return SkMakeZip(glyphs.subspan(offset, count),
446 fLeftTop.subspan(offset, count));
447 };
448
449 DrawWriter::Instances instances{*dw, {}, {}, 4};
450 instances.reserve(count);
451 // Need to send width, height, uvPos, xyPos, and strikeToSourceScale
452 // pre-transform coords = (s*w*b_x + t_x, s*h*b_y + t_y)
453 // where (b_x, b_y) are the vertexID coords
454 for (auto [glyph, leftTop]: quadData()) {
455 auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
456 instances.append(1) << AtlasPt{uint16_t(ar-al), uint16_t(ab-at)}
457 << AtlasPt{uint16_t(al & 0x1fff), at}
458 << leftTop << /*index=*/uint16_t(al >> 13) << flags
459 << 1.0f
460 << depth << ssboIndex;
461 }
462 }
463 #endif
464
465 struct AtlasPt {
466 uint16_t u;
467 uint16_t v;
468 };
469
470 #if defined(SK_GANESH)
471 // Normal text mask, SDFT, or color.
472 struct Mask2DVertex {
473 SkPoint devicePos;
474 GrColor color;
475 AtlasPt atlasPos;
476 };
477
478 struct ARGB2DVertex {
ARGB2DVertex__anon459911e60111::ARGB2DVertex479 ARGB2DVertex(SkPoint d, GrColor, AtlasPt a) : devicePos{d}, atlasPos{a} {}
480
481 SkPoint devicePos;
482 AtlasPt atlasPos;
483 };
484
485 // Perspective SDFT or SDFT forced to 3D or perspective color.
486 struct Mask3DVertex {
487 SkPoint3 devicePos;
488 GrColor color;
489 AtlasPt atlasPos;
490 };
491
492 struct ARGB3DVertex {
ARGB3DVertex__anon459911e60111::ARGB3DVertex493 ARGB3DVertex(SkPoint3 d, GrColor, AtlasPt a) : devicePos{d}, atlasPos{a} {}
494
495 SkPoint3 devicePos;
496 AtlasPt atlasPos;
497 };
498
op_mask_type(MaskFormat maskFormat)499 AtlasTextOp::MaskType op_mask_type(MaskFormat maskFormat) {
500 switch (maskFormat) {
501 case MaskFormat::kA8: return AtlasTextOp::MaskType::kGrayscaleCoverage;
502 case MaskFormat::kA565: return AtlasTextOp::MaskType::kLCDCoverage;
503 case MaskFormat::kARGB: return AtlasTextOp::MaskType::kColorBitmap;
504 }
505 SkUNREACHABLE;
506 }
507
calculate_colors(skgpu::v1::SurfaceDrawContext * sdc,const SkPaint & paint,const SkMatrix & matrix,MaskFormat maskFormat,GrPaint * grPaint)508 SkPMColor4f calculate_colors(skgpu::v1::SurfaceDrawContext* sdc,
509 const SkPaint& paint,
510 const SkMatrix& matrix,
511 MaskFormat maskFormat,
512 GrPaint* grPaint) {
513 GrRecordingContext* rContext = sdc->recordingContext();
514 const GrColorInfo& colorInfo = sdc->colorInfo();
515 const SkSurfaceProps& props = sdc->surfaceProps();
516 if (maskFormat == MaskFormat::kARGB) {
517 SkPaintToGrPaintReplaceShader(rContext, colorInfo, paint, matrix, nullptr, props, grPaint);
518 float a = grPaint->getColor4f().fA;
519 return {a, a, a, a};
520 }
521 SkPaintToGrPaint(rContext, colorInfo, paint, matrix, props, grPaint);
522 return grPaint->getColor4f();
523 }
524
position_matrix(const SkMatrix & drawMatrix,SkPoint drawOrigin)525 SkMatrix position_matrix(const SkMatrix& drawMatrix, SkPoint drawOrigin) {
526 SkMatrix position_matrix = drawMatrix;
527 return position_matrix.preTranslate(drawOrigin.x(), drawOrigin.y());
528 }
529 #endif // defined(SK_GANESH)
530
531 // Check for integer translate with the same 2x2 matrix.
532 // Returns the translation, and true if the change from initial matrix to the position matrix
533 // support using direct glyph masks.
can_use_direct(const SkMatrix & initialPositionMatrix,const SkMatrix & positionMatrix)534 std::tuple<bool, SkVector> can_use_direct(
535 const SkMatrix& initialPositionMatrix, const SkMatrix& positionMatrix) {
536 // The existing direct glyph info can be used if the initialPositionMatrix, and the
537 // positionMatrix have the same 2x2, and the translation between them is integer.
538 // Calculate the translation in source space to a translation in device space by mapping
539 // (0, 0) through both the initial position matrix and the position matrix; take the difference.
540 SkVector translation = positionMatrix.mapOrigin() - initialPositionMatrix.mapOrigin();
541 return {initialPositionMatrix.getScaleX() == positionMatrix.getScaleX() &&
542 initialPositionMatrix.getScaleY() == positionMatrix.getScaleY() &&
543 initialPositionMatrix.getSkewX() == positionMatrix.getSkewX() &&
544 initialPositionMatrix.getSkewY() == positionMatrix.getSkewY() &&
545 SkScalarIsInt(translation.x()) && SkScalarIsInt(translation.y()),
546 translation};
547 }
548
549 // -- PathOpSubmitter ------------------------------------------------------------------------------
550 // PathOpSubmitter holds glyph ids until ready to draw. During drawing, the glyph ids are
551 // converted to SkPaths. PathOpSubmitter can only be serialized when it is holding glyph ids;
552 // it can only be serialized before submitDraws has been called.
553 class PathOpSubmitter {
554 public:
555 PathOpSubmitter() = delete;
556 PathOpSubmitter(const PathOpSubmitter&) = delete;
557 const PathOpSubmitter& operator=(const PathOpSubmitter&) = delete;
PathOpSubmitter(PathOpSubmitter && that)558 PathOpSubmitter(PathOpSubmitter&& that)
559 // Transfer ownership of fIDsOrPaths from that to this.
560 : fIDsOrPaths{std::exchange(
561 const_cast<SkSpan<IDOrPath>&>(that.fIDsOrPaths), SkSpan<IDOrPath>{})}
562 , fPositions{that.fPositions}
563 , fStrikeToSourceScale{that.fStrikeToSourceScale}
564 , fIsAntiAliased{that.fIsAntiAliased}
565 , fStrikePromise{std::move(that.fStrikePromise)} {}
operator =(PathOpSubmitter && that)566 PathOpSubmitter& operator=(PathOpSubmitter&& that) {
567 this->~PathOpSubmitter();
568 new (this) PathOpSubmitter{std::move(that)};
569 return *this;
570 }
571 PathOpSubmitter(bool isAntiAliased,
572 SkScalar strikeToSourceScale,
573 SkSpan<SkPoint> positions,
574 SkSpan<IDOrPath> idsOrPaths,
575 SkStrikePromise&& strikePromise);
576
577 ~PathOpSubmitter();
578
579 static PathOpSubmitter Make(const SkZip<SkPackedGlyphID, SkPoint>& accepted,
580 bool isAntiAliased,
581 SkScalar strikeToSourceScale,
582 SkStrikePromise&& strikePromise,
583 SubRunAllocator* alloc);
584
585 int unflattenSize() const;
586 void flatten(SkWriteBuffer& buffer) const;
587 static std::optional<PathOpSubmitter> MakeFromBuffer(SkReadBuffer& buffer,
588 SubRunAllocator* alloc,
589 const SkStrikeClient* client);
590
591 // submitDraws is not thread safe. It only occurs the single thread drawing portion of the GPU
592 // rendering.
593 void submitDraws(SkCanvas*,
594 SkPoint drawOrigin,
595 const SkPaint& paint) const;
596
597 private:
598 // When PathOpSubmitter is created only the glyphIDs are needed, during the submitDraws call,
599 // the glyphIDs are converted to SkPaths.
600 const SkSpan<IDOrPath> fIDsOrPaths;
601 const SkSpan<const SkPoint> fPositions;
602 const SkScalar fStrikeToSourceScale;
603 const bool fIsAntiAliased;
604
605 mutable SkStrikePromise fStrikePromise;
606 mutable SkOnce fConvertIDsToPaths;
607 mutable bool fPathsAreCreated{false};
608 };
609
unflattenSize() const610 int PathOpSubmitter::unflattenSize() const {
611 return fPositions.size_bytes() + fIDsOrPaths.size_bytes();
612 }
613
flatten(SkWriteBuffer & buffer) const614 void PathOpSubmitter::flatten(SkWriteBuffer& buffer) const {
615 fStrikePromise.flatten(buffer);
616
617 buffer.writeInt(fIsAntiAliased);
618 buffer.writeScalar(fStrikeToSourceScale);
619 buffer.writePointArray(fPositions.data(), SkCount(fPositions));
620 for (IDOrPath& idOrPath : fIDsOrPaths) {
621 buffer.writeInt(idOrPath.fGlyphID);
622 }
623 }
624
MakeFromBuffer(SkReadBuffer & buffer,SubRunAllocator * alloc,const SkStrikeClient * client)625 std::optional<PathOpSubmitter> PathOpSubmitter::MakeFromBuffer(SkReadBuffer& buffer,
626 SubRunAllocator* alloc,
627 const SkStrikeClient* client) {
628 std::optional<SkStrikePromise> strikePromise =
629 SkStrikePromise::MakeFromBuffer(buffer, client, SkStrikeCache::GlobalStrikeCache());
630 if (!buffer.validate(strikePromise.has_value())) {
631 return std::nullopt;
632 }
633
634 bool isAntiAlias = buffer.readInt();
635
636 SkScalar strikeToSourceScale = buffer.readScalar();
637 if (!buffer.validate(0 < strikeToSourceScale)) { return std::nullopt; }
638
639 SkSpan<SkPoint> positions = make_points_from_buffer(buffer, alloc);
640 if (positions.empty()) { return std::nullopt; }
641 const int glyphCount = SkCount(positions);
642
643 // Remember, we stored an int for glyph id.
644 if (!buffer.validateCanReadN<int>(glyphCount)) { return std::nullopt; }
645 auto idsOrPaths = SkSpan(alloc->makeUniqueArray<IDOrPath>(glyphCount).release(), glyphCount);
646 for (auto& idOrPath : idsOrPaths) {
647 idOrPath.fGlyphID = SkTo<SkGlyphID>(buffer.readInt());
648 }
649
650 if (!buffer.isValid()) { return std::nullopt; }
651
652 return PathOpSubmitter{isAntiAlias,
653 strikeToSourceScale,
654 positions,
655 idsOrPaths,
656 std::move(strikePromise.value())};
657 }
658
PathOpSubmitter(bool isAntiAliased,SkScalar strikeToSourceScale,SkSpan<SkPoint> positions,SkSpan<IDOrPath> idsOrPaths,SkStrikePromise && strikePromise)659 PathOpSubmitter::PathOpSubmitter(
660 bool isAntiAliased,
661 SkScalar strikeToSourceScale,
662 SkSpan<SkPoint> positions,
663 SkSpan<IDOrPath> idsOrPaths,
664 SkStrikePromise&& strikePromise)
665 : fIDsOrPaths{idsOrPaths}
666 , fPositions{positions}
667 , fStrikeToSourceScale{strikeToSourceScale}
668 , fIsAntiAliased{isAntiAliased}
669 , fStrikePromise{std::move(strikePromise)} {
670 SkASSERT(!fPositions.empty());
671 }
672
~PathOpSubmitter()673 PathOpSubmitter::~PathOpSubmitter() {
674 // If we have converted glyph IDs to paths, then clean up the SkPaths.
675 if (fPathsAreCreated) {
676 for (auto& idOrPath : fIDsOrPaths) {
677 idOrPath.fPath.~SkPath();
678 }
679 }
680 }
681
Make(const SkZip<SkPackedGlyphID,SkPoint> & accepted,bool isAntiAliased,SkScalar strikeToSourceScale,SkStrikePromise && strikePromise,SubRunAllocator * alloc)682 PathOpSubmitter PathOpSubmitter::Make(const SkZip<SkPackedGlyphID, SkPoint>& accepted,
683 bool isAntiAliased,
684 SkScalar strikeToSourceScale,
685 SkStrikePromise&& strikePromise,
686 SubRunAllocator* alloc) {
687 int glyphCount = SkCount(accepted);
688 SkPoint* positions = alloc->makePODArray<SkPoint>(glyphCount);
689 IDOrPath* idsOrPaths = alloc->makeUniqueArray<IDOrPath>(glyphCount).release();
690
691 for (auto [dstIdOrPath, dstPosition, srcPackedGlyphID, srcPosition] :
692 SkMakeZip(idsOrPaths, positions, accepted.get<0>(), accepted.get<1>())) {
693 dstPosition = srcPosition;
694 dstIdOrPath.fGlyphID = srcPackedGlyphID.glyphID();
695 }
696
697 return PathOpSubmitter{isAntiAliased,
698 strikeToSourceScale,
699 SkSpan(positions, glyphCount),
700 SkSpan(idsOrPaths, glyphCount),
701 std::move(strikePromise)};
702 }
703
704 void
submitDraws(SkCanvas * canvas,SkPoint drawOrigin,const SkPaint & paint) const705 PathOpSubmitter::submitDraws(SkCanvas* canvas, SkPoint drawOrigin, const SkPaint& paint) const {
706 // Convert the glyph IDs to paths if it hasn't been done yet. This is thread safe.
707 fConvertIDsToPaths([&]() {
708 if (SkStrike* strike = fStrikePromise.strike()) {
709 strike->glyphIDsToPaths(fIDsOrPaths);
710
711 // Drop ref to strike so that it can be purged from the cache if needed.
712 fStrikePromise.resetStrike();
713 fPathsAreCreated = true;
714 }
715 });
716
717 SkPaint runPaint{paint};
718 runPaint.setAntiAlias(fIsAntiAliased);
719
720 SkMaskFilterBase* maskFilter = as_MFB(runPaint.getMaskFilter());
721
722 // Calculate the matrix that maps the path glyphs from their size in the strike to
723 // the graphics source space.
724 SkMatrix strikeToSource = SkMatrix::Scale(fStrikeToSourceScale, fStrikeToSourceScale);
725 strikeToSource.postTranslate(drawOrigin.x(), drawOrigin.y());
726
727 // If there are shaders, non-blur mask filters or styles, the path must be scaled into source
728 // space independently of the CTM. This allows the CTM to be correct for the different effects.
729 SkStrokeRec style(runPaint);
730 bool needsExactCTM = runPaint.getShader()
731 || runPaint.getPathEffect()
732 || (!style.isFillStyle() && !style.isHairlineStyle())
733 || (maskFilter != nullptr && !maskFilter->asABlur(nullptr));
734 if (!needsExactCTM) {
735 SkMaskFilterBase::BlurRec blurRec;
736
737 // If there is a blur mask filter, then sigma needs to be adjusted to account for the
738 // scaling of fStrikeToSourceScale.
739 if (maskFilter != nullptr && maskFilter->asABlur(&blurRec)) {
740 runPaint.setMaskFilter(
741 SkMaskFilter::MakeBlur(blurRec.fStyle, blurRec.fSigma / fStrikeToSourceScale));
742 }
743 for (auto [idOrPath, pos] : SkMakeZip(fIDsOrPaths, fPositions)) {
744 // Transform the glyph to source space.
745 SkMatrix pathMatrix = strikeToSource;
746 pathMatrix.postTranslate(pos.x(), pos.y());
747
748 SkAutoCanvasRestore acr(canvas, true);
749 canvas->concat(pathMatrix);
750 canvas->drawPath(idOrPath.fPath, runPaint);
751 }
752 } else {
753 // Transform the path to device because the deviceMatrix must be unchanged to
754 // draw effect, filter or shader paths.
755 for (auto [idOrPath, pos] : SkMakeZip(fIDsOrPaths, fPositions)) {
756 // Transform the glyph to source space.
757 SkMatrix pathMatrix = strikeToSource;
758 pathMatrix.postTranslate(pos.x(), pos.y());
759
760 SkPath deviceOutline;
761 idOrPath.fPath.transform(pathMatrix, &deviceOutline);
762 deviceOutline.setIsVolatile(true);
763 canvas->drawPath(deviceOutline, runPaint);
764 }
765 }
766 }
767
768 // -- PathSubRun -----------------------------------------------------------------------------------
769 class PathSubRun final : public SubRun {
770 public:
PathSubRun(PathOpSubmitter && pathDrawing)771 PathSubRun(PathOpSubmitter&& pathDrawing) : fPathDrawing(std::move(pathDrawing)) {}
772
Make(const SkZip<SkPackedGlyphID,SkPoint> & accepted,bool isAntiAliased,SkScalar strikeToSourceScale,SkStrikePromise && strikePromise,SubRunAllocator * alloc)773 static SubRunOwner Make(const SkZip<SkPackedGlyphID, SkPoint>& accepted,
774 bool isAntiAliased,
775 SkScalar strikeToSourceScale,
776 SkStrikePromise&& strikePromise,
777 SubRunAllocator* alloc) {
778 return alloc->makeUnique<PathSubRun>(
779 PathOpSubmitter::Make(
780 accepted, isAntiAliased, strikeToSourceScale, std::move(strikePromise), alloc));
781 }
782
783 #if defined(SK_GANESH)
draw(SkCanvas * canvas,const GrClip *,const SkMatrixProvider &,SkPoint drawOrigin,const SkPaint & paint,sk_sp<SkRefCnt>,skgpu::v1::SurfaceDrawContext *) const784 void draw(SkCanvas* canvas,
785 const GrClip*,
786 const SkMatrixProvider&,
787 SkPoint drawOrigin,
788 const SkPaint& paint,
789 sk_sp<SkRefCnt>,
790 skgpu::v1::SurfaceDrawContext*) const override {
791 fPathDrawing.submitDraws(canvas, drawOrigin, paint);
792 }
793 #endif // defined(SK_GANESH)
794 #if defined(SK_GRAPHITE)
draw(SkCanvas * canvas,SkPoint drawOrigin,const SkPaint & paint,sk_sp<SkRefCnt> subRunStorage,Device * device) const795 void draw(SkCanvas* canvas,
796 SkPoint drawOrigin,
797 const SkPaint& paint,
798 sk_sp<SkRefCnt> subRunStorage,
799 Device* device) const override {
800 fPathDrawing.submitDraws(canvas, drawOrigin, paint);
801 }
802 #endif // SK_GRAPHITE
803
804 int unflattenSize() const override;
805
canReuse(const SkPaint & paint,const SkMatrix & positionMatrix) const806 bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const override {
807 return true;
808 }
testingOnly_atlasSubRun() const809 const AtlasSubRun* testingOnly_atlasSubRun() const override { return nullptr; }
810 static SubRunOwner MakeFromBuffer(const SkMatrix& initialPositionMatrix,
811 SkReadBuffer& buffer,
812 SubRunAllocator* alloc,
813 const SkStrikeClient* client);
814
815 protected:
subRunType() const816 SubRunType subRunType() const override { return kPath; }
817 void doFlatten(SkWriteBuffer& buffer) const override;
818
819 private:
820 PathOpSubmitter fPathDrawing;
821 };
822
unflattenSize() const823 int PathSubRun::unflattenSize() const {
824 return sizeof(PathSubRun) + fPathDrawing.unflattenSize();
825 }
826
doFlatten(SkWriteBuffer & buffer) const827 void PathSubRun::doFlatten(SkWriteBuffer& buffer) const {
828 fPathDrawing.flatten(buffer);
829 }
830
MakeFromBuffer(const SkMatrix & initialPositionMatrix,SkReadBuffer & buffer,SubRunAllocator * alloc,const SkStrikeClient * client)831 SubRunOwner PathSubRun::MakeFromBuffer(const SkMatrix& initialPositionMatrix,
832 SkReadBuffer& buffer,
833 SubRunAllocator* alloc,
834 const SkStrikeClient* client) {
835 auto pathOpSubmitter = PathOpSubmitter::MakeFromBuffer(buffer, alloc, client);
836 if (!buffer.validate(pathOpSubmitter.has_value())) { return nullptr; }
837 return alloc->makeUnique<PathSubRun>(std::move(*pathOpSubmitter));
838 }
839
840 // -- DrawableOpSubmitter --------------------------------------------------------------------------
841 // Shared code for submitting GPU ops for drawing glyphs as drawables.
842 class DrawableOpSubmitter {
843 public:
844 DrawableOpSubmitter() = delete;
845 DrawableOpSubmitter(const DrawableOpSubmitter&) = delete;
846 const DrawableOpSubmitter& operator=(const DrawableOpSubmitter&) = delete;
DrawableOpSubmitter(DrawableOpSubmitter && that)847 DrawableOpSubmitter(DrawableOpSubmitter&& that)
848 : fStrikeToSourceScale{that.fStrikeToSourceScale}
849 , fPositions{that.fPositions}
850 , fIDsOrDrawables{that.fIDsOrDrawables}
851 , fStrikePromise{std::move(that.fStrikePromise)} {}
operator =(DrawableOpSubmitter && that)852 DrawableOpSubmitter& operator=(DrawableOpSubmitter&& that) {
853 this->~DrawableOpSubmitter();
854 new (this) DrawableOpSubmitter{std::move(that)};
855 return *this;
856 }
857 DrawableOpSubmitter(SkScalar strikeToSourceScale,
858 SkSpan<SkPoint> positions,
859 SkSpan<IDOrDrawable> idsOrDrawables,
860 SkStrikePromise&& strikePromise);
861
862 static DrawableOpSubmitter Make(const SkZip<SkPackedGlyphID, SkPoint>& accepted,
863 SkScalar strikeToSourceScale,
864 SkStrikePromise&& strikePromise,
865 SubRunAllocator* alloc);
866
867 int unflattenSize() const;
868 void flatten(SkWriteBuffer& buffer) const;
869 static std::optional<DrawableOpSubmitter> MakeFromBuffer(SkReadBuffer& buffer,
870 SubRunAllocator* alloc,
871 const SkStrikeClient* client);
872 void submitDraws(SkCanvas* canvas, SkPoint drawOrigin, const SkPaint& paint) const;
873
874 private:
875 const SkScalar fStrikeToSourceScale;
876 const SkSpan<SkPoint> fPositions;
877 const SkSpan<IDOrDrawable> fIDsOrDrawables;
878 // When the promise is converted to a strike it acts as the ref on the strike to keep the
879 // SkDrawable data alive.
880 mutable SkStrikePromise fStrikePromise;
881 mutable SkOnce fConvertIDsToDrawables;
882 };
883
unflattenSize() const884 int DrawableOpSubmitter::unflattenSize() const {
885 return fPositions.size_bytes() + fIDsOrDrawables.size_bytes();
886 }
887
flatten(SkWriteBuffer & buffer) const888 void DrawableOpSubmitter::flatten(SkWriteBuffer& buffer) const {
889 fStrikePromise.flatten(buffer);
890
891 buffer.writeScalar(fStrikeToSourceScale);
892 buffer.writePointArray(fPositions.data(), SkCount(fPositions));
893 for (IDOrDrawable idOrDrawable : fIDsOrDrawables) {
894 buffer.writeInt(idOrDrawable.fGlyphID);
895 }
896 }
897
MakeFromBuffer(SkReadBuffer & buffer,SubRunAllocator * alloc,const SkStrikeClient * client)898 std::optional<DrawableOpSubmitter> DrawableOpSubmitter::MakeFromBuffer(
899 SkReadBuffer& buffer, SubRunAllocator* alloc, const SkStrikeClient* client) {
900 std::optional<SkStrikePromise> strikePromise =
901 SkStrikePromise::MakeFromBuffer(buffer, client, SkStrikeCache::GlobalStrikeCache());
902 if (!buffer.validate(strikePromise.has_value())) {
903 return std::nullopt;
904 }
905
906 SkScalar strikeToSourceScale = buffer.readScalar();
907 if (!buffer.validate(0 < strikeToSourceScale)) { return std::nullopt; }
908
909 SkSpan<SkPoint> positions = make_points_from_buffer(buffer, alloc);
910 if (positions.empty()) { return std::nullopt; }
911 const int glyphCount = SkCount(positions);
912
913 if (!buffer.validateCanReadN<int>(glyphCount)) { return std::nullopt; }
914 auto idsOrDrawables = alloc->makePODArray<IDOrDrawable>(glyphCount);
915 for (int i = 0; i < SkToInt(glyphCount); ++i) {
916 // Remember, we stored an int for glyph id.
917 idsOrDrawables[i].fGlyphID = SkTo<SkGlyphID>(buffer.readInt());
918 }
919
920 SkASSERT(buffer.isValid());
921 return DrawableOpSubmitter{strikeToSourceScale,
922 positions,
923 SkSpan(idsOrDrawables, glyphCount),
924 std::move(strikePromise.value())};
925 }
926
DrawableOpSubmitter(SkScalar strikeToSourceScale,SkSpan<SkPoint> positions,SkSpan<IDOrDrawable> idsOrDrawables,SkStrikePromise && strikePromise)927 DrawableOpSubmitter::DrawableOpSubmitter(
928 SkScalar strikeToSourceScale,
929 SkSpan<SkPoint> positions,
930 SkSpan<IDOrDrawable> idsOrDrawables,
931 SkStrikePromise&& strikePromise)
932 : fStrikeToSourceScale{strikeToSourceScale}
933 , fPositions{positions}
934 , fIDsOrDrawables{idsOrDrawables}
935 , fStrikePromise(std::move(strikePromise)) {
936 SkASSERT(!fPositions.empty());
937 }
938
Make(const SkZip<SkPackedGlyphID,SkPoint> & accepted,SkScalar strikeToSourceScale,SkStrikePromise && strikePromise,SubRunAllocator * alloc)939 DrawableOpSubmitter DrawableOpSubmitter::Make(const SkZip<SkPackedGlyphID, SkPoint>& accepted,
940 SkScalar strikeToSourceScale,
941 SkStrikePromise&& strikePromise,
942 SubRunAllocator* alloc) {
943 int glyphCount = SkCount(accepted);
944 SkPoint* positions = alloc->makePODArray<SkPoint>(glyphCount);
945 IDOrDrawable* idsOrDrawables = alloc->makePODArray<IDOrDrawable>(glyphCount);
946 for (auto [i, variant, pos] : SkMakeEnumerate(accepted)) {
947 positions[i] = pos;
948 idsOrDrawables[i].fGlyphID = variant.glyphID();
949 }
950
951 return DrawableOpSubmitter{strikeToSourceScale,
952 SkSpan(positions, glyphCount),
953 SkSpan(idsOrDrawables, glyphCount),
954 std::move(strikePromise)};
955 }
956
957 void
submitDraws(SkCanvas * canvas,SkPoint drawOrigin,const SkPaint & paint) const958 DrawableOpSubmitter::submitDraws(SkCanvas* canvas, SkPoint drawOrigin,const SkPaint& paint) const {
959 // Convert glyph IDs to Drawables if it hasn't been done yet.
960 fConvertIDsToDrawables([&]() {
961 fStrikePromise.strike()->glyphIDsToDrawables(fIDsOrDrawables);
962 // Do not call resetStrike() because the strike must remain owned to ensure the Drawable
963 // data is not freed.
964 });
965
966 // Calculate the matrix that maps the path glyphs from their size in the strike to
967 // the graphics source space.
968 SkMatrix strikeToSource = SkMatrix::Scale(fStrikeToSourceScale, fStrikeToSourceScale);
969 strikeToSource.postTranslate(drawOrigin.x(), drawOrigin.y());
970
971 // Transform the path to device because the deviceMatrix must be unchanged to
972 // draw effect, filter or shader paths.
973 for (auto [i, position] : SkMakeEnumerate(fPositions)) {
974 SkDrawable* drawable = fIDsOrDrawables[i].fDrawable;
975
976 if (drawable == nullptr) {
977 // This better be pinned to keep the drawable data alive.
978 fStrikePromise.strike()->verifyPinnedStrike();
979 SkDEBUGFAIL("Drawable should not be nullptr.");
980 continue;
981 }
982
983 // Transform the glyph to source space.
984 SkMatrix pathMatrix = strikeToSource;
985 pathMatrix.postTranslate(position.x(), position.y());
986
987 SkAutoCanvasRestore acr(canvas, false);
988 SkRect drawableBounds = drawable->getBounds();
989 pathMatrix.mapRect(&drawableBounds);
990 canvas->saveLayer(&drawableBounds, &paint);
991 drawable->draw(canvas, &pathMatrix);
992 }
993 }
994
995 template <typename SubRunT>
make_drawable_sub_run(const SkZip<SkPackedGlyphID,SkPoint> & drawables,SkScalar strikeToSourceScale,SkStrikePromise && strikePromise,SubRunAllocator * alloc)996 SubRunOwner make_drawable_sub_run(const SkZip<SkPackedGlyphID, SkPoint>& drawables,
997 SkScalar strikeToSourceScale,
998 SkStrikePromise&& strikePromise,
999 SubRunAllocator* alloc) {
1000 return alloc->makeUnique<SubRunT>(
1001 DrawableOpSubmitter::Make(drawables, strikeToSourceScale, std::move(strikePromise), alloc));
1002 }
1003
1004 // -- DrawableSubRun -------------------------------------------------------------------------------
1005 class DrawableSubRun : public SubRun {
1006 public:
DrawableSubRun(DrawableOpSubmitter && drawingDrawing)1007 DrawableSubRun(DrawableOpSubmitter&& drawingDrawing)
1008 : fDrawingDrawing(std::move(drawingDrawing)) {}
1009
1010 static SubRunOwner MakeFromBuffer(const SkMatrix&,
1011 SkReadBuffer& buffer,
1012 SubRunAllocator* alloc,
1013 const SkStrikeClient* client);
1014 #if defined(SK_GANESH)
draw(SkCanvas * canvas,const GrClip * clip,const SkMatrixProvider & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,sk_sp<SkRefCnt> subRunStorage,skgpu::v1::SurfaceDrawContext * sdc) const1015 void draw(SkCanvas* canvas,
1016 const GrClip* clip,
1017 const SkMatrixProvider& viewMatrix,
1018 SkPoint drawOrigin,
1019 const SkPaint& paint,
1020 sk_sp<SkRefCnt> subRunStorage,
1021 skgpu::v1::SurfaceDrawContext* sdc) const override {
1022 fDrawingDrawing.submitDraws(canvas, drawOrigin, paint);
1023 }
1024 #endif // defined(SK_GANESH)
1025 #if defined(SK_GRAPHITE)
draw(SkCanvas * canvas,SkPoint drawOrigin,const SkPaint & paint,sk_sp<SkRefCnt> subRunStorage,Device * device) const1026 void draw(SkCanvas* canvas,
1027 SkPoint drawOrigin,
1028 const SkPaint& paint,
1029 sk_sp<SkRefCnt> subRunStorage,
1030 Device* device) const override {
1031 fDrawingDrawing.submitDraws(canvas, drawOrigin, paint);
1032 }
1033 #endif // SK_GRAPHITE
1034
1035 int unflattenSize() const override;
1036
1037 bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const override;
1038
1039 const AtlasSubRun* testingOnly_atlasSubRun() const override;
1040
1041 protected:
subRunType() const1042 SubRunType subRunType() const override { return kDrawable; }
1043 void doFlatten(SkWriteBuffer& buffer) const override;
1044
1045 private:
1046 DrawableOpSubmitter fDrawingDrawing;
1047 };
1048
unflattenSize() const1049 int DrawableSubRun::unflattenSize() const {
1050 return sizeof(DrawableSubRun) + fDrawingDrawing.unflattenSize();
1051 }
1052
doFlatten(SkWriteBuffer & buffer) const1053 void DrawableSubRun::doFlatten(SkWriteBuffer& buffer) const {
1054 fDrawingDrawing.flatten(buffer);
1055 }
1056
MakeFromBuffer(const SkMatrix &,SkReadBuffer & buffer,SubRunAllocator * alloc,const SkStrikeClient * client)1057 SubRunOwner DrawableSubRun::MakeFromBuffer(const SkMatrix&,
1058 SkReadBuffer& buffer,
1059 SubRunAllocator* alloc,
1060 const SkStrikeClient* client) {
1061 auto drawableOpSubmitter = DrawableOpSubmitter::MakeFromBuffer(buffer, alloc, client);
1062 if (!buffer.validate(drawableOpSubmitter.has_value())) { return nullptr; }
1063 return alloc->makeUnique<DrawableSubRun>(std::move(*drawableOpSubmitter));
1064 }
1065
canReuse(const SkPaint & paint,const SkMatrix & positionMatrix) const1066 bool DrawableSubRun::canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const {
1067 return true;
1068 }
1069
testingOnly_atlasSubRun() const1070 const AtlasSubRun* DrawableSubRun::testingOnly_atlasSubRun() const {
1071 return nullptr;
1072 }
1073
1074 #if defined(SK_GANESH)
1075 enum ClipMethod {
1076 kClippedOut,
1077 kUnclipped,
1078 kGPUClipped,
1079 kGeometryClipped
1080 };
1081
1082 std::tuple<ClipMethod, SkIRect>
calculate_clip(const GrClip * clip,SkRect deviceBounds,SkRect glyphBounds)1083 calculate_clip(const GrClip* clip, SkRect deviceBounds, SkRect glyphBounds) {
1084 if (clip == nullptr && !deviceBounds.intersects(glyphBounds)) {
1085 return {kClippedOut, SkIRect::MakeEmpty()};
1086 } else if (clip != nullptr) {
1087 switch (auto result = clip->preApply(glyphBounds, GrAA::kNo); result.fEffect) {
1088 case GrClip::Effect::kClippedOut:
1089 return {kClippedOut, SkIRect::MakeEmpty()};
1090 case GrClip::Effect::kUnclipped:
1091 return {kUnclipped, SkIRect::MakeEmpty()};
1092 case GrClip::Effect::kClipped: {
1093 if (result.fIsRRect && result.fRRect.isRect()) {
1094 SkRect r = result.fRRect.rect();
1095 if (result.fAA == GrAA::kNo || GrClip::IsPixelAligned(r)) {
1096 SkIRect clipRect = SkIRect::MakeEmpty();
1097 // Clip geometrically during onPrepare using clipRect.
1098 r.round(&clipRect);
1099 if (clipRect.contains(glyphBounds)) {
1100 // If fully within the clip, signal no clipping using the empty rect.
1101 return {kUnclipped, SkIRect::MakeEmpty()};
1102 }
1103 // Use the clipRect to clip the geometry.
1104 return {kGeometryClipped, clipRect};
1105 }
1106 // Partial pixel clipped at this point. Have the GPU handle it.
1107 }
1108 }
1109 break;
1110 }
1111 }
1112 return {kGPUClipped, SkIRect::MakeEmpty()};
1113 }
1114 template <typename Rect>
ltbr(const Rect & r)1115 auto ltbr(const Rect& r) {
1116 return std::make_tuple(r.left(), r.top(), r.right(), r.bottom());
1117 }
1118
1119 // Handle any combination of BW or color and clip or no clip.
1120 template<typename Quad, typename VertexData>
generalized_direct_2D(SkZip<Quad,const Glyph *,const VertexData> quadData,GrColor color,SkPoint originOffset,SkIRect * clip=nullptr)1121 void generalized_direct_2D(SkZip<Quad, const Glyph*, const VertexData> quadData,
1122 GrColor color,
1123 SkPoint originOffset,
1124 SkIRect* clip = nullptr) {
1125 for (auto[quad, glyph, leftTop] : quadData) {
1126 auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
1127 uint16_t w = ar - al,
1128 h = ab - at;
1129 SkScalar l = leftTop.x() + originOffset.x(),
1130 t = leftTop.y() + originOffset.y();
1131 if (clip == nullptr) {
1132 auto[dl, dt, dr, db] = SkRect::MakeLTRB(l, t, l + w, t + h);
1133 quad[0] = {{dl, dt}, color, {al, at}}; // L,T
1134 quad[1] = {{dl, db}, color, {al, ab}}; // L,B
1135 quad[2] = {{dr, dt}, color, {ar, at}}; // R,T
1136 quad[3] = {{dr, db}, color, {ar, ab}}; // R,B
1137 } else {
1138 SkIRect devIRect = SkIRect::MakeLTRB(l, t, l + w, t + h);
1139 SkScalar dl, dt, dr, db;
1140 if (!clip->containsNoEmptyCheck(devIRect)) {
1141 if (SkIRect clipped; clipped.intersect(devIRect, *clip)) {
1142 al += clipped.left() - devIRect.left();
1143 at += clipped.top() - devIRect.top();
1144 ar += clipped.right() - devIRect.right();
1145 ab += clipped.bottom() - devIRect.bottom();
1146 std::tie(dl, dt, dr, db) = ltbr(clipped);
1147 } else {
1148 // TODO: omit generating any vertex data for fully clipped glyphs ?
1149 std::tie(dl, dt, dr, db) = std::make_tuple(0, 0, 0, 0);
1150 std::tie(al, at, ar, ab) = std::make_tuple(0, 0, 0, 0);
1151 }
1152 } else {
1153 std::tie(dl, dt, dr, db) = ltbr(devIRect);
1154 }
1155 quad[0] = {{dl, dt}, color, {al, at}}; // L,T
1156 quad[1] = {{dl, db}, color, {al, ab}}; // L,B
1157 quad[2] = {{dr, dt}, color, {ar, at}}; // R,T
1158 quad[3] = {{dr, db}, color, {ar, ab}}; // R,B
1159 }
1160 }
1161 }
1162
1163 // The 99% case. No clip. Non-color only.
direct_2D(SkZip<Mask2DVertex[4],const Glyph *,const SkPoint> quadData,GrColor color,SkPoint originOffset)1164 void direct_2D(SkZip<Mask2DVertex[4],
1165 const Glyph*,
1166 const SkPoint> quadData,
1167 GrColor color,
1168 SkPoint originOffset) {
1169 for (auto[quad, glyph, leftTop] : quadData) {
1170 auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
1171 SkScalar dl = leftTop.x() + originOffset.x(),
1172 dt = leftTop.y() + originOffset.y(),
1173 dr = dl + (ar - al),
1174 db = dt + (ab - at);
1175
1176 quad[0] = {{dl, dt}, color, {al, at}}; // L,T
1177 quad[1] = {{dl, db}, color, {al, ab}}; // L,B
1178 quad[2] = {{dr, dt}, color, {ar, at}}; // R,T
1179 quad[3] = {{dr, db}, color, {ar, ab}}; // R,B
1180 }
1181 }
1182 #endif // defined(SK_GANESH)
1183
1184 // -- DirectMaskSubRun -------------------------------------------------------------------------
1185 class DirectMaskSubRun final : public SubRun, public AtlasSubRun {
1186 public:
1187 DirectMaskSubRun(MaskFormat format,
1188 const SkMatrix& initialPositionMatrix,
1189 SkRect deviceBounds,
1190 SkSpan<const SkPoint> devicePositions,
1191 GlyphVector&& glyphs);
1192
1193 static SubRunOwner Make(SkRect runBounds,
1194 const SkZip<SkPackedGlyphID, SkPoint>& accepted,
1195 const SkMatrix& initialPositionMatrix,
1196 SkStrikePromise&& strikePromise,
1197 MaskFormat format,
1198 SubRunAllocator* alloc);
1199
1200 static SubRunOwner MakeFromBuffer(const SkMatrix& initialPositionMatrix,
1201 SkReadBuffer& buffer,
1202 SubRunAllocator* alloc,
1203 const SkStrikeClient* client);
1204 #if defined(SK_GANESH)
1205 void draw(SkCanvas*,
1206 const GrClip* clip,
1207 const SkMatrixProvider& viewMatrix,
1208 SkPoint drawOrigin,
1209 const SkPaint& paint,
1210 sk_sp<SkRefCnt> subRunOwner,
1211 skgpu::v1::SurfaceDrawContext* sdc) const override;
1212 #endif // defined(SK_GANESH)
1213
1214 #if defined(SK_GRAPHITE)
1215 void draw(SkCanvas*,
1216 SkPoint drawOrigin,
1217 const SkPaint&,
1218 sk_sp<SkRefCnt> subRunStorage,
1219 Device*) const override;
1220 #endif
1221
1222 int unflattenSize() const override;
1223
1224 int glyphCount() const override;
maskFormat() const1225 MaskFormat maskFormat() const override { return fMaskFormat; }
1226
1227 void testingOnly_packedGlyphIDToGlyph(StrikeCache* cache) const override;
1228
1229 #if defined(SK_GANESH)
1230 size_t vertexStride(const SkMatrix& drawMatrix) const override;
1231
1232 std::tuple<const GrClip*, GrOp::Owner>
1233 makeAtlasTextOp(const GrClip*,
1234 const SkMatrixProvider& viewMatrix,
1235 SkPoint,
1236 const SkPaint&,
1237 sk_sp<SkRefCnt>&& subRunStorage,
1238 skgpu::v1::SurfaceDrawContext*) const override;
1239
1240 std::tuple<bool, int>
1241 regenerateAtlas(int begin, int end, GrMeshDrawTarget*) const override;
1242
1243 void fillVertexData(void* vertexDst, int offset, int count,
1244 GrColor color,
1245 const SkMatrix& drawMatrix, SkPoint drawOrigin,
1246 SkIRect clip) const override;
1247 #endif // defined(SK_GANESH)
1248
1249 #if defined(SK_GRAPHITE)
1250 std::tuple<bool, int>
1251 regenerateAtlas(int begin, int end, Recorder*) const override;
1252
1253 std::tuple<gr::Rect, Transform> boundsAndDeviceMatrix(const Transform&,
1254 SkPoint drawOrigin) const override;
1255
renderer(const RendererProvider * renderers) const1256 const Renderer* renderer(const RendererProvider* renderers) const override {
1257 return renderers->bitmapText();
1258 }
1259
1260 void fillInstanceData(skgpu::graphite::DrawWriter*,
1261 int offset, int count,
1262 int ssboIndex,
1263 SkScalar depth) const override;
1264 #endif
1265
1266 bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const override;
1267
1268 const AtlasSubRun* testingOnly_atlasSubRun() const override;
1269
1270 protected:
subRunType() const1271 SubRunType subRunType() const override { return kDirectMask; }
1272 void doFlatten(SkWriteBuffer& buffer) const override;
1273
1274 private:
1275 // Return true if the positionMatrix represents an integer translation. Return the device
1276 // bounding box of all the glyphs. If the bounding box is empty, then something went singular
1277 // and this operation should be dropped.
1278 std::tuple<bool, SkRect> deviceRectAndCheckTransform(const SkMatrix& positionMatrix) const;
1279
1280 const MaskFormat fMaskFormat;
1281 const SkMatrix& fInitialPositionMatrix;
1282
1283 // The vertex bounds in device space. The bounds are the joined rectangles of all the glyphs.
1284 const SkRect fGlyphDeviceBounds;
1285 const SkSpan<const SkPoint> fLeftTopDevicePos;
1286
1287 // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
1288 // be single threaded.
1289 mutable GlyphVector fGlyphs;
1290 };
1291
DirectMaskSubRun(MaskFormat format,const SkMatrix & initialPositionMatrix,SkRect deviceBounds,SkSpan<const SkPoint> devicePositions,GlyphVector && glyphs)1292 DirectMaskSubRun::DirectMaskSubRun(MaskFormat format,
1293 const SkMatrix& initialPositionMatrix,
1294 SkRect deviceBounds,
1295 SkSpan<const SkPoint> devicePositions,
1296 GlyphVector&& glyphs)
1297 : fMaskFormat{format}
1298 , fInitialPositionMatrix{initialPositionMatrix}
1299 , fGlyphDeviceBounds{deviceBounds}
1300 , fLeftTopDevicePos{devicePositions}
1301 , fGlyphs{std::move(glyphs)} {}
1302
Make(SkRect runBounds,const SkZip<SkPackedGlyphID,SkPoint> & accepted,const SkMatrix & initialPositionMatrix,SkStrikePromise && strikePromise,MaskFormat format,SubRunAllocator * alloc)1303 SubRunOwner DirectMaskSubRun::Make(SkRect runBounds,
1304 const SkZip<SkPackedGlyphID, SkPoint>& accepted,
1305 const SkMatrix& initialPositionMatrix,
1306 SkStrikePromise&& strikePromise,
1307 MaskFormat format,
1308 SubRunAllocator* alloc) {
1309 auto glyphLeftTop = alloc->makePODArray<SkPoint>(accepted.size());
1310 auto glyphIDs = alloc->makePODArray<GlyphVector::Variant>(accepted.size());
1311
1312 for (auto [i, packedID, pos] : SkMakeEnumerate(accepted)) {
1313 glyphLeftTop[i] = pos;
1314 glyphIDs[i].packedGlyphID = packedID;
1315 }
1316
1317 SkSpan<const SkPoint> leftTop{glyphLeftTop, accepted.size()};
1318 return alloc->makeUnique<DirectMaskSubRun>(
1319 format, initialPositionMatrix, runBounds, leftTop,
1320 GlyphVector{std::move(strikePromise), {glyphIDs, accepted.size()}});
1321 }
1322
canReuse(const SkPaint & paint,const SkMatrix & positionMatrix) const1323 bool DirectMaskSubRun::canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const {
1324 auto [reuse, _] = can_use_direct(fInitialPositionMatrix, positionMatrix);
1325 return reuse;
1326 }
1327
MakeFromBuffer(const SkMatrix & initialPositionMatrix,SkReadBuffer & buffer,SubRunAllocator * alloc,const SkStrikeClient * client)1328 SubRunOwner DirectMaskSubRun::MakeFromBuffer(const SkMatrix& initialPositionMatrix,
1329 SkReadBuffer& buffer,
1330 SubRunAllocator* alloc,
1331 const SkStrikeClient* client) {
1332 MaskFormat maskType = (MaskFormat)buffer.readInt();
1333 SkRect runBounds = buffer.readRect();
1334
1335 SkSpan<SkPoint> leftTop = make_points_from_buffer(buffer, alloc);
1336 if (leftTop.empty()) { return nullptr; }
1337 const int glyphCount = SkCount(leftTop);
1338
1339 auto glyphVector = GlyphVector::MakeFromBuffer(buffer, client, alloc);
1340 if (!buffer.validate(glyphVector.has_value())) { return nullptr; }
1341 if (!buffer.validate(SkCount(glyphVector->glyphs()) == glyphCount)) { return nullptr; }
1342 SkASSERT(buffer.isValid());
1343 return alloc->makeUnique<DirectMaskSubRun>(
1344 maskType, initialPositionMatrix, runBounds, leftTop,
1345 std::move(glyphVector.value()));
1346 }
1347
doFlatten(SkWriteBuffer & buffer) const1348 void DirectMaskSubRun::doFlatten(SkWriteBuffer& buffer) const {
1349 buffer.writeInt(static_cast<int>(fMaskFormat));
1350 buffer.writeRect(fGlyphDeviceBounds);
1351 buffer.writePointArray(fLeftTopDevicePos.data(), SkCount(fLeftTopDevicePos));
1352 fGlyphs.flatten(buffer);
1353 }
1354
unflattenSize() const1355 int DirectMaskSubRun::unflattenSize() const {
1356 return sizeof(DirectMaskSubRun) +
1357 fGlyphs.unflattenSize() +
1358 sizeof(SkPoint) * fGlyphs.glyphs().size();
1359 }
1360
testingOnly_atlasSubRun() const1361 const AtlasSubRun* DirectMaskSubRun::testingOnly_atlasSubRun() const {
1362 return this;
1363 }
1364
glyphCount() const1365 int DirectMaskSubRun::glyphCount() const {
1366 return SkCount(fGlyphs.glyphs());
1367 }
1368
1369 #if defined(SK_GANESH)
vertexStride(const SkMatrix & positionMatrix) const1370 size_t DirectMaskSubRun::vertexStride(const SkMatrix& positionMatrix) const {
1371 if (!positionMatrix.hasPerspective()) {
1372 if (fMaskFormat != MaskFormat::kARGB) {
1373 return sizeof(Mask2DVertex);
1374 } else {
1375 return sizeof(ARGB2DVertex);
1376 }
1377 } else {
1378 if (fMaskFormat != MaskFormat::kARGB) {
1379 return sizeof(Mask3DVertex);
1380 } else {
1381 return sizeof(ARGB3DVertex);
1382 }
1383 }
1384 }
1385
draw(SkCanvas *,const GrClip * clip,const SkMatrixProvider & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,sk_sp<SkRefCnt> subRunStorage,skgpu::v1::SurfaceDrawContext * sdc) const1386 void DirectMaskSubRun::draw(SkCanvas*,
1387 const GrClip* clip,
1388 const SkMatrixProvider& viewMatrix,
1389 SkPoint drawOrigin,
1390 const SkPaint& paint,
1391 sk_sp<SkRefCnt> subRunStorage,
1392 skgpu::v1::SurfaceDrawContext* sdc) const {
1393 auto[drawingClip, op] = this->makeAtlasTextOp(
1394 clip, viewMatrix, drawOrigin, paint, std::move(subRunStorage), sdc);
1395 if (op != nullptr) {
1396 sdc->addDrawOp(drawingClip, std::move(op));
1397 }
1398 }
1399
makeAtlasTextOp(const GrClip * clip,const SkMatrixProvider & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,sk_sp<SkRefCnt> && subRunStorage,skgpu::v1::SurfaceDrawContext * sdc) const1400 std::tuple<const GrClip*, GrOp::Owner> DirectMaskSubRun::makeAtlasTextOp(
1401 const GrClip* clip,
1402 const SkMatrixProvider& viewMatrix,
1403 SkPoint drawOrigin,
1404 const SkPaint& paint,
1405 sk_sp<SkRefCnt>&& subRunStorage,
1406 skgpu::v1::SurfaceDrawContext* sdc) const {
1407 SkASSERT(this->glyphCount() != 0);
1408 const SkMatrix& drawMatrix = viewMatrix.localToDevice();
1409 const SkMatrix& positionMatrix = position_matrix(drawMatrix, drawOrigin);
1410
1411 auto [integerTranslate, subRunDeviceBounds] = this->deviceRectAndCheckTransform(positionMatrix);
1412 if (subRunDeviceBounds.isEmpty()) {
1413 return {nullptr, nullptr};
1414 }
1415 // Rect for optimized bounds clipping when doing an integer translate.
1416 SkIRect geometricClipRect = SkIRect::MakeEmpty();
1417 if (integerTranslate) {
1418 // We can clip geometrically using clipRect and ignore clip when an axis-aligned rectangular
1419 // non-AA clip is used. If clipRect is empty, and clip is nullptr, then there is no clipping
1420 // needed.
1421 const SkRect deviceBounds = SkRect::MakeWH(sdc->width(), sdc->height());
1422 auto [clipMethod, clipRect] = calculate_clip(clip, deviceBounds, subRunDeviceBounds);
1423
1424 switch (clipMethod) {
1425 case kClippedOut:
1426 // Returning nullptr as op means skip this op.
1427 return {nullptr, nullptr};
1428 case kUnclipped:
1429 case kGeometryClipped:
1430 // GPU clip is not needed.
1431 clip = nullptr;
1432 break;
1433 case kGPUClipped:
1434 // Use th GPU clip; clipRect is ignored.
1435 break;
1436 }
1437 geometricClipRect = clipRect;
1438
1439 if (!geometricClipRect.isEmpty()) { SkASSERT(clip == nullptr); }
1440 }
1441
1442 GrPaint grPaint;
1443 const SkPMColor4f drawingColor = calculate_colors(sdc,
1444 paint,
1445 drawMatrix,
1446 fMaskFormat,
1447 &grPaint);
1448
1449 auto geometry = AtlasTextOp::Geometry::Make(*this,
1450 drawMatrix,
1451 drawOrigin,
1452 geometricClipRect,
1453 std::move(subRunStorage),
1454 drawingColor,
1455 sdc->arenaAlloc());
1456
1457 GrRecordingContext* const rContext = sdc->recordingContext();
1458 GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext,
1459 op_mask_type(fMaskFormat),
1460 !integerTranslate,
1461 this->glyphCount(),
1462 subRunDeviceBounds,
1463 geometry,
1464 sdc->colorInfo(),
1465 std::move(grPaint));
1466 return {clip, std::move(op)};
1467 }
1468 #endif // defined(SK_GANESH)
1469
1470 #if defined(SK_GRAPHITE)
draw(SkCanvas *,SkPoint drawOrigin,const SkPaint & paint,sk_sp<SkRefCnt> subRunStorage,Device * device) const1471 void DirectMaskSubRun::draw(SkCanvas*,
1472 SkPoint drawOrigin,
1473 const SkPaint& paint,
1474 sk_sp<SkRefCnt> subRunStorage,
1475 Device* device) const {
1476 this->AtlasSubRun::draw(device, drawOrigin, paint, std::move(subRunStorage));
1477 }
1478 #endif
1479
testingOnly_packedGlyphIDToGlyph(StrikeCache * cache) const1480 void DirectMaskSubRun::testingOnly_packedGlyphIDToGlyph(StrikeCache *cache) const {
1481 fGlyphs.packedGlyphIDToGlyph(cache);
1482 }
1483
1484 #if defined(SK_GANESH)
regenerateAtlas(int begin,int end,GrMeshDrawTarget * target) const1485 std::tuple<bool, int> DirectMaskSubRun::regenerateAtlas(int begin, int end,
1486 GrMeshDrawTarget* target) const {
1487 return fGlyphs.regenerateAtlas(begin, end, fMaskFormat, 0, target);
1488 }
1489
1490 template<typename Quad, typename VertexData>
transformed_direct_2D(SkZip<Quad,const Glyph *,const VertexData> quadData,GrColor color,const SkMatrix & matrix)1491 void transformed_direct_2D(SkZip<Quad, const Glyph*, const VertexData> quadData,
1492 GrColor color,
1493 const SkMatrix& matrix) {
1494 for (auto[quad, glyph, leftTop] : quadData) {
1495 auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
1496 SkScalar dl = leftTop.x(),
1497 dt = leftTop.y(),
1498 dr = dl + (ar - al),
1499 db = dt + (ab - at);
1500 SkPoint lt = matrix.mapXY(dl, dt),
1501 lb = matrix.mapXY(dl, db),
1502 rt = matrix.mapXY(dr, dt),
1503 rb = matrix.mapXY(dr, db);
1504 quad[0] = {lt, color, {al, at}}; // L,T
1505 quad[1] = {lb, color, {al, ab}}; // L,B
1506 quad[2] = {rt, color, {ar, at}}; // R,T
1507 quad[3] = {rb, color, {ar, ab}}; // R,B
1508 }
1509 }
1510
1511 template<typename Quad, typename VertexData>
transformed_direct_3D(SkZip<Quad,const Glyph *,const VertexData> quadData,GrColor color,const SkMatrix & matrix)1512 void transformed_direct_3D(SkZip<Quad, const Glyph*, const VertexData> quadData,
1513 GrColor color,
1514 const SkMatrix& matrix) {
1515 auto mapXYZ = [&](SkScalar x, SkScalar y) {
1516 SkPoint pt{x, y};
1517 SkPoint3 result;
1518 matrix.mapHomogeneousPoints(&result, &pt, 1);
1519 return result;
1520 };
1521 for (auto[quad, glyph, leftTop] : quadData) {
1522 auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
1523 SkScalar dl = leftTop.x(),
1524 dt = leftTop.y(),
1525 dr = dl + (ar - al),
1526 db = dt + (ab - at);
1527 SkPoint3 lt = mapXYZ(dl, dt),
1528 lb = mapXYZ(dl, db),
1529 rt = mapXYZ(dr, dt),
1530 rb = mapXYZ(dr, db);
1531 quad[0] = {lt, color, {al, at}}; // L,T
1532 quad[1] = {lb, color, {al, ab}}; // L,B
1533 quad[2] = {rt, color, {ar, at}}; // R,T
1534 quad[3] = {rb, color, {ar, ab}}; // R,B
1535 }
1536 }
1537
fillVertexData(void * vertexDst,int offset,int count,GrColor color,const SkMatrix & drawMatrix,SkPoint drawOrigin,SkIRect clip) const1538 void DirectMaskSubRun::fillVertexData(void* vertexDst, int offset, int count,
1539 GrColor color,
1540 const SkMatrix& drawMatrix, SkPoint drawOrigin,
1541 SkIRect clip) const {
1542 auto quadData = [&](auto dst) {
1543 return SkMakeZip(dst,
1544 fGlyphs.glyphs().subspan(offset, count),
1545 fLeftTopDevicePos.subspan(offset, count));
1546 };
1547
1548 const SkMatrix positionMatrix = position_matrix(drawMatrix, drawOrigin);
1549 auto [noTransformNeeded, originOffset] =
1550 can_use_direct(fInitialPositionMatrix, positionMatrix);
1551
1552 if (noTransformNeeded) {
1553 if (clip.isEmpty()) {
1554 if (fMaskFormat != MaskFormat::kARGB) {
1555 using Quad = Mask2DVertex[4];
1556 SkASSERT(sizeof(Mask2DVertex) == this->vertexStride(SkMatrix::I()));
1557 direct_2D(quadData((Quad*)vertexDst), color, originOffset);
1558 } else {
1559 using Quad = ARGB2DVertex[4];
1560 SkASSERT(sizeof(ARGB2DVertex) == this->vertexStride(SkMatrix::I()));
1561 generalized_direct_2D(quadData((Quad*)vertexDst), color, originOffset);
1562 }
1563 } else {
1564 if (fMaskFormat != MaskFormat::kARGB) {
1565 using Quad = Mask2DVertex[4];
1566 SkASSERT(sizeof(Mask2DVertex) == this->vertexStride(SkMatrix::I()));
1567 generalized_direct_2D(quadData((Quad*)vertexDst), color, originOffset, &clip);
1568 } else {
1569 using Quad = ARGB2DVertex[4];
1570 SkASSERT(sizeof(ARGB2DVertex) == this->vertexStride(SkMatrix::I()));
1571 generalized_direct_2D(quadData((Quad*)vertexDst), color, originOffset, &clip);
1572 }
1573 }
1574 } else if (SkMatrix inverse; fInitialPositionMatrix.invert(&inverse)) {
1575 SkMatrix viewDifference = SkMatrix::Concat(positionMatrix, inverse);
1576 if (!viewDifference.hasPerspective()) {
1577 if (fMaskFormat != MaskFormat::kARGB) {
1578 using Quad = Mask2DVertex[4];
1579 SkASSERT(sizeof(Mask2DVertex) == this->vertexStride(positionMatrix));
1580 transformed_direct_2D(quadData((Quad*)vertexDst), color, viewDifference);
1581 } else {
1582 using Quad = ARGB2DVertex[4];
1583 SkASSERT(sizeof(ARGB2DVertex) == this->vertexStride(positionMatrix));
1584 transformed_direct_2D(quadData((Quad*)vertexDst), color, viewDifference);
1585 }
1586 } else {
1587 if (fMaskFormat != MaskFormat::kARGB) {
1588 using Quad = Mask3DVertex[4];
1589 SkASSERT(sizeof(Mask3DVertex) == this->vertexStride(positionMatrix));
1590 transformed_direct_3D(quadData((Quad*)vertexDst), color, viewDifference);
1591 } else {
1592 using Quad = ARGB3DVertex[4];
1593 SkASSERT(sizeof(ARGB3DVertex) == this->vertexStride(positionMatrix));
1594 transformed_direct_3D(quadData((Quad*)vertexDst), color, viewDifference);
1595 }
1596 }
1597 }
1598 }
1599 #endif // defined(SK_GANESH)
1600
1601 #if defined(SK_GRAPHITE)
regenerateAtlas(int begin,int end,Recorder * recorder) const1602 std::tuple<bool, int> DirectMaskSubRun::regenerateAtlas(int begin, int end,
1603 Recorder* recorder) const {
1604 return fGlyphs.regenerateAtlas(begin, end, fMaskFormat, 0, recorder);
1605 }
1606
boundsAndDeviceMatrix(const Transform & localToDevice,SkPoint drawOrigin) const1607 std::tuple<gr::Rect, Transform> DirectMaskSubRun::boundsAndDeviceMatrix(
1608 const Transform& localToDevice, SkPoint drawOrigin) const {
1609 // The baked-in matrix differs from the current localToDevice by a translation if the upper 2x2
1610 // remains the same, and there's no perspective. Since there's no projection, Z is irrelevant
1611 // so it's okay that fInitialPositionMatrix is an SkMatrix and has discarded the 3rd row/col,
1612 // and can ignore those values in localToDevice.
1613 const SkM44& positionMatrix = localToDevice.matrix();
1614 const bool compatibleMatrix = positionMatrix.rc(0,0) == fInitialPositionMatrix.rc(0,0) &&
1615 positionMatrix.rc(0,1) == fInitialPositionMatrix.rc(0,1) &&
1616 positionMatrix.rc(1,0) == fInitialPositionMatrix.rc(1,0) &&
1617 positionMatrix.rc(1,1) == fInitialPositionMatrix.rc(1,1) &&
1618 localToDevice.type() != Transform::Type::kProjection &&
1619 !fInitialPositionMatrix.hasPerspective();
1620
1621 if (compatibleMatrix) {
1622 const SkV4 mappedOrigin = positionMatrix.map(drawOrigin.x(), drawOrigin.y(), 0.f, 1.f);
1623 const SkV2 offset = {mappedOrigin.x - fInitialPositionMatrix.getTranslateX(),
1624 mappedOrigin.y - fInitialPositionMatrix.getTranslateY()};
1625 if (SkScalarIsInt(offset.x) && SkScalarIsInt(offset.y)) {
1626 // The offset is an integer (but make sure), which means the generated mask can be
1627 // accessed without changing how texels would be sampled.
1628 return {gr::Rect(fGlyphDeviceBounds),
1629 Transform(SkM44::Translate(SkScalarRoundToInt(offset.x),
1630 SkScalarRoundToInt(offset.y)))};
1631 }
1632 }
1633
1634 // Otherwise compute the relative transformation from fInitialPositionMatrix to localToDevice,
1635 // with the drawOrigin applied. If fInitialPositionMatrix or the concatenation is not invertible
1636 // the returned Transform is marked invalid and the draw will be automatically dropped.
1637 return {gr::Rect(fGlyphDeviceBounds),
1638 localToDevice.preTranslate(drawOrigin.x(), drawOrigin.y())
1639 .concatInverse(SkM44(fInitialPositionMatrix))};
1640 }
1641
fillInstanceData(DrawWriter * dw,int offset,int count,int ssboIndex,SkScalar depth) const1642 void DirectMaskSubRun::fillInstanceData(DrawWriter* dw,
1643 int offset, int count,
1644 int ssboIndex,
1645 SkScalar depth) const {
1646 auto quadData = [&]() {
1647 return SkMakeZip(fGlyphs.glyphs().subspan(offset, count),
1648 fLeftTopDevicePos.subspan(offset, count));
1649 };
1650
1651 DrawWriter::Instances instances{*dw, {}, {}, 4};
1652 instances.reserve(count);
1653 unsigned short flags = (unsigned short)fMaskFormat;
1654 for (auto [glyph, leftTop]: quadData()) {
1655 auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
1656 instances.append(1) << AtlasPt{uint16_t(ar-al), uint16_t(ab-at)}
1657 << AtlasPt{uint16_t(al & 0x1fff), at}
1658 << leftTop << /*index=*/uint16_t(al >> 13) << flags
1659 << 1.0f
1660 << depth << ssboIndex;
1661 }
1662 }
1663
1664 #endif
1665
1666 // true if only need to translate by integer amount, device rect.
deviceRectAndCheckTransform(const SkMatrix & positionMatrix) const1667 std::tuple<bool, SkRect> DirectMaskSubRun::deviceRectAndCheckTransform(
1668 const SkMatrix& positionMatrix) const {
1669 const SkMatrix& initialMatrix = fInitialPositionMatrix;
1670 const SkPoint offset = positionMatrix.mapOrigin() - initialMatrix.mapOrigin();
1671
1672 const bool compatibleMatrix = positionMatrix[0] == initialMatrix[0] &&
1673 positionMatrix[1] == initialMatrix[1] &&
1674 positionMatrix[3] == initialMatrix[3] &&
1675 positionMatrix[4] == initialMatrix[4] &&
1676 !positionMatrix.hasPerspective() &&
1677 !initialMatrix.hasPerspective();
1678
1679 if (compatibleMatrix && SkScalarIsInt(offset.x()) && SkScalarIsInt(offset.y())) {
1680 return {true, fGlyphDeviceBounds.makeOffset(offset)};
1681 } else if (SkMatrix inverse; fInitialPositionMatrix.invert(&inverse)) {
1682 SkMatrix viewDifference = SkMatrix::Concat(positionMatrix, inverse);
1683 return {false, viewDifference.mapRect(fGlyphDeviceBounds)};
1684 }
1685
1686 // initialPositionMatrix is singular. Do nothing.
1687 return {false, SkRect::MakeEmpty()};
1688 }
1689
1690 // -- TransformedMaskSubRun ------------------------------------------------------------------------
1691 class TransformedMaskSubRun final : public SubRun, public AtlasSubRun {
1692 public:
TransformedMaskSubRun(const SkMatrix & initialPositionMatrix,TransformedMaskVertexFiller && vertexFiller,GlyphVector && glyphs)1693 TransformedMaskSubRun(const SkMatrix& initialPositionMatrix,
1694 TransformedMaskVertexFiller&& vertexFiller,
1695 GlyphVector&& glyphs)
1696 : fInitialPositionMatrix{initialPositionMatrix}
1697 , fVertexFiller{std::move(vertexFiller)}
1698 , fGlyphs{std::move(glyphs)} {}
1699
Make(const SkZip<SkPackedGlyphID,SkPoint> & accepted,const SkMatrix & initialPositionMatrix,SkStrikePromise && strikePromise,SkMatrix creationMatrix,SkRect creationBounds,MaskFormat maskType,SubRunAllocator * alloc)1700 static SubRunOwner Make(const SkZip<SkPackedGlyphID, SkPoint>& accepted,
1701 const SkMatrix& initialPositionMatrix,
1702 SkStrikePromise&& strikePromise,
1703 SkMatrix creationMatrix,
1704 SkRect creationBounds,
1705 MaskFormat maskType,
1706 SubRunAllocator* alloc) {
1707 auto vertexFiller = TransformedMaskVertexFiller::Make(
1708 maskType, creationMatrix, creationBounds, accepted, alloc);
1709
1710 auto glyphVector = GlyphVector::Make(std::move(strikePromise), accepted.get<0>(), alloc);
1711
1712 return alloc->makeUnique<TransformedMaskSubRun>(
1713 initialPositionMatrix, std::move(vertexFiller), std::move(glyphVector));
1714 }
1715
MakeFromBuffer(const SkMatrix & initialPositionMatrix,SkReadBuffer & buffer,SubRunAllocator * alloc,const SkStrikeClient * client)1716 static SubRunOwner MakeFromBuffer(const SkMatrix& initialPositionMatrix,
1717 SkReadBuffer& buffer,
1718 SubRunAllocator* alloc,
1719 const SkStrikeClient* client) {
1720 auto vertexFiller = TransformedMaskVertexFiller::MakeFromBuffer(buffer, alloc);
1721 if (!buffer.validate(vertexFiller.has_value())) { return nullptr; }
1722
1723 auto glyphVector = GlyphVector::MakeFromBuffer(buffer, client, alloc);
1724 if (!buffer.validate(glyphVector.has_value())) { return nullptr; }
1725 if (!buffer.validate(SkCount(glyphVector->glyphs()) == vertexFiller->count())) {
1726 return nullptr;
1727 }
1728 return alloc->makeUnique<TransformedMaskSubRun>(
1729 initialPositionMatrix, std::move(*vertexFiller), std::move(*glyphVector));
1730 }
1731
unflattenSize() const1732 int unflattenSize() const override {
1733 return sizeof(TransformedMaskSubRun) +
1734 fGlyphs.unflattenSize() +
1735 fVertexFiller.unflattenSize();
1736 }
1737
canReuse(const SkPaint & paint,const SkMatrix & positionMatrix) const1738 bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const override {
1739 // If we are not scaling the cache entry to be larger, than a cache with smaller glyphs may
1740 // be better.
1741 if (fInitialPositionMatrix.getMaxScale() < 1) {
1742 return false;
1743 }
1744 return true;
1745 }
1746
testingOnly_atlasSubRun() const1747 const AtlasSubRun* testingOnly_atlasSubRun() const override { return this; }
1748
testingOnly_packedGlyphIDToGlyph(StrikeCache * cache) const1749 void testingOnly_packedGlyphIDToGlyph(StrikeCache *cache) const override {
1750 fGlyphs.packedGlyphIDToGlyph(cache);
1751 }
1752
glyphCount() const1753 int glyphCount() const override { return SkCount(fGlyphs.glyphs()); }
1754
maskFormat() const1755 MaskFormat maskFormat() const override { return fVertexFiller.grMaskType(); }
1756
1757 #if defined(SK_GANESH)
1758
draw(SkCanvas *,const GrClip * clip,const SkMatrixProvider & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,sk_sp<SkRefCnt> subRunStorage,skgpu::v1::SurfaceDrawContext * sdc) const1759 void draw(SkCanvas*,
1760 const GrClip* clip,
1761 const SkMatrixProvider& viewMatrix,
1762 SkPoint drawOrigin,
1763 const SkPaint& paint,
1764 sk_sp<SkRefCnt> subRunStorage,
1765 skgpu::v1::SurfaceDrawContext* sdc) const override {
1766 auto[drawingClip, op] = this->makeAtlasTextOp(
1767 clip, viewMatrix, drawOrigin, paint, std::move(subRunStorage), sdc);
1768 if (op != nullptr) {
1769 sdc->addDrawOp(drawingClip, std::move(op));
1770 }
1771 }
1772
1773 std::tuple<const GrClip*, GrOp::Owner>
makeAtlasTextOp(const GrClip * clip,const SkMatrixProvider & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,sk_sp<SkRefCnt> && subRunStorage,skgpu::v1::SurfaceDrawContext * sdc) const1774 makeAtlasTextOp(const GrClip* clip,
1775 const SkMatrixProvider& viewMatrix,
1776 SkPoint drawOrigin,
1777 const SkPaint& paint,
1778 sk_sp<SkRefCnt>&& subRunStorage,
1779 skgpu::v1::SurfaceDrawContext* sdc) const override {
1780 SkASSERT(this->glyphCount() != 0);
1781
1782 const SkMatrix& drawMatrix = viewMatrix.localToDevice();
1783
1784 GrPaint grPaint;
1785 SkPMColor4f drawingColor = calculate_colors(sdc,
1786 paint,
1787 drawMatrix,
1788 fVertexFiller.grMaskType(),
1789 &grPaint);
1790
1791 auto geometry = AtlasTextOp::Geometry::Make(*this,
1792 drawMatrix,
1793 drawOrigin,
1794 SkIRect::MakeEmpty(),
1795 std::move(subRunStorage),
1796 drawingColor,
1797 sdc->arenaAlloc());
1798
1799 GrRecordingContext* const rContext = sdc->recordingContext();
1800 SkMatrix positionMatrix = position_matrix(drawMatrix, drawOrigin);
1801 GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext,
1802 fVertexFiller.opMaskType(),
1803 true,
1804 this->glyphCount(),
1805 this->deviceRect(positionMatrix),
1806 geometry,
1807 sdc->colorInfo(),
1808 std::move(grPaint));
1809 return {clip, std::move(op)};
1810 }
1811
regenerateAtlas(int begin,int end,GrMeshDrawTarget * target) const1812 std::tuple<bool, int> regenerateAtlas(int begin, int end,
1813 GrMeshDrawTarget* target) const override {
1814 return fGlyphs.regenerateAtlas(begin, end, fVertexFiller.grMaskType(), 1, target);
1815 }
1816
fillVertexData(void * vertexDst,int offset,int count,GrColor color,const SkMatrix & drawMatrix,SkPoint drawOrigin,SkIRect clip) const1817 void fillVertexData(
1818 void* vertexDst, int offset, int count,
1819 GrColor color,
1820 const SkMatrix& drawMatrix, SkPoint drawOrigin,
1821 SkIRect clip) const override {
1822 const SkMatrix positionMatrix = position_matrix(drawMatrix, drawOrigin);
1823 fVertexFiller.fillVertexData(offset, count,
1824 fGlyphs.glyphs(),
1825 color,
1826 positionMatrix,
1827 clip,
1828 vertexDst);
1829 }
1830
vertexStride(const SkMatrix & drawMatrix) const1831 size_t vertexStride(const SkMatrix& drawMatrix) const override {
1832 return fVertexFiller.vertexStride(drawMatrix);
1833 }
1834
1835 #endif // defined(SK_GANESH)
1836
1837 #if defined(SK_GRAPHITE)
1838
draw(SkCanvas *,SkPoint drawOrigin,const SkPaint & paint,sk_sp<SkRefCnt> subRunStorage,Device * device) const1839 void draw(SkCanvas*,
1840 SkPoint drawOrigin,
1841 const SkPaint& paint,
1842 sk_sp<SkRefCnt> subRunStorage,
1843 Device* device) const override {
1844 this->AtlasSubRun::draw(device, drawOrigin, paint, std::move(subRunStorage));
1845 }
1846
regenerateAtlas(int begin,int end,Recorder * recorder) const1847 std::tuple<bool, int> regenerateAtlas(int begin, int end, Recorder* recorder) const override {
1848 return fGlyphs.regenerateAtlas(begin, end, fVertexFiller.grMaskType(), 1, recorder);
1849 }
1850
boundsAndDeviceMatrix(const Transform & localToDevice,SkPoint drawOrigin) const1851 std::tuple<gr::Rect, Transform> boundsAndDeviceMatrix(const Transform& localToDevice,
1852 SkPoint drawOrigin) const override {
1853 const SkMatrix viewDifference = fVertexFiller.viewDifference(
1854 localToDevice.preTranslate(drawOrigin.x(), drawOrigin.y()));
1855 return {gr::Rect(fVertexFiller.creationBounds()), Transform(SkM44(viewDifference))};
1856 }
1857
renderer(const RendererProvider * renderers) const1858 const Renderer* renderer(const RendererProvider* renderers) const override {
1859 return renderers->bitmapText();
1860 }
1861
fillInstanceData(DrawWriter * dw,int offset,int count,int ssboIndex,SkScalar depth) const1862 void fillInstanceData(DrawWriter* dw,
1863 int offset, int count,
1864 int ssboIndex,
1865 SkScalar depth) const override {
1866 unsigned short flags = (unsigned short)fVertexFiller.grMaskType();
1867 fVertexFiller.fillInstanceData(dw,
1868 offset, count,
1869 flags,
1870 ssboIndex,
1871 fGlyphs.glyphs(),
1872 depth);
1873 }
1874
1875 #endif // SK_GRAPHITE
1876
1877 protected:
subRunType() const1878 SubRunType subRunType() const override { return kTransformMask; }
1879
doFlatten(SkWriteBuffer & buffer) const1880 void doFlatten(SkWriteBuffer& buffer) const override {
1881 fVertexFiller.flatten(buffer);
1882 fGlyphs.flatten(buffer);
1883 }
1884
1885 private:
1886 // The rectangle that surrounds all the glyph bounding boxes in device space.
deviceRect(const SkMatrix & positionMatrix) const1887 SkRect deviceRect(const SkMatrix& positionMatrix) const {
1888 return fVertexFiller.deviceRect(positionMatrix);
1889 }
1890
1891 const SkMatrix& fInitialPositionMatrix;
1892
1893 const TransformedMaskVertexFiller fVertexFiller;
1894
1895 // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
1896 // be single threaded.
1897 mutable GlyphVector fGlyphs;
1898 }; // class TransformedMaskSubRun
1899
1900 // -- SDFTSubRun -----------------------------------------------------------------------------------
1901
has_some_antialiasing(const SkFont & font)1902 bool has_some_antialiasing(const SkFont& font ) {
1903 SkFont::Edging edging = font.getEdging();
1904 return edging == SkFont::Edging::kAntiAlias
1905 || edging == SkFont::Edging::kSubpixelAntiAlias;
1906 }
1907
1908 #if !defined(SK_DISABLE_SDF_TEXT)
1909
1910 #if defined(SK_GANESH)
1911
calculate_sdf_parameters(const skgpu::v1::SurfaceDrawContext & sdc,const SkMatrix & drawMatrix,bool useLCDText,bool isAntiAliased)1912 static std::tuple<AtlasTextOp::MaskType, uint32_t, bool> calculate_sdf_parameters(
1913 const skgpu::v1::SurfaceDrawContext& sdc,
1914 const SkMatrix& drawMatrix,
1915 bool useLCDText,
1916 bool isAntiAliased) {
1917 const GrColorInfo& colorInfo = sdc.colorInfo();
1918 const SkSurfaceProps& props = sdc.surfaceProps();
1919 bool isBGR = SkPixelGeometryIsBGR(props.pixelGeometry());
1920 bool isLCD = useLCDText && SkPixelGeometryIsH(props.pixelGeometry());
1921 using MT = AtlasTextOp::MaskType;
1922 MT maskType = !isAntiAliased ? MT::kAliasedDistanceField
1923 : isLCD ? (isBGR ? MT::kLCDBGRDistanceField
1924 : MT::kLCDDistanceField)
1925 : MT::kGrayscaleDistanceField;
1926
1927 bool useGammaCorrectDistanceTable = colorInfo.isLinearlyBlended();
1928 uint32_t DFGPFlags = drawMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
1929 DFGPFlags |= drawMatrix.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0;
1930 DFGPFlags |= useGammaCorrectDistanceTable ? kGammaCorrect_DistanceFieldEffectFlag : 0;
1931 DFGPFlags |= MT::kAliasedDistanceField == maskType ? kAliased_DistanceFieldEffectFlag : 0;
1932 DFGPFlags |= drawMatrix.hasPerspective() ? kPerspective_DistanceFieldEffectFlag : 0;
1933
1934 if (isLCD) {
1935 DFGPFlags |= kUseLCD_DistanceFieldEffectFlag;
1936 DFGPFlags |= MT::kLCDBGRDistanceField == maskType ? kBGR_DistanceFieldEffectFlag : 0;
1937 }
1938 return {maskType, DFGPFlags, useGammaCorrectDistanceTable};
1939 }
1940
1941 #endif // defined(SK_GANESH)
1942
1943 class SDFTSubRun final : public SubRun, public AtlasSubRun {
1944 public:
SDFTSubRun(bool useLCDText,bool antiAliased,const SDFTMatrixRange & matrixRange,TransformedMaskVertexFiller && vertexFiller,GlyphVector && glyphs)1945 SDFTSubRun(bool useLCDText,
1946 bool antiAliased,
1947 const SDFTMatrixRange& matrixRange,
1948 TransformedMaskVertexFiller&& vertexFiller,
1949 GlyphVector&& glyphs)
1950 : fUseLCDText{useLCDText}
1951 , fAntiAliased{antiAliased}
1952 , fMatrixRange{matrixRange}
1953 , fVertexFiller{std::move(vertexFiller)}
1954 , fGlyphs{std::move(glyphs)} { }
1955
Make(const SkZip<SkPackedGlyphID,SkPoint> & accepted,const SkFont & runFont,SkStrikePromise && strikePromise,const SkMatrix & creationMatrix,SkRect creationBounds,const SDFTMatrixRange & matrixRange,SubRunAllocator * alloc)1956 static SubRunOwner Make(const SkZip<SkPackedGlyphID, SkPoint>& accepted,
1957 const SkFont& runFont,
1958 SkStrikePromise&& strikePromise,
1959 const SkMatrix& creationMatrix,
1960 SkRect creationBounds,
1961 const SDFTMatrixRange& matrixRange,
1962 SubRunAllocator* alloc) {
1963 auto vertexFiller = TransformedMaskVertexFiller::Make(
1964 MaskFormat::kA8,
1965 creationMatrix,
1966 creationBounds,
1967 accepted,
1968 alloc);
1969
1970 auto glyphVector = GlyphVector::Make(std::move(strikePromise), accepted.get<0>(), alloc);
1971
1972 return alloc->makeUnique<SDFTSubRun>(
1973 runFont.getEdging() == SkFont::Edging::kSubpixelAntiAlias,
1974 has_some_antialiasing(runFont),
1975 matrixRange,
1976 std::move(vertexFiller),
1977 std::move(glyphVector));
1978 }
1979
MakeFromBuffer(const SkMatrix &,SkReadBuffer & buffer,SubRunAllocator * alloc,const SkStrikeClient * client)1980 static SubRunOwner MakeFromBuffer(const SkMatrix&,
1981 SkReadBuffer& buffer,
1982 SubRunAllocator* alloc,
1983 const SkStrikeClient* client) {
1984 int useLCD = buffer.readInt();
1985 int isAntiAliased = buffer.readInt();
1986 SDFTMatrixRange matrixRange = SDFTMatrixRange::MakeFromBuffer(buffer);
1987 auto vertexFiller = TransformedMaskVertexFiller::MakeFromBuffer(buffer, alloc);
1988 if (!buffer.validate(vertexFiller.has_value())) { return nullptr; }
1989 auto glyphVector = GlyphVector::MakeFromBuffer(buffer, client, alloc);
1990 if (!buffer.validate(glyphVector.has_value())) { return nullptr; }
1991 if (!buffer.validate(SkCount(glyphVector->glyphs()) == vertexFiller->count())) {
1992 return nullptr;
1993 }
1994 return alloc->makeUnique<SDFTSubRun>(useLCD,
1995 isAntiAliased,
1996 matrixRange,
1997 std::move(*vertexFiller),
1998 std::move(*glyphVector));
1999 }
2000
unflattenSize() const2001 int unflattenSize() const override {
2002 return sizeof(SDFTSubRun) + fGlyphs.unflattenSize() + fVertexFiller.unflattenSize();
2003 }
2004
canReuse(const SkPaint & paint,const SkMatrix & positionMatrix) const2005 bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const override {
2006 return fMatrixRange.matrixInRange(positionMatrix);
2007 }
2008
testingOnly_atlasSubRun() const2009 const AtlasSubRun* testingOnly_atlasSubRun() const override { return this; }
2010
testingOnly_packedGlyphIDToGlyph(StrikeCache * cache) const2011 void testingOnly_packedGlyphIDToGlyph(StrikeCache *cache) const override {
2012 fGlyphs.packedGlyphIDToGlyph(cache);
2013 }
2014
glyphCount() const2015 int glyphCount() const override { return fVertexFiller.count(); }
maskFormat() const2016 MaskFormat maskFormat() const override { return fVertexFiller.grMaskType(); }
2017
2018 #if defined(SK_GANESH)
draw(SkCanvas *,const GrClip * clip,const SkMatrixProvider & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,sk_sp<SkRefCnt> subRunStorage,skgpu::v1::SurfaceDrawContext * sdc) const2019 void draw(SkCanvas*,
2020 const GrClip* clip,
2021 const SkMatrixProvider& viewMatrix,
2022 SkPoint drawOrigin,
2023 const SkPaint& paint,
2024 sk_sp<SkRefCnt> subRunStorage,
2025 skgpu::v1::SurfaceDrawContext* sdc) const override {
2026 auto[drawingClip, op] = this->makeAtlasTextOp(
2027 clip, viewMatrix, drawOrigin, paint, std::move(subRunStorage), sdc);
2028 if (op != nullptr) {
2029 sdc->addDrawOp(drawingClip, std::move(op));
2030 }
2031 }
2032
makeAtlasTextOp(const GrClip * clip,const SkMatrixProvider & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,sk_sp<SkRefCnt> && subRunStorage,skgpu::v1::SurfaceDrawContext * sdc) const2033 std::tuple<const GrClip*, GrOp::Owner> makeAtlasTextOp(
2034 const GrClip* clip,
2035 const SkMatrixProvider& viewMatrix,
2036 SkPoint drawOrigin,
2037 const SkPaint& paint,
2038 sk_sp<SkRefCnt>&& subRunStorage,
2039 skgpu::v1::SurfaceDrawContext* sdc) const override {
2040 SkASSERT(this->glyphCount() != 0);
2041
2042 const SkMatrix& drawMatrix = viewMatrix.localToDevice();
2043
2044 GrPaint grPaint;
2045 SkPMColor4f drawingColor = calculate_colors(sdc,
2046 paint,
2047 drawMatrix,
2048 MaskFormat::kA8,
2049 &grPaint);
2050
2051 auto [maskType, DFGPFlags, useGammaCorrectDistanceTable] =
2052 calculate_sdf_parameters(*sdc, drawMatrix, fUseLCDText, fAntiAliased);
2053
2054 auto geometry = AtlasTextOp::Geometry::Make(*this,
2055 drawMatrix,
2056 drawOrigin,
2057 SkIRect::MakeEmpty(),
2058 std::move(subRunStorage),
2059 drawingColor,
2060 sdc->arenaAlloc());
2061
2062 GrRecordingContext* const rContext = sdc->recordingContext();
2063 SkMatrix positionMatrix = position_matrix(drawMatrix, drawOrigin);
2064 GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext,
2065 maskType,
2066 true,
2067 this->glyphCount(),
2068 this->deviceRect(positionMatrix),
2069 SkPaintPriv::ComputeLuminanceColor(paint),
2070 useGammaCorrectDistanceTable,
2071 DFGPFlags,
2072 geometry,
2073 std::move(grPaint));
2074
2075 return {clip, std::move(op)};
2076 }
2077
regenerateAtlas(int begin,int end,GrMeshDrawTarget * target) const2078 std::tuple<bool, int> regenerateAtlas(
2079 int begin, int end, GrMeshDrawTarget* target) const override {
2080 return fGlyphs.regenerateAtlas(begin, end, MaskFormat::kA8, SK_DistanceFieldInset, target);
2081 }
2082
fillVertexData(void * vertexDst,int offset,int count,GrColor color,const SkMatrix & drawMatrix,SkPoint drawOrigin,SkIRect clip) const2083 void fillVertexData(
2084 void *vertexDst, int offset, int count,
2085 GrColor color,
2086 const SkMatrix& drawMatrix, SkPoint drawOrigin,
2087 SkIRect clip) const override {
2088 const SkMatrix positionMatrix = position_matrix(drawMatrix, drawOrigin);
2089
2090 fVertexFiller.fillVertexData(offset, count,
2091 fGlyphs.glyphs(),
2092 color,
2093 positionMatrix,
2094 clip,
2095 vertexDst);
2096 }
2097
vertexStride(const SkMatrix & drawMatrix) const2098 size_t vertexStride(const SkMatrix& drawMatrix) const override {
2099 if (drawMatrix.hasPerspective()) {
2100 return sizeof(Mask3DVertex);
2101 } else {
2102 return sizeof(Mask2DVertex);
2103 }
2104 }
2105
2106 #endif // defined(SK_GANESH)
2107
2108 #if defined(SK_GRAPHITE)
2109
draw(SkCanvas *,SkPoint drawOrigin,const SkPaint & paint,sk_sp<SkRefCnt> subRunStorage,Device * device) const2110 void draw(SkCanvas*,
2111 SkPoint drawOrigin,
2112 const SkPaint& paint,
2113 sk_sp<SkRefCnt> subRunStorage,
2114 Device* device) const override {
2115 this->AtlasSubRun::draw(device, drawOrigin, paint, std::move(subRunStorage));
2116 }
2117
regenerateAtlas(int begin,int end,Recorder * recorder) const2118 std::tuple<bool, int> regenerateAtlas(int begin, int end, Recorder *recorder) const override {
2119 return fGlyphs.regenerateAtlas(
2120 begin, end, MaskFormat::kA8, SK_DistanceFieldInset, recorder);
2121 }
2122
boundsAndDeviceMatrix(const Transform & localToDevice,SkPoint drawOrigin) const2123 std::tuple<gr::Rect, Transform> boundsAndDeviceMatrix(const Transform& localToDevice,
2124 SkPoint drawOrigin) const override {
2125 const SkMatrix viewDifference = fVertexFiller.viewDifference(
2126 localToDevice.preTranslate(drawOrigin.x(), drawOrigin.y()));
2127 return {gr::Rect(fVertexFiller.creationBounds()), Transform(SkM44(viewDifference))};
2128 }
2129
renderer(const RendererProvider * renderers) const2130 const Renderer* renderer(const RendererProvider* renderers) const override {
2131 return renderers->sdfText(fUseLCDText);
2132 }
2133
fillInstanceData(DrawWriter * dw,int offset,int count,int ssboIndex,SkScalar depth) const2134 void fillInstanceData(DrawWriter* dw,
2135 int offset, int count,
2136 int ssboIndex,
2137 SkScalar depth) const override {
2138 fVertexFiller.fillInstanceData(dw,
2139 offset, count, /*flags=*/0,
2140 ssboIndex,
2141 fGlyphs.glyphs(),
2142 depth);
2143 }
2144
2145 #endif // SK_GRAPHITE
2146
2147 protected:
subRunType() const2148 SubRunType subRunType() const override { return kSDFT; }
doFlatten(SkWriteBuffer & buffer) const2149 void doFlatten(SkWriteBuffer& buffer) const override {
2150 buffer.writeInt(fUseLCDText);
2151 buffer.writeInt(fAntiAliased);
2152 fMatrixRange.flatten(buffer);
2153 fVertexFiller.flatten(buffer);
2154 fGlyphs.flatten(buffer);
2155 }
2156
2157 private:
2158 // The rectangle that surrounds all the glyph bounding boxes in device space.
deviceRect(const SkMatrix & positionMatrix) const2159 SkRect deviceRect(const SkMatrix& positionMatrix) const {
2160 return fVertexFiller.deviceRect(positionMatrix);
2161 }
2162
2163 const bool fUseLCDText;
2164 const bool fAntiAliased;
2165 const SDFTMatrixRange fMatrixRange;
2166
2167 const TransformedMaskVertexFiller fVertexFiller;
2168
2169 // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
2170 // be single threaded.
2171 mutable GlyphVector fGlyphs;
2172 }; // class SDFTSubRun
2173
2174 #endif // !defined(SK_DISABLE_SDF_TEXT)
2175
2176 // -- SubRun ---------------------------------------------------------------------------------------
2177
2178 template<typename AddSingleMaskFormat>
add_multi_mask_format(AddSingleMaskFormat addSingleMaskFormat,const SkZip<SkPackedGlyphID,SkPoint,SkMask::Format> & accepted)2179 void add_multi_mask_format(
2180 AddSingleMaskFormat addSingleMaskFormat,
2181 const SkZip<SkPackedGlyphID, SkPoint, SkMask::Format>& accepted) {
2182 if (accepted.empty()) { return; }
2183
2184 auto maskSpan = accepted.get<2>();
2185 MaskFormat format = Glyph::FormatFromSkGlyph(maskSpan[0]);
2186 size_t startIndex = 0;
2187 for (size_t i = 1; i < accepted.size(); i++) {
2188 MaskFormat nextFormat = Glyph::FormatFromSkGlyph(maskSpan[i]);
2189 if (format != nextFormat) {
2190 auto interval = accepted.subspan(startIndex, i - startIndex);
2191 // Only pass the packed glyph ids and positions.
2192 auto glyphsWithSameFormat = SkMakeZip(interval.get<0>(), interval.get<1>());
2193 // Take a ref on the strike. This should rarely happen.
2194 addSingleMaskFormat(glyphsWithSameFormat, format);
2195 format = nextFormat;
2196 startIndex = i;
2197 }
2198 }
2199 auto interval = accepted.last(accepted.size() - startIndex);
2200 auto glyphsWithSameFormat = SkMakeZip(interval.get<0>(), interval.get<1>());
2201 addSingleMaskFormat(glyphsWithSameFormat, format);
2202 }
2203 } // namespace
2204
2205 namespace sktext::gpu {
2206 SubRun::~SubRun() = default;
flatten(SkWriteBuffer & buffer) const2207 void SubRun::flatten(SkWriteBuffer& buffer) const {
2208 buffer.writeInt(this->subRunType());
2209 this->doFlatten(buffer);
2210 }
2211
MakeFromBuffer(const SkMatrix & initialPositionMatrix,SkReadBuffer & buffer,SubRunAllocator * alloc,const SkStrikeClient * client)2212 SubRunOwner SubRun::MakeFromBuffer(const SkMatrix& initialPositionMatrix,
2213 SkReadBuffer& buffer,
2214 SubRunAllocator* alloc,
2215 const SkStrikeClient* client) {
2216 using Maker = SubRunOwner (*)(const SkMatrix&,
2217 SkReadBuffer&,
2218 SubRunAllocator*,
2219 const SkStrikeClient*);
2220
2221 static Maker makers[kSubRunTypeCount] = {
2222 nullptr, // 0 index is bad.
2223 DirectMaskSubRun::MakeFromBuffer,
2224 #if !defined(SK_DISABLE_SDF_TEXT)
2225 SDFTSubRun::MakeFromBuffer,
2226 #endif
2227 TransformedMaskSubRun::MakeFromBuffer,
2228 PathSubRun::MakeFromBuffer,
2229 DrawableSubRun::MakeFromBuffer,
2230 };
2231 int subRunTypeInt = buffer.readInt();
2232 SkASSERT(kBad < subRunTypeInt && subRunTypeInt < kSubRunTypeCount);
2233 if (!buffer.validate(kBad < subRunTypeInt && subRunTypeInt < kSubRunTypeCount)) {
2234 return nullptr;
2235 }
2236 auto maker = makers[subRunTypeInt];
2237 if (!buffer.validate(maker != nullptr)) { return nullptr; }
2238 return maker(initialPositionMatrix, buffer, alloc, client);
2239 }
2240
2241 // -- SubRunContainer ------------------------------------------------------------------------------
SubRunContainer(const SkMatrix & initialPositionMatrix)2242 SubRunContainer::SubRunContainer(const SkMatrix& initialPositionMatrix)
2243 : fInitialPositionMatrix{initialPositionMatrix} {}
2244
flattenAllocSizeHint(SkWriteBuffer & buffer) const2245 void SubRunContainer::flattenAllocSizeHint(SkWriteBuffer& buffer) const {
2246 int unflattenSizeHint = 0;
2247 for (auto& subrun : fSubRuns) {
2248 unflattenSizeHint += subrun.unflattenSize();
2249 }
2250 buffer.writeInt(unflattenSizeHint);
2251 }
2252
AllocSizeHintFromBuffer(SkReadBuffer & buffer)2253 int SubRunContainer::AllocSizeHintFromBuffer(SkReadBuffer& buffer) {
2254 int subRunsSizeHint = buffer.readInt();
2255
2256 // Since the hint doesn't affect correctness, if it looks fishy just pick a reasonable
2257 // value.
2258 if (subRunsSizeHint < 0 || (1 << 16) < subRunsSizeHint) {
2259 subRunsSizeHint = 128;
2260 }
2261 return subRunsSizeHint;
2262 }
2263
flattenRuns(SkWriteBuffer & buffer) const2264 void SubRunContainer::flattenRuns(SkWriteBuffer& buffer) const {
2265 buffer.writeMatrix(fInitialPositionMatrix);
2266 int subRunCount = 0;
2267 for ([[maybe_unused]] auto& subRun : fSubRuns) {
2268 subRunCount += 1;
2269 }
2270 buffer.writeInt(subRunCount);
2271 for (auto& subRun : fSubRuns) {
2272 subRun.flatten(buffer);
2273 }
2274 }
2275
MakeFromBufferInAlloc(SkReadBuffer & buffer,const SkStrikeClient * client,SubRunAllocator * alloc)2276 SubRunContainerOwner SubRunContainer::MakeFromBufferInAlloc(SkReadBuffer& buffer,
2277 const SkStrikeClient* client,
2278 SubRunAllocator* alloc) {
2279 SkMatrix positionMatrix;
2280 buffer.readMatrix(&positionMatrix);
2281 if (!buffer.isValid()) { return nullptr; }
2282 SubRunContainerOwner container = alloc->makeUnique<SubRunContainer>(positionMatrix);
2283
2284 int subRunCount = buffer.readInt();
2285 SkASSERT(subRunCount > 0);
2286 if (!buffer.validate(subRunCount > 0)) { return nullptr; }
2287 for (int i = 0; i < subRunCount; ++i) {
2288 auto subRunOwner = SubRun::MakeFromBuffer(
2289 container->initialPosition(), buffer, alloc, client);
2290 if (!buffer.validate(subRunOwner != nullptr)) { return nullptr; }
2291 if (subRunOwner != nullptr) {
2292 container->fSubRuns.append(std::move(subRunOwner));
2293 }
2294 }
2295 return container;
2296 }
2297
EstimateAllocSize(const GlyphRunList & glyphRunList)2298 size_t SubRunContainer::EstimateAllocSize(const GlyphRunList& glyphRunList) {
2299 // The difference in alignment from the per-glyph data to the SubRun;
2300 constexpr size_t alignDiff = alignof(DirectMaskSubRun) - alignof(SkPoint);
2301 constexpr size_t vertexDataToSubRunPadding = alignDiff > 0 ? alignDiff : 0;
2302 size_t totalGlyphCount = glyphRunList.totalGlyphCount();
2303 // This is optimized for DirectMaskSubRun which is by far the most common case.
2304 return totalGlyphCount * sizeof(SkPoint)
2305 + GlyphVector::GlyphVectorSize(totalGlyphCount)
2306 + glyphRunList.runCount() * (sizeof(DirectMaskSubRun) + vertexDataToSubRunPadding)
2307 + sizeof(SubRunContainer);
2308 }
2309
find_maximum_glyph_dimension(StrikeForGPU * strike,SkSpan<const SkGlyphID> glyphs)2310 SkScalar find_maximum_glyph_dimension(StrikeForGPU* strike, SkSpan<const SkGlyphID> glyphs) {
2311 StrikeMutationMonitor m{strike};
2312 SkScalar maxDimension = 0;
2313 for (SkGlyphID glyphID : glyphs) {
2314 SkGlyphDigest digest = strike->digestFor(kMask, SkPackedGlyphID{glyphID});
2315 maxDimension = std::max(static_cast<SkScalar>(digest.maxDimension()), maxDimension);
2316 }
2317
2318 return maxDimension;
2319 }
2320
2321 #if !defined(SK_DISABLE_SDF_TEXT)
prepare_for_SDFT_drawing(StrikeForGPU * strike,const SkMatrix & creationMatrix,SkDrawableGlyphBuffer * accepted,SkSourceGlyphBuffer * rejected)2322 SkRect prepare_for_SDFT_drawing(StrikeForGPU* strike,
2323 const SkMatrix& creationMatrix,
2324 SkDrawableGlyphBuffer* accepted,
2325 SkSourceGlyphBuffer* rejected) {
2326 SkGlyphRect boundingRect = skglyph::empty_rect();
2327 StrikeMutationMonitor m{strike};
2328 for (auto [i, packedID, pos] : SkMakeEnumerate(accepted->input())) {
2329 if (!SkScalarsAreFinite(pos.x(), pos.y())) {
2330 continue;
2331 }
2332
2333 SkGlyphDigest digest = strike->digestFor(kSDFT, packedID);
2334 switch (digest.actionFor(kSDFT)) {
2335 case GlyphAction::kAccept: {
2336 SkPoint mappedPos = creationMatrix.mapPoint(pos);
2337 const SkGlyphRect glyphBounds =
2338 digest.bounds()
2339 // The SDFT glyphs have 2-pixel wide padding that should
2340 // not be used in calculating the source rectangle.
2341 .inset(SK_DistanceFieldInset, SK_DistanceFieldInset)
2342 .offset(mappedPos);
2343 boundingRect = skglyph::rect_union(boundingRect, glyphBounds);
2344 accepted->accept(packedID, glyphBounds.leftTop(), digest.maskFormat());
2345 break;
2346 }
2347 case GlyphAction::kReject:
2348 rejected->reject(i);
2349 break;
2350 default:
2351 break;
2352 }
2353 }
2354
2355 return boundingRect.rect();
2356 }
2357 #endif
2358
prepare_for_direct_mask_drawing(StrikeForGPU * strike,const SkMatrix & positionMatrix,SkDrawableGlyphBuffer * accepted,SkSourceGlyphBuffer * rejected)2359 SkRect prepare_for_direct_mask_drawing(StrikeForGPU* strike,
2360 const SkMatrix& positionMatrix,
2361 SkDrawableGlyphBuffer* accepted,
2362 SkSourceGlyphBuffer* rejected) {
2363 const SkIPoint mask = strike->roundingSpec().ignorePositionFieldMask;
2364 const SkPoint halfSampleFreq = strike->roundingSpec().halfAxisSampleFreq;
2365
2366 // Build up the mapping from source space to device space. Add the rounding constant
2367 // halfSampleFreq, so we just need to floor to get the device result.
2368 SkMatrix positionMatrixWithRounding = positionMatrix;
2369 positionMatrixWithRounding.postTranslate(halfSampleFreq.x(), halfSampleFreq.y());
2370
2371 SkGlyphRect boundingRect = skglyph::empty_rect();
2372 StrikeMutationMonitor m{strike};
2373 for (auto [i, packedID, pos] : SkMakeEnumerate(accepted->input())) {
2374 if (!SkScalarsAreFinite(pos.x(), pos.y())) {
2375 continue;
2376 }
2377
2378 const SkPoint mappedPos = positionMatrixWithRounding.mapPoint(pos);
2379 const SkGlyphID glyphID = packedID.glyphID();
2380 const SkPackedGlyphID packedGlyphID = SkPackedGlyphID{glyphID, mappedPos, mask};
2381 auto digest = strike->digestFor(kDirectMask, packedGlyphID);
2382 switch (digest.actionFor(kDirectMask)) {
2383 case GlyphAction::kAccept: {
2384 const SkPoint roundedPos{SkScalarFloorToScalar(mappedPos.x()),
2385 SkScalarFloorToScalar(mappedPos.y())};
2386 const SkGlyphRect glyphBounds = digest.bounds().offset(roundedPos);
2387 boundingRect = skglyph::rect_union(boundingRect, glyphBounds);
2388 accepted->accept(packedGlyphID, glyphBounds.leftTop(), digest.maskFormat());
2389 break;
2390 }
2391 case GlyphAction::kReject:
2392 rejected->reject(i);
2393 break;
2394 default:
2395 break;
2396 }
2397 }
2398
2399 return boundingRect.rect();
2400 }
2401
prepare_for_mask_drawing(StrikeForGPU * strike,const SkMatrix & creationMatrix,SkDrawableGlyphBuffer * accepted,SkSourceGlyphBuffer * rejected)2402 SkRect prepare_for_mask_drawing(StrikeForGPU* strike,
2403 const SkMatrix& creationMatrix,
2404 SkDrawableGlyphBuffer* accepted,
2405 SkSourceGlyphBuffer* rejected) {
2406 SkGlyphRect boundingRect = skglyph::empty_rect();
2407 StrikeMutationMonitor m{strike};
2408 for (auto [i, packedID, pos] : SkMakeEnumerate(accepted->input())) {
2409 if (!SkScalarsAreFinite(pos.x(), pos.y())) {
2410 continue;
2411 }
2412
2413 const SkGlyphDigest digest = strike->digestFor(kMask, packedID);
2414 switch (digest.actionFor(kMask)) {
2415 case GlyphAction::kAccept: {
2416 const SkPoint mappedPos = creationMatrix.mapPoint(pos);
2417 const SkGlyphRect glyphBounds = digest.bounds().offset(mappedPos);
2418 boundingRect = skglyph::rect_union(boundingRect, glyphBounds);
2419 accepted->accept(packedID, glyphBounds.leftTop(), digest.maskFormat());
2420 break;
2421 }
2422 case GlyphAction::kReject:
2423 rejected->reject(i);
2424 break;
2425 default:
2426 break;
2427 }
2428 }
2429
2430 return boundingRect.rect();
2431 }
2432
prepare_for_path_drawing(StrikeForGPU * strike,SkDrawableGlyphBuffer * accepted,SkSourceGlyphBuffer * rejected)2433 void prepare_for_path_drawing(StrikeForGPU* strike,
2434 SkDrawableGlyphBuffer* accepted,
2435 SkSourceGlyphBuffer* rejected) {
2436 StrikeMutationMonitor m{strike};
2437 for (auto [i, packedID, pos] : SkMakeEnumerate(accepted->input())) {
2438 if (SkScalarsAreFinite(pos.x(), pos.y())) {
2439 switch (strike->digestFor(kPath, packedID).actionFor(kPath)) {
2440 case GlyphAction::kAccept:
2441 accepted->accept(packedID, pos);
2442 break;
2443 case GlyphAction::kReject:
2444 rejected->reject(i);
2445 break;
2446 default:
2447 break;
2448 }
2449 }
2450 }
2451 }
2452
prepare_for_drawable_drawing(StrikeForGPU * strike,SkDrawableGlyphBuffer * accepted,SkSourceGlyphBuffer * rejected)2453 void prepare_for_drawable_drawing(StrikeForGPU* strike,
2454 SkDrawableGlyphBuffer* accepted,
2455 SkSourceGlyphBuffer* rejected) {
2456 StrikeMutationMonitor m{strike};
2457 for (auto [i, packedID, pos] : SkMakeEnumerate(accepted->input())) {
2458 if (SkScalarsAreFinite(pos.x(), pos.y())) {
2459 switch (strike->digestFor(kDrawable, packedID).actionFor(kDrawable)) {
2460 case GlyphAction::kAccept:
2461 accepted->accept(packedID, pos);
2462 break;
2463 case GlyphAction::kReject:
2464 rejected->reject(i);
2465 break;
2466 default:
2467 break;
2468 }
2469 }
2470 }
2471 }
2472
MakeInAlloc(const GlyphRunList & glyphRunList,const SkMatrix & positionMatrix,const SkPaint & runPaint,SkStrikeDeviceInfo strikeDeviceInfo,StrikeForGPUCacheInterface * strikeCache,SubRunAllocator * alloc,SubRunCreationBehavior creationBehavior,const char * tag)2473 SubRunContainerOwner SubRunContainer::MakeInAlloc(
2474 const GlyphRunList& glyphRunList,
2475 const SkMatrix& positionMatrix,
2476 const SkPaint& runPaint,
2477 SkStrikeDeviceInfo strikeDeviceInfo,
2478 StrikeForGPUCacheInterface* strikeCache,
2479 SubRunAllocator* alloc,
2480 SubRunCreationBehavior creationBehavior,
2481 const char* tag) {
2482 SkASSERT(alloc != nullptr);
2483 SkASSERT(strikeDeviceInfo.fSDFTControl != nullptr);
2484
2485 SubRunContainerOwner container = alloc->makeUnique<SubRunContainer>(positionMatrix);
2486 // If there is no SDFT description ignore all SubRuns.
2487 if (strikeDeviceInfo.fSDFTControl == nullptr) {
2488 return container;
2489 }
2490
2491 const SkSurfaceProps deviceProps = strikeDeviceInfo.fSurfaceProps;
2492 const SkScalerContextFlags scalerContextFlags = strikeDeviceInfo.fScalerContextFlags;
2493 #if !defined(SK_DISABLE_SDF_TEXT)
2494 const SDFTControl SDFTControl = *strikeDeviceInfo.fSDFTControl;
2495 const SkScalar maxMaskSize = SDFTControl.maxSize();
2496 #else
2497 const SkScalar maxMaskSize = 256;
2498 #endif
2499
2500 auto bufferScope = SkSubRunBuffers::EnsureBuffers(glyphRunList);
2501 auto [accepted, rejected] = bufferScope.buffers();
2502 SkPoint glyphRunListLocation = glyphRunList.sourceBounds().center();
2503
2504 // Handle all the runs in the glyphRunList
2505 for (auto& glyphRun : glyphRunList) {
2506 rejected->setSource(glyphRun.source());
2507 const SkFont& runFont = glyphRun.font();
2508
2509 SkScalar approximateDeviceTextSize =
2510 // Since the positionMatrix has the origin prepended, use the plain
2511 // sourceBounds from above.
2512 SkFontPriv::ApproximateTransformedTextSize(runFont, positionMatrix,
2513 glyphRunListLocation);
2514
2515
2516 // Atlas mask cases - SDFT and direct mask
2517 // Only consider using direct or SDFT drawing if not drawing hairlines and not too big.
2518 if ((runPaint.getStyle() != SkPaint::kStroke_Style || runPaint.getStrokeWidth() != 0) &&
2519 approximateDeviceTextSize < maxMaskSize) {
2520
2521 #if !defined(SK_DISABLE_SDF_TEXT)
2522 // SDFT case
2523 if (SDFTControl.isSDFT(approximateDeviceTextSize, runPaint, positionMatrix)) {
2524 // Process SDFT - This should be the .009% case.
2525 const auto& [strikeSpec, strikeToSourceScale, matrixRange] =
2526 SkStrikeSpec::MakeSDFT(
2527 runFont, runPaint, deviceProps, positionMatrix,
2528 glyphRunListLocation, SDFTControl);
2529
2530 if (!SkScalarNearlyZero(strikeToSourceScale)) {
2531 sk_sp<StrikeForGPU> strike = strikeSpec.findOrCreateScopedStrike(strikeCache);
2532
2533 // The creationMatrix needs to scale the strike data when inverted and
2534 // multiplied by the positionMatrix. The final CTM should be:
2535 // [positionMatrix][scale by strikeToSourceScale],
2536 // which should equal the following because of the transform during the vertex
2537 // calculation,
2538 // [positionMatrix][creationMatrix]^-1.
2539 // So, the creation matrix needs to be
2540 // [scale by 1/strikeToSourceScale].
2541 SkMatrix creationMatrix =
2542 SkMatrix::Scale(1.f/strikeToSourceScale, 1.f/strikeToSourceScale);
2543
2544 accepted->startSource(rejected->source());
2545
2546 SkRect creationBounds =
2547 prepare_for_SDFT_drawing(strike.get(), creationMatrix, accepted, rejected);
2548 rejected->flipRejectsToSource();
2549
2550 if (creationBehavior == kAddSubRuns && !accepted->empty()) {
2551 container->fSubRuns.append(SDFTSubRun::Make(
2552 accepted->accepted(),
2553 runFont,
2554 strike->strikePromise(),
2555 creationMatrix,
2556 creationBounds,
2557 matrixRange,
2558 alloc));
2559 }
2560 }
2561 }
2562 #endif // !defined(SK_DISABLE_SDF_TEXT)
2563
2564 // Direct Mask case
2565 // Handle all the directly mapped mask subruns.
2566 if (!rejected->source().empty() && !positionMatrix.hasPerspective()) {
2567 // Process masks including ARGB - this should be the 99.99% case.
2568 // This will handle medium size emoji that are sharing the run with SDFT drawn text.
2569 // If things are too big they will be passed along to the drawing of last resort
2570 // below.
2571 SkStrikeSpec strikeSpec = SkStrikeSpec::MakeMask(
2572 runFont, runPaint, deviceProps, scalerContextFlags, positionMatrix);
2573
2574 sk_sp<StrikeForGPU> strike = strikeSpec.findOrCreateScopedStrike(strikeCache);
2575
2576 accepted->startSource(rejected->source());
2577 SkRect bounds =
2578 prepare_for_direct_mask_drawing(
2579 strike.get(), positionMatrix, accepted, rejected);
2580 rejected->flipRejectsToSource();
2581
2582 if (creationBehavior == kAddSubRuns && !accepted->empty()) {
2583 auto addGlyphsWithSameFormat =
2584 [&](const SkZip<SkPackedGlyphID, SkPoint>& acceptedGlyphsAndLocations,
2585 MaskFormat format) {
2586 container->fSubRuns.append(
2587 DirectMaskSubRun::Make(bounds,
2588 acceptedGlyphsAndLocations,
2589 container->initialPosition(),
2590 strike->strikePromise(),
2591 format,
2592 alloc));
2593 };
2594 add_multi_mask_format(addGlyphsWithSameFormat,
2595 accepted->acceptedWithMaskFormat());
2596 }
2597 }
2598 }
2599
2600 // Drawable case
2601 // Handle all the drawable glyphs - usually large or perspective color glyphs.
2602 if (!rejected->source().empty()) {
2603 auto [strikeSpec, strikeToSourceScale] =
2604 SkStrikeSpec::MakePath(runFont, runPaint, deviceProps, scalerContextFlags);
2605
2606 if (!SkScalarNearlyZero(strikeToSourceScale)) {
2607 sk_sp<StrikeForGPU> strike = strikeSpec.findOrCreateScopedStrike(strikeCache);
2608
2609 accepted->startSource(rejected->source());
2610 prepare_for_drawable_drawing(strike.get(), accepted, rejected);
2611 rejected->flipRejectsToSource();
2612
2613 if (creationBehavior == kAddSubRuns && !accepted->empty()) {
2614 container->fSubRuns.append(make_drawable_sub_run<DrawableSubRun>(
2615 accepted->accepted(),
2616 strikeToSourceScale,
2617 strike->strikePromise(),
2618 alloc));
2619 }
2620 }
2621 }
2622
2623 // Path case
2624 // Handle path subruns. Mainly, large or large perspective glyphs with no color.
2625 if (!rejected->source().empty()) {
2626 auto [strikeSpec, strikeToSourceScale] =
2627 SkStrikeSpec::MakePath(runFont, runPaint, deviceProps, scalerContextFlags);
2628
2629 if (!SkScalarNearlyZero(strikeToSourceScale)) {
2630 sk_sp<StrikeForGPU> strike = strikeSpec.findOrCreateScopedStrike(strikeCache);
2631
2632 accepted->startSource(rejected->source());
2633
2634 prepare_for_path_drawing(strike.get(), accepted, rejected);
2635 rejected->flipRejectsToSource();
2636
2637 if (creationBehavior == kAddSubRuns && !accepted->empty()) {
2638 container->fSubRuns.append(
2639 PathSubRun::Make(accepted->accepted(),
2640 has_some_antialiasing(runFont),
2641 strikeToSourceScale,
2642 strike->strikePromise(),
2643 alloc));
2644 }
2645 }
2646 }
2647
2648 // Drawing of last resort case
2649 // Draw all the rest of the rejected glyphs from above. This scales out of the atlas to
2650 // the screen, so quality will suffer. This mainly handles large color or perspective
2651 // color not handled by Drawables.
2652 if (!rejected->source().empty() && !SkScalarNearlyZero(approximateDeviceTextSize)) {
2653 // Creation matrix will be changed below to meet the following criteria:
2654 // * No perspective - the font scaler and the strikes can't handle perspective masks.
2655 // * Fits atlas - creationMatrix will be conditioned so that the maximum glyph
2656 // dimension for this run will be < kMaxBilerpAtlasDimension.
2657 SkMatrix creationMatrix = positionMatrix;
2658
2659 // Condition creationMatrix for perspective.
2660 if (creationMatrix.hasPerspective()) {
2661 // Find a scale factor that reduces pixelation caused by keystoning.
2662 SkPoint center = glyphRunList.sourceBounds().center();
2663 SkScalar maxAreaScale = SkMatrixPriv::DifferentialAreaScale(creationMatrix, center);
2664 SkScalar perspectiveFactor = 1;
2665 if (SkScalarIsFinite(maxAreaScale) && !SkScalarNearlyZero(maxAreaScale)) {
2666 perspectiveFactor = SkScalarSqrt(maxAreaScale);
2667 }
2668
2669 // Masks can not be created in perspective. Create a non-perspective font with a
2670 // scale that will support the perspective keystoning.
2671 creationMatrix = SkMatrix::Scale(perspectiveFactor, perspectiveFactor);
2672 }
2673
2674 // Reduce to make a one pixel border for the bilerp padding.
2675 static const constexpr SkScalar kMaxBilerpAtlasDimension =
2676 SkGlyphDigest::kSkSideTooBigForAtlas - 2;
2677
2678 // Get the raw glyph IDs to simulate device drawing to figure the maximum device
2679 // dimension.
2680 const SkSpan<const SkGlyphID> glyphs = rejected->source().get<0>();
2681
2682 // maxGlyphDimension always returns an integer even though the return type is SkScalar.
2683 auto maxGlyphDimension = [&](const SkMatrix& m) {
2684 const SkStrikeSpec strikeSpec = SkStrikeSpec::MakeTransformMask(
2685 runFont, runPaint, deviceProps, scalerContextFlags, m);
2686 const sk_sp<StrikeForGPU> gaugingStrike =
2687 strikeSpec.findOrCreateScopedStrike(strikeCache);
2688 const SkScalar maxDimension =
2689 find_maximum_glyph_dimension(gaugingStrike.get(), glyphs);
2690 if (maxDimension == 0) {
2691 // Text Scalers don't create glyphs with a dimension larger than 65535. For very
2692 // large sizes, this will cause all the dimensions to go to zero. Use 65535 as
2693 // the dimension.
2694 // TODO: There is a problem where a small character (say .) and a large
2695 // character (say M) are in the same run. If the run is scaled to be very
2696 // large, then the M may return 0 because its dimensions are > 65535, but
2697 // the small character produces regular result because its largest dimension
2698 // is < 65535. This will create an improper scale factor causing the M to be
2699 // too large to fit in the atlas. Tracked by skia:13714.
2700 return 65535.0f;
2701 }
2702 return maxDimension;
2703 };
2704
2705 // Condition the creationMatrix so that glyphs fit in the atlas.
2706 for (SkScalar maxDimension = maxGlyphDimension(creationMatrix);
2707 maxDimension <= 0 || kMaxBilerpAtlasDimension < maxDimension;
2708 maxDimension = maxGlyphDimension(creationMatrix))
2709 {
2710 // The SkScalerContext has a limit of 65536 maximum dimension.
2711 // reductionFactor will always be < 1 because
2712 // maxDimension > kMaxBilerpAtlasDimension, and because maxDimension will always
2713 // be an integer the reduction factor will always be at most 254 / 255.
2714 SkScalar reductionFactor = kMaxBilerpAtlasDimension / maxDimension;
2715 creationMatrix.postScale(reductionFactor, reductionFactor);
2716 }
2717
2718 // Draw using the creationMatrix.
2719 SkStrikeSpec strikeSpec = SkStrikeSpec::MakeTransformMask(
2720 runFont, runPaint, deviceProps, scalerContextFlags, creationMatrix);
2721 sk_sp<StrikeForGPU> strike = strikeSpec.findOrCreateScopedStrike(strikeCache);
2722
2723 accepted->startSource(rejected->source());
2724 SkRect creationBounds =
2725 prepare_for_mask_drawing(strike.get(), creationMatrix, accepted, rejected);
2726 rejected->flipRejectsToSource();
2727 SkASSERT(rejected->source().empty());
2728
2729 if (creationBehavior == kAddSubRuns && !accepted->empty()) {
2730 auto addGlyphsWithSameFormat =
2731 [&](const SkZip<SkPackedGlyphID, SkPoint>& acceptedGlyphsAndLocations,
2732 MaskFormat format) {
2733 container->fSubRuns.append(
2734 TransformedMaskSubRun::Make(acceptedGlyphsAndLocations,
2735 container->initialPosition(),
2736 strike->strikePromise(),
2737 creationMatrix,
2738 creationBounds,
2739 format,
2740 alloc));
2741 };
2742 add_multi_mask_format(addGlyphsWithSameFormat,
2743 accepted->acceptedWithMaskFormat());
2744 }
2745 }
2746 }
2747
2748 return container;
2749 }
2750
2751 #if defined(SK_GANESH)
draw(SkCanvas * canvas,const GrClip * clip,const SkMatrixProvider & viewMatrix,SkPoint drawOrigin,const SkPaint & paint,const SkRefCnt * subRunStorage,skgpu::v1::SurfaceDrawContext * sdc) const2752 void SubRunContainer::draw(SkCanvas* canvas,
2753 const GrClip* clip,
2754 const SkMatrixProvider& viewMatrix,
2755 SkPoint drawOrigin,
2756 const SkPaint& paint,
2757 const SkRefCnt* subRunStorage,
2758 skgpu::v1::SurfaceDrawContext* sdc) const {
2759 for (auto& subRun : fSubRuns) {
2760 subRun.draw(canvas, clip, viewMatrix, drawOrigin, paint, sk_ref_sp(subRunStorage), sdc);
2761 }
2762 }
2763 #endif // defined(SK_GANESH)
2764
2765 #if defined(SK_GRAPHITE)
draw(SkCanvas * canvas,SkPoint drawOrigin,const SkPaint & paint,const SkRefCnt * subRunStorage,skgpu::graphite::Device * device) const2766 void SubRunContainer::draw(SkCanvas* canvas,
2767 SkPoint drawOrigin,
2768 const SkPaint& paint,
2769 const SkRefCnt* subRunStorage,
2770 skgpu::graphite::Device* device) const {
2771 for (auto& subRun : fSubRuns) {
2772 subRun.draw(canvas, drawOrigin, paint, sk_ref_sp(subRunStorage), device);
2773 }
2774 }
2775 #endif
2776
canReuse(const SkPaint & paint,const SkMatrix & positionMatrix) const2777 bool SubRunContainer::canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const {
2778 for (const SubRun& subRun : fSubRuns) {
2779 if (!subRun.canReuse(paint, positionMatrix)) {
2780 return false;
2781 }
2782 }
2783 return true;
2784 }
2785 } // namespace sktext::gpu
2786