2 * Copyright 2015 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
7 #include "src/text/gpu/TextBlob.h"
9 #include "include/core/SkColorFilter.h"
10 #include "include/core/SkScalar.h"
12 #include "include/gpu/GrRecordingContext.h"
14 #include "include/private/SkTemplates.h"
15 #include "include/private/chromium/SkChromeRemoteGlyphCache.h"
16 #include "include/private/chromium/Slug.h"
17 #include "src/core/SkEnumerate.h"
18 #include "src/core/SkFontPriv.h"
19 #include "src/core/SkGlyph.h"
20 #include "src/core/SkMaskFilterBase.h"
21 #include "src/core/SkMatrixProvider.h"
22 #include "src/core/SkPaintPriv.h"
23 #include "src/core/SkReadBuffer.h"
24 #include "src/core/SkStrikeCache.h"
25 #include "src/core/SkStrikeSpec.h"
27 #include "src/gpu/ganesh/GrClip.h"
28 #include "src/gpu/ganesh/GrMeshDrawTarget.h"
29 #include "src/gpu/ganesh/GrStyle.h"
30 #include "src/gpu/ganesh/SkGr.h"
31 #include "src/gpu/ganesh/effects/GrDistanceFieldGeoProc.h"
32 #include "src/gpu/ganesh/geometry/GrStyledShape.h"
35 #include "src/text/gpu/Glyph.h"
36 #include "src/text/gpu/GlyphVector.h"
37 #include "src/text/gpu/SDFTControl.h"
38 #include "src/text/gpu/StrikeCache.h"
39 #include "src/text/gpu/SubRunAllocator.h"
42 #include "src/gpu/ganesh/GrBlurUtils.h"
43 #include "src/gpu/ganesh/ops/AtlasTextOp.h"
44 #include "src/gpu/ganesh/text/GrAtlasManager.h"
45 #include "src/gpu/ganesh/v1/Device_v1.h"
46 #include "src/gpu/ganesh/v1/SurfaceDrawContext_v1.h"
48 using AtlasTextOp = skgpu::v1::AtlasTextOp;
53 using MaskFormat = skgpu::MaskFormat;
55 namespace sktext::gpu {
56 // -- GPU Text -------------------------------------------------------------------------------------
57 // There are three broad types of SubRun implementations for drawing text using the GPU.
58 // TextBlob (runs with no postfix) - these runs support drawing for TextBlobs.
59 // sktext::gpu::Slug (Slug postfix) - these runs support drawing of Slugs.
62 // * drawMatrix - the CTM from the canvas.
63 // * drawOrigin - the x, y location of the drawTextBlob call.
64 // * positionMatrix - this is the combination of the drawMatrix and the drawOrigin:
65 // positionMatrix = drawMatrix * TranslationMatrix(drawOrigin.x, drawOrigin.y);
68 // In order to use Slugs, you need to set the fSupportBilerpFromGlyphAtlas on GrContextOptions.
70 enum SubRun::SubRunType : int {
71 kBad = 0, // Make this 0 to line up with errors from readInt.
80 // -- SubRun -------------------------------------------------------------------------------------
81 SubRun::~SubRun() = default;
82 const BlobSubRun* SubRun::blobCast() const {
83 SK_ABORT("This is not a subclass of GrBlobSubRun.");
85 } // namespace sktext::gpu
87 using namespace sktext::gpu;
91 bool pun_read(SkReadBuffer& buffer, T* dst) {
92 return buffer.readPad32(dst, sizeof(T));
96 void pun_write(SkWriteBuffer& buffer, const T& src) {
97 buffer.writePad32(&src, sizeof(T));
100 // -- TransformedMaskVertexFiller ------------------------------------------------------------------
101 class TransformedMaskVertexFiller {
102 struct PositionAndExtent;
104 TransformedMaskVertexFiller(MaskFormat maskFormat,
106 SkScalar strikeToSourceScale,
108 SkSpan<const PositionAndExtent> positionAndExtent);
110 static TransformedMaskVertexFiller Make(MaskFormat maskType,
112 SkScalar strikeToSourceScale,
113 const SkZip<SkGlyphVariant, SkPoint>& accepted,
114 SubRunAllocator* alloc);
116 static std::optional<TransformedMaskVertexFiller> MakeFromBuffer(
117 SkReadBuffer& buffer, SubRunAllocator* alloc);
118 int unflattenSize() const;
119 void flatten(SkWriteBuffer& buffer) const;
122 size_t vertexStride(const SkMatrix& matrix) const {
123 if (fMaskType != MaskFormat::kARGB) {
124 // For formats MaskFormat::kA565 and MaskFormat::kA8 where A8 include SDF.
125 return matrix.hasPerspective() ? sizeof(Mask3DVertex) : sizeof(Mask2DVertex);
127 // For format MaskFormat::kARGB
128 return matrix.hasPerspective() ? sizeof(ARGB3DVertex) : sizeof(ARGB2DVertex);
132 void fillVertexData(int offset, int count,
133 SkSpan<const Glyph*> glyphs,
135 const SkMatrix& positionMatrix,
137 void* vertexBuffer) const;
139 AtlasTextOp::MaskType opMaskType() const;
141 SkRect deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const;
142 MaskFormat grMaskType() const {return fMaskType;}
143 int count() const { return SkCount(fPositionAndExtent); }
146 struct PositionAndExtent {
148 // The rectangle of the glyphs in strike space. But, for kDirectMask this also implies a
149 // device space rect.
159 // Normal text mask, SDFT, or color.
160 struct Mask2DVertex {
166 struct ARGB2DVertex {
167 ARGB2DVertex(SkPoint d, GrColor, AtlasPt a) : devicePos{d}, atlasPos{a} {}
173 // Perspective SDFT or SDFT forced to 3D or perspective color.
174 struct Mask3DVertex {
180 struct ARGB3DVertex {
181 ARGB3DVertex(SkPoint3 d, GrColor, AtlasPt a) : devicePos{d}, atlasPos{a} {}
188 std::array<SkScalar, 4> sourceRect(PositionAndExtent positionAndExtent) const;
191 template<typename Quad, typename VertexData>
192 void fill2D(SkZip<Quad, const Glyph*, const VertexData> quadData,
194 const SkMatrix& matrix) const;
196 template<typename Quad, typename VertexData>
197 void fill3D(SkZip<Quad, const Glyph*, const VertexData> quadData,
199 const SkMatrix& matrix) const;
202 const MaskFormat fMaskType;
203 const SkPoint fPaddingInset;
204 const SkScalar fStrikeToSourceScale;
205 const SkRect fSourceBounds;
206 const SkSpan<const PositionAndExtent> fPositionAndExtent;
209 TransformedMaskVertexFiller::TransformedMaskVertexFiller(
210 MaskFormat maskFormat,
212 SkScalar strikeToSourceScale,
214 SkSpan<const PositionAndExtent> positionAndExtent)
215 : fMaskType{maskFormat}
216 , fPaddingInset{SkPoint::Make(dstPadding, dstPadding)}
217 , fStrikeToSourceScale{strikeToSourceScale}
218 , fSourceBounds{sourceBounds}
219 , fPositionAndExtent{positionAndExtent} {}
221 TransformedMaskVertexFiller TransformedMaskVertexFiller::Make(
224 SkScalar strikeToSourceScale,
225 const SkZip<SkGlyphVariant, SkPoint>& accepted,
226 SubRunAllocator* alloc) {
227 SkRect sourceBounds = SkRectPriv::MakeLargestInverted();
228 SkSpan<PositionAndExtent> positionAndExtent = alloc->makePODArray<PositionAndExtent>(
231 auto [variant, pos] = e;
232 const SkGlyph* skGlyph = variant;
233 int16_t l = skGlyph->left(),
235 r = l + skGlyph->width(),
236 b = t + skGlyph->height();
237 SkPoint lt = SkPoint::Make(l, t) * strikeToSourceScale + pos,
238 rb = SkPoint::Make(r, b) * strikeToSourceScale + pos;
240 sourceBounds.joinPossiblyEmptyRect(
241 SkRect::MakeLTRB(lt.x(), lt.y(), rb.x(), rb.y()));
242 return PositionAndExtent{pos, {l, t, r, b}};
244 return TransformedMaskVertexFiller{
245 maskType, dstPadding, strikeToSourceScale, sourceBounds, positionAndExtent};
248 static bool check_glyph_count(SkReadBuffer& buffer, int glyphCount) {
249 return 0 < glyphCount && static_cast<size_t>(glyphCount) < (buffer.available() / 4);
252 std::optional<TransformedMaskVertexFiller> TransformedMaskVertexFiller::MakeFromBuffer(
253 SkReadBuffer& buffer, SubRunAllocator* alloc) {
254 int checkingMaskType = buffer.readInt();
255 if (!buffer.validate(0 <= checkingMaskType && checkingMaskType < skgpu::kMaskFormatCount)) {
258 MaskFormat maskType = (MaskFormat)checkingMaskType;
259 int dstPadding = buffer.readInt();
260 if (!buffer.validate(0 <= dstPadding && dstPadding <= 2)) { return {}; }
261 SkScalar strikeToSourceScale = buffer.readScalar();
262 if (!buffer.validate(0 < strikeToSourceScale)) { return {}; }
263 SkRect sourceBounds = buffer.readRect();
265 int glyphCount = buffer.readInt();
266 if (!buffer.validate(check_glyph_count(buffer, glyphCount))) { return {}; }
267 PositionAndExtent* positionAndExtentStorage =
268 alloc->makePODArray<PositionAndExtent>(glyphCount);
269 for (int i = 0; i < glyphCount; ++i) {
270 pun_read(buffer, &positionAndExtentStorage[i]);
272 SkSpan<PositionAndExtent> positionAndExtent(positionAndExtentStorage, glyphCount);
274 return {TransformedMaskVertexFiller{
275 maskType, dstPadding, strikeToSourceScale, sourceBounds, positionAndExtent}};
278 SkRect TransformedMaskVertexFiller::deviceRect(
279 const SkMatrix& drawMatrix, SkPoint drawOrigin) const {
280 SkRect outBounds = fSourceBounds;
281 outBounds.offset(drawOrigin);
282 return drawMatrix.mapRect(outBounds);
285 int TransformedMaskVertexFiller::unflattenSize() const {
286 return fPositionAndExtent.size_bytes();
289 void TransformedMaskVertexFiller::flatten(SkWriteBuffer& buffer) const {
290 buffer.writeInt(static_cast<int>(fMaskType));
291 buffer.writeInt(SkScalarRoundToInt(fPaddingInset.x()));
292 buffer.writeScalar(fStrikeToSourceScale);
293 buffer.writeRect(fSourceBounds);
294 buffer.writeInt(SkCount(fPositionAndExtent));
295 for (auto posAndExt : fPositionAndExtent) {
296 pun_write(buffer, posAndExt);
300 std::array<SkScalar, 4>
301 TransformedMaskVertexFiller::sourceRect(PositionAndExtent positionAndExtent) const {
302 auto[pos, rect] = positionAndExtent;
303 auto[l, t, r, b] = rect;
304 SkPoint LT = (SkPoint::Make(l, t) + fPaddingInset) * fStrikeToSourceScale + pos,
305 RB = (SkPoint::Make(r, b) - fPaddingInset) * fStrikeToSourceScale + pos;
306 return {LT.x(), LT.y(), RB.x(), RB.y()};
310 void TransformedMaskVertexFiller::fillVertexData(int offset, int count,
311 SkSpan<const Glyph*> glyphs,
313 const SkMatrix& positionMatrix,
315 void* vertexBuffer) const {
316 auto quadData = [&](auto dst) {
317 return SkMakeZip(dst,
318 glyphs.subspan(offset, count),
319 fPositionAndExtent.subspan(offset, count));
322 if (!positionMatrix.hasPerspective()) {
323 if (fMaskType == MaskFormat::kARGB) {
324 using Quad = ARGB2DVertex[4];
325 SkASSERT(sizeof(ARGB2DVertex) == this->vertexStride(positionMatrix));
326 this->fill2D(quadData((Quad*) vertexBuffer), color, positionMatrix);
328 using Quad = Mask2DVertex[4];
329 SkASSERT(sizeof(Mask2DVertex) == this->vertexStride(positionMatrix));
330 this->fill2D(quadData((Quad*) vertexBuffer), color, positionMatrix);
333 if (fMaskType == MaskFormat::kARGB) {
334 using Quad = ARGB3DVertex[4];
335 SkASSERT(sizeof(ARGB3DVertex) == this->vertexStride(positionMatrix));
336 this->fill3D(quadData((Quad*) vertexBuffer), color, positionMatrix);
338 using Quad = Mask3DVertex[4];
339 SkASSERT(sizeof(Mask3DVertex) == this->vertexStride(positionMatrix));
340 this->fill3D(quadData((Quad*) vertexBuffer), color, positionMatrix);
345 template<typename Quad, typename VertexData>
346 void TransformedMaskVertexFiller::fill2D(SkZip<Quad, const Glyph*, const VertexData> quadData,
348 const SkMatrix& positionMatrix) const {
349 for (auto[quad, glyph, positionAndExtent] : quadData) {
350 auto [l, t, r, b] = this->sourceRect(positionAndExtent);
351 SkPoint lt = positionMatrix.mapXY(l, t),
352 lb = positionMatrix.mapXY(l, b),
353 rt = positionMatrix.mapXY(r, t),
354 rb = positionMatrix.mapXY(r, b);
355 auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
356 quad[0] = {lt, color, {al, at}}; // L,T
357 quad[1] = {lb, color, {al, ab}}; // L,B
358 quad[2] = {rt, color, {ar, at}}; // R,T
359 quad[3] = {rb, color, {ar, ab}}; // R,B
363 template<typename Quad, typename VertexData>
364 void TransformedMaskVertexFiller::fill3D(SkZip<Quad, const Glyph*, const VertexData> quadData,
366 const SkMatrix& positionMatrix) const {
367 auto mapXYZ = [&](SkScalar x, SkScalar y) {
370 positionMatrix.mapHomogeneousPoints(&result, &pt, 1);
373 for (auto[quad, glyph, positionAndExtent] : quadData) {
374 auto [l, t, r, b] = this->sourceRect(positionAndExtent);
375 SkPoint3 lt = mapXYZ(l, t),
379 auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
380 quad[0] = {lt, color, {al, at}}; // L,T
381 quad[1] = {lb, color, {al, ab}}; // L,B
382 quad[2] = {rt, color, {ar, at}}; // R,T
383 quad[3] = {rb, color, {ar, ab}}; // R,B
387 AtlasTextOp::MaskType TransformedMaskVertexFiller::opMaskType() const {
389 case MaskFormat::kA8: return AtlasTextOp::MaskType::kGrayscaleCoverage;
390 case MaskFormat::kA565: return AtlasTextOp::MaskType::kLCDCoverage;
391 case MaskFormat::kARGB: return AtlasTextOp::MaskType::kColorBitmap;
403 // Normal text mask, SDFT, or color.
404 struct Mask2DVertex {
410 struct ARGB2DVertex {
411 ARGB2DVertex(SkPoint d, GrColor, AtlasPt a) : devicePos{d}, atlasPos{a} {}
417 // Perspective SDFT or SDFT forced to 3D or perspective color.
418 struct Mask3DVertex {
424 struct ARGB3DVertex {
425 ARGB3DVertex(SkPoint3 d, GrColor, AtlasPt a) : devicePos{d}, atlasPos{a} {}
431 AtlasTextOp::MaskType op_mask_type(MaskFormat maskFormat) {
432 switch (maskFormat) {
433 case MaskFormat::kA8: return AtlasTextOp::MaskType::kGrayscaleCoverage;
434 case MaskFormat::kA565: return AtlasTextOp::MaskType::kLCDCoverage;
435 case MaskFormat::kARGB: return AtlasTextOp::MaskType::kColorBitmap;
440 SkPMColor4f calculate_colors(skgpu::SurfaceContext* sc,
441 const SkPaint& paint,
442 const SkMatrixProvider& matrix,
443 MaskFormat maskFormat,
445 GrRecordingContext* rContext = sc->recordingContext();
446 const GrColorInfo& colorInfo = sc->colorInfo();
447 if (maskFormat == MaskFormat::kARGB) {
448 SkPaintToGrPaintReplaceShader(rContext, colorInfo, paint, matrix, nullptr, grPaint);
449 float a = grPaint->getColor4f().fA;
452 SkPaintToGrPaint(rContext, colorInfo, paint, matrix, grPaint);
453 return grPaint->getColor4f();
457 SkMatrix position_matrix(const SkMatrix& drawMatrix, SkPoint drawOrigin) {
458 SkMatrix position_matrix = drawMatrix;
459 return position_matrix.preTranslate(drawOrigin.x(), drawOrigin.y());
462 // Check for integer translate with the same 2x2 matrix.
463 // Returns the translation, and true if the change from initial matrix to the position matrix
464 // support using direct glyph masks.
465 std::tuple<bool, SkVector> can_use_direct(
466 const SkMatrix& initialPositionMatrix, const SkMatrix& positionMatrix) {
467 // The existing direct glyph info can be used if the initialPositionMatrix, and the
468 // positionMatrix have the same 2x2, and the translation between them is integer.
469 // Calculate the translation in source space to a translation in device space by mapping
470 // (0, 0) through both the initial position matrix and the position matrix; take the difference.
471 SkVector translation = positionMatrix.mapOrigin() - initialPositionMatrix.mapOrigin();
472 return {initialPositionMatrix.getScaleX() == positionMatrix.getScaleX() &&
473 initialPositionMatrix.getScaleY() == positionMatrix.getScaleY() &&
474 initialPositionMatrix.getSkewX() == positionMatrix.getSkewX() &&
475 initialPositionMatrix.getSkewY() == positionMatrix.getSkewY() &&
476 SkScalarIsInt(translation.x()) && SkScalarIsInt(translation.y()),
480 // -- PathOpSubmitter ------------------------------------------------------------------------------
481 // Shared code for submitting GPU ops for drawing glyphs as paths.
482 class PathOpSubmitter {
486 PathOpSubmitter(bool isAntiAliased,
487 SkScalar strikeToSourceScale,
488 SkSpan<SkPoint> positions,
489 SkSpan<SkGlyphID> glyphIDs,
490 std::unique_ptr<SkPath[], SubRunAllocator::ArrayDestroyer> paths,
491 const SkDescriptor& descriptor);
493 static PathOpSubmitter Make(const SkZip<SkGlyphVariant, SkPoint>& accepted,
495 SkScalar strikeToSourceScale,
496 const SkDescriptor& descriptor,
497 SubRunAllocator* alloc);
499 int unflattenSize() const;
500 void flatten(SkWriteBuffer& buffer) const;
501 static std::optional<PathOpSubmitter> MakeFromBuffer(SkReadBuffer& buffer,
502 SubRunAllocator* alloc,
503 const SkStrikeClient* client);
506 void submitOps(SkCanvas*,
508 const SkMatrixProvider& viewMatrix,
510 const SkPaint& paint,
511 skgpu::v1::SurfaceDrawContext* sdc) const;
515 const bool fIsAntiAliased;
516 const SkScalar fStrikeToSourceScale;
517 const SkSpan<const SkPoint> fPositions;
518 const SkSpan<const SkGlyphID> fGlyphIDs;
519 std::unique_ptr<SkPath[], SubRunAllocator::ArrayDestroyer> fPaths;
520 const SkAutoDescriptor fDescriptor;
523 int PathOpSubmitter::unflattenSize() const {
524 return fPositions.size_bytes() + fGlyphIDs.size_bytes() + SkCount(fGlyphIDs) * sizeof(SkPath);
527 void PathOpSubmitter::flatten(SkWriteBuffer& buffer) const {
528 buffer.writeInt(fIsAntiAliased);
529 buffer.writeScalar(fStrikeToSourceScale);
530 buffer.writeInt(SkCount(fPositions));
531 for (auto pos : fPositions) {
532 buffer.writePoint(pos);
534 for (SkGlyphID glyphID : fGlyphIDs) {
535 buffer.writeInt(glyphID);
537 fDescriptor.getDesc()->flatten(buffer);
540 std::optional<PathOpSubmitter> PathOpSubmitter::MakeFromBuffer(SkReadBuffer& buffer,
541 SubRunAllocator* alloc,
542 const SkStrikeClient* client) {
543 bool isAntiAlias = buffer.readInt();
544 SkScalar strikeToSourceScale = buffer.readScalar();
546 int glyphCount = buffer.readInt();
547 if (!buffer.validate(check_glyph_count(buffer, glyphCount))) { return {}; }
548 if (!buffer.validateCanReadN<SkPoint>(glyphCount)) { return {}; }
549 SkPoint* positions = alloc->makePODArray<SkPoint>(glyphCount);
550 for (int i = 0; i < glyphCount; ++i) {
551 positions[i] = buffer.readPoint();
554 // Remember, we stored an int for glyph id.
555 if (!buffer.validateCanReadN<int>(glyphCount)) { return {}; }
556 SkGlyphID* glyphIDs = alloc->makePODArray<SkGlyphID>(glyphCount);
557 for (int i = 0; i < glyphCount; ++i) {
558 glyphIDs[i] = SkTo<SkGlyphID>(buffer.readInt());
561 auto descriptor = SkAutoDescriptor::MakeFromBuffer(buffer);
562 if (!buffer.validate(descriptor.has_value())) { return {}; }
564 // Translate the TypefaceID if this was transferred from the GPU process.
565 if (client != nullptr) {
566 if (!client->translateTypefaceID(&descriptor.value())) { return {}; }
569 auto strike = SkStrikeCache::GlobalStrikeCache()->findStrike(*descriptor->getDesc());
570 if (!buffer.validate(strike != nullptr)) { return {}; }
572 auto paths = alloc->makeUniqueArray<SkPath>(glyphCount);
573 SkBulkGlyphMetricsAndPaths pathGetter{std::move(strike)};
575 for (auto [i, glyphID] : SkMakeEnumerate(SkMakeSpan(glyphIDs, glyphCount))) {
576 const SkPath* path = pathGetter.glyph(glyphID)->path();
577 // There should never be missing paths in a sub run.
578 if (path == nullptr) { return {}; }
582 SkASSERT(buffer.isValid());
583 return {PathOpSubmitter{isAntiAlias,
585 SkMakeSpan(positions, glyphCount),
586 SkMakeSpan(glyphIDs, glyphCount),
588 *descriptor->getDesc()}};
591 PathOpSubmitter::PathOpSubmitter(
593 SkScalar strikeToSourceScale,
594 SkSpan<SkPoint> positions,
595 SkSpan<SkGlyphID> glyphIDs,
596 std::unique_ptr<SkPath[], SubRunAllocator::ArrayDestroyer> paths,
597 const SkDescriptor& descriptor)
598 : fIsAntiAliased{isAntiAliased}
599 , fStrikeToSourceScale{strikeToSourceScale}
600 , fPositions{positions}
601 , fGlyphIDs{glyphIDs}
602 , fPaths{std::move(paths)}
603 , fDescriptor{descriptor} {
604 SkASSERT(!fPositions.empty());
607 PathOpSubmitter PathOpSubmitter::Make(const SkZip<SkGlyphVariant, SkPoint>& accepted,
609 SkScalar strikeToSourceScale,
610 const SkDescriptor& descriptor,
611 SubRunAllocator* alloc) {
612 int glyphCount = SkCount(accepted);
613 SkPoint* positions = alloc->makePODArray<SkPoint>(glyphCount);
614 SkGlyphID* glyphIDs = alloc->makePODArray<SkGlyphID>(glyphCount);
615 auto paths = alloc->makeUniqueArray<SkPath>(glyphCount);
617 for (auto [i, variant, pos] : SkMakeEnumerate(accepted)) {
619 glyphIDs[i] = variant.glyph()->getGlyphID();
620 paths[i] = *variant.glyph()->path();
623 return PathOpSubmitter{isAntiAliased,
625 SkMakeSpan(positions, glyphCount),
626 SkMakeSpan(glyphIDs, glyphCount),
632 void PathOpSubmitter::submitOps(SkCanvas* canvas,
634 const SkMatrixProvider& viewMatrix,
636 const SkPaint& paint,
637 skgpu::v1::SurfaceDrawContext* sdc) const {
638 SkPaint runPaint{paint};
639 runPaint.setAntiAlias(fIsAntiAliased);
642 SkMaskFilterBase* maskFilter = as_MFB(runPaint.getMaskFilter());
644 // Calculate the matrix that maps the path glyphs from their size in the strike to
645 // the graphics source space.
646 SkMatrix strikeToSource = SkMatrix::Scale(fStrikeToSourceScale, fStrikeToSourceScale);
647 strikeToSource.postTranslate(drawOrigin.x(), drawOrigin.y());
649 // If there are shaders, non-blur mask filters or styles, the path must be scaled into source
650 // space independently of the CTM. This allows the CTM to be correct for the different effects.
651 GrStyle style(runPaint);
652 bool needsExactCTM = runPaint.getShader()
654 || (maskFilter != nullptr && !maskFilter->asABlur(nullptr));
655 if (!needsExactCTM) {
656 SkMaskFilterBase::BlurRec blurRec;
658 // If there is a blur mask filter, then sigma needs to be adjusted to account for the
659 // scaling of fStrikeToSourceScale.
660 if (maskFilter != nullptr && maskFilter->asABlur(&blurRec)) {
661 runPaint.setMaskFilter(
662 SkMaskFilter::MakeBlur(blurRec.fStyle, blurRec.fSigma / fStrikeToSourceScale));
664 for (auto [path, pos] : SkMakeZip(fPaths.get(), fPositions)) {
665 // Transform the glyph to source space.
666 SkMatrix pathMatrix = strikeToSource;
667 pathMatrix.postTranslate(pos.x(), pos.y());
669 SkAutoCanvasRestore acr(canvas, true);
670 canvas->concat(pathMatrix);
671 canvas->drawPath(path, runPaint);
674 // Transform the path to device because the deviceMatrix must be unchanged to
675 // draw effect, filter or shader paths.
676 for (auto [path, pos] : SkMakeZip(fPaths.get(), fPositions)) {
677 // Transform the glyph to source space.
678 SkMatrix pathMatrix = strikeToSource;
679 pathMatrix.postTranslate(pos.x(), pos.y());
681 SkPath deviceOutline;
682 path.transform(pathMatrix, &deviceOutline);
683 deviceOutline.setIsVolatile(true);
684 canvas->drawPath(deviceOutline, runPaint);
690 // -- PathSubRun -----------------------------------------------------------------------------------
691 class PathSubRun final : public SubRun {
693 PathSubRun(PathOpSubmitter&& pathDrawing) : fPathDrawing(std::move(pathDrawing)) {}
695 static SubRunOwner Make(const SkZip<SkGlyphVariant, SkPoint>& accepted,
697 SkScalar strikeToSourceScale,
698 const SkDescriptor& descriptor,
699 SubRunAllocator* alloc) {
700 return alloc->makeUnique<PathSubRun>(
701 PathOpSubmitter::Make(
702 accepted, isAntiAliased, strikeToSourceScale, descriptor, alloc));
706 void draw(SkCanvas* canvas,
708 const SkMatrixProvider& viewMatrix,
710 const SkPaint& paint,
711 sk_sp<SkRefCnt> subRunStorage,
712 skgpu::v1::SurfaceDrawContext* sdc) const override {
713 fPathDrawing.submitOps(canvas, clip, viewMatrix, drawOrigin, paint, sdc);
717 int unflattenSize() const override;
719 bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const override {
722 const AtlasSubRun* testingOnly_atlasSubRun() const override { return nullptr; }
723 static SubRunOwner MakeFromBuffer(const SkMatrix& initialPositionMatrix,
724 SkReadBuffer& buffer,
725 SubRunAllocator* alloc,
726 const SkStrikeClient* client);
729 SubRunType subRunType() const override { return kPath; }
730 void doFlatten(SkWriteBuffer& buffer) const override;
733 PathOpSubmitter fPathDrawing;
736 int PathSubRun::unflattenSize() const {
737 return sizeof(PathSubRun) + fPathDrawing.unflattenSize();
740 void PathSubRun::doFlatten(SkWriteBuffer& buffer) const {
741 fPathDrawing.flatten(buffer);
744 SubRunOwner PathSubRun::MakeFromBuffer(const SkMatrix& initialPositionMatrix,
745 SkReadBuffer& buffer,
746 SubRunAllocator* alloc,
747 const SkStrikeClient* client) {
748 auto pathOpSubmitter = PathOpSubmitter::MakeFromBuffer(buffer, alloc, client);
749 if (!buffer.validate(pathOpSubmitter.has_value())) { return nullptr; }
750 return alloc->makeUnique<PathSubRun>(std::move(*pathOpSubmitter));
753 // -- DrawableOpSubmitter --------------------------------------------------------------------------
754 // Shared code for submitting GPU ops for drawing glyphs as drawables.
755 class DrawableOpSubmitter {
757 DrawableOpSubmitter(SkScalar strikeToSourceScale,
758 SkSpan<SkPoint> positions,
759 SkSpan<SkGlyphID> glyphIDs,
760 SkSpan<SkDrawable*> drawableData,
761 sk_sp<SkStrike>&& strike,
762 const SkDescriptor& descriptor);
764 static DrawableOpSubmitter Make(const SkZip<SkGlyphVariant, SkPoint>& accepted,
765 sk_sp<SkStrike>&& strike,
766 SkScalar strikeToSourceScale,
767 const SkDescriptor& descriptor,
768 SubRunAllocator* alloc);
770 int unflattenSize() const;
771 void flatten(SkWriteBuffer& buffer) const;
772 static std::optional<DrawableOpSubmitter> MakeFromBuffer(SkReadBuffer& buffer,
773 SubRunAllocator* alloc,
774 const SkStrikeClient* client);
776 void submitOps(SkCanvas*,
778 const SkMatrixProvider& viewMatrix,
780 const SkPaint& paint,
781 skgpu::v1::SurfaceDrawContext* sdc) const;
785 const SkScalar fStrikeToSourceScale;
786 const SkSpan<SkPoint> fPositions;
787 const SkSpan<SkGlyphID> fGlyphIDs;
788 const SkSpan<SkDrawable*> fDrawables;
789 sk_sp<SkStrike> fStrike; // Owns the fDrawables.
790 const SkAutoDescriptor fDescriptor;
793 int DrawableOpSubmitter::unflattenSize() const {
794 return fPositions.size_bytes() +
795 fGlyphIDs.size_bytes() +
796 SkCount(fPositions) * sizeof(sk_sp<SkDrawable>);
799 void DrawableOpSubmitter::flatten(SkWriteBuffer& buffer) const {
800 buffer.writeScalar(fStrikeToSourceScale);
801 buffer.writeInt(SkCount(fPositions));
802 for (auto pos : fPositions) {
803 buffer.writePoint(pos);
805 for (SkGlyphID glyphID : fGlyphIDs) {
806 buffer.writeInt(glyphID);
808 fDescriptor.getDesc()->flatten(buffer);
811 std::optional<DrawableOpSubmitter> DrawableOpSubmitter::MakeFromBuffer(
812 SkReadBuffer& buffer, SubRunAllocator* alloc, const SkStrikeClient* client) {
813 SkScalar strikeToSourceScale = buffer.readScalar();
815 int glyphCount = buffer.readInt();
816 if (!buffer.validate(check_glyph_count(buffer, glyphCount))) { return {}; }
817 if (!buffer.validateCanReadN<SkPoint>(glyphCount)) { return {}; }
818 SkPoint* positions = alloc->makePODArray<SkPoint>(glyphCount);
819 for (int i = 0; i < glyphCount; ++i) {
820 positions[i] = buffer.readPoint();
823 // Remember, we stored an int for glyph id.
824 if (!buffer.validateCanReadN<int>(glyphCount)) { return {}; }
825 SkGlyphID* glyphIDs = alloc->makePODArray<SkGlyphID>(glyphCount);
826 for (int i = 0; i < glyphCount; ++i) {
827 glyphIDs[i] = SkTo<SkGlyphID>(buffer.readInt());
830 auto descriptor = SkAutoDescriptor::MakeFromBuffer(buffer);
831 if (!buffer.validate(descriptor.has_value())) { return {}; }
833 // Translate the TypefaceID if this was transferred from the GPU process.
834 if (client != nullptr) {
835 if (!client->translateTypefaceID(&descriptor.value())) { return {}; }
838 auto strike = SkStrikeCache::GlobalStrikeCache()->findStrike(*descriptor->getDesc());
839 if (!buffer.validate(strike != nullptr)) { return {}; }
841 auto drawables = alloc->makePODArray<SkDrawable*>(glyphCount);
842 SkBulkGlyphMetricsAndDrawables drawableGetter(sk_sp<SkStrike>{strike});
843 auto glyphs = drawableGetter.glyphs(SkMakeSpan(glyphIDs, glyphCount));
845 for (auto [i, glyph] : SkMakeEnumerate(glyphs)) {
846 drawables[i] = glyph->drawable();
849 SkASSERT(buffer.isValid());
850 return {DrawableOpSubmitter{strikeToSourceScale,
851 SkMakeSpan(positions, glyphCount),
852 SkMakeSpan(glyphIDs, glyphCount),
853 SkMakeSpan(drawables, glyphCount),
855 *descriptor->getDesc()}};
858 DrawableOpSubmitter::DrawableOpSubmitter(
859 SkScalar strikeToSourceScale,
860 SkSpan<SkPoint> positions,
861 SkSpan<SkGlyphID> glyphIDs,
862 SkSpan<SkDrawable*> drawables,
863 sk_sp<SkStrike>&& strike,
864 const SkDescriptor& descriptor)
865 : fStrikeToSourceScale{strikeToSourceScale}
866 , fPositions{positions}
867 , fGlyphIDs{glyphIDs}
868 , fDrawables{std::move(drawables)}
869 , fStrike(std::move(strike))
870 , fDescriptor{descriptor} {
871 SkASSERT(!fPositions.empty());
874 DrawableOpSubmitter DrawableOpSubmitter::Make(const SkZip<SkGlyphVariant, SkPoint>& accepted,
875 sk_sp<SkStrike>&& strike,
876 SkScalar strikeToSourceScale,
877 const SkDescriptor& descriptor,
878 SubRunAllocator* alloc) {
879 int glyphCount = SkCount(accepted);
880 SkPoint* positions = alloc->makePODArray<SkPoint>(glyphCount);
881 SkGlyphID* glyphIDs = alloc->makePODArray<SkGlyphID>(glyphCount);
882 SkDrawable** drawables = alloc->makePODArray<SkDrawable*>(glyphCount);
883 for (auto [i, variant, pos] : SkMakeEnumerate(accepted)) {
885 glyphIDs[i] = variant.glyph()->getGlyphID();
886 drawables[i] = variant.glyph()->drawable();
889 return DrawableOpSubmitter{strikeToSourceScale,
890 SkMakeSpan(positions, glyphCount),
891 SkMakeSpan(glyphIDs, glyphCount),
892 SkMakeSpan(drawables, glyphCount),
898 void DrawableOpSubmitter::submitOps(SkCanvas* canvas,
900 const SkMatrixProvider& viewMatrix,
902 const SkPaint& paint,
903 skgpu::v1::SurfaceDrawContext* sdc) const {
904 // Calculate the matrix that maps the path glyphs from their size in the strike to
905 // the graphics source space.
906 SkMatrix strikeToSource = SkMatrix::Scale(fStrikeToSourceScale, fStrikeToSourceScale);
907 strikeToSource.postTranslate(drawOrigin.x(), drawOrigin.y());
909 // Transform the path to device because the deviceMatrix must be unchanged to
910 // draw effect, filter or shader paths.
911 for (auto [i, position] : SkMakeEnumerate(fPositions)) {
912 SkDrawable* drawable = fDrawables[i];
913 // Transform the glyph to source space.
914 SkMatrix pathMatrix = strikeToSource;
915 pathMatrix.postTranslate(position.x(), position.y());
917 SkAutoCanvasRestore acr(canvas, false);
918 SkRect drawableBounds = drawable->getBounds();
919 pathMatrix.mapRect(&drawableBounds);
920 canvas->saveLayer(&drawableBounds, &paint);
921 drawable->draw(canvas, &pathMatrix);
926 template <typename SubRunT>
927 SubRunOwner make_drawable_sub_run(const SkZip<SkGlyphVariant, SkPoint>& drawables,
928 sk_sp<SkStrike>&& strike,
929 SkScalar strikeToSourceScale,
930 const SkDescriptor& descriptor,
931 SubRunAllocator* alloc) {
932 return alloc->makeUnique<SubRunT>(
933 DrawableOpSubmitter::Make(drawables, std::move(strike),
934 strikeToSourceScale, descriptor, alloc));
937 // -- DrawableSubRun -------------------------------------------------------------------------------
938 class DrawableSubRun : public SubRun {
940 DrawableSubRun(DrawableOpSubmitter&& drawingDrawing)
941 : fDrawingDrawing(std::move(drawingDrawing)) {}
943 static SubRunOwner MakeFromBuffer(const SkMatrix&,
944 SkReadBuffer& buffer,
945 SubRunAllocator* alloc,
946 const SkStrikeClient* client);
948 void draw(SkCanvas* canvas,
950 const SkMatrixProvider& viewMatrix,
952 const SkPaint& paint,
953 sk_sp<SkRefCnt> subRunStorage,
954 skgpu::v1::SurfaceDrawContext* sdc) const override {
955 fDrawingDrawing.submitOps(canvas, clip, viewMatrix, drawOrigin, paint, sdc);
959 int unflattenSize() const override;
961 bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const override;
963 const AtlasSubRun* testingOnly_atlasSubRun() const override;
966 SubRunType subRunType() const override { return kDrawable; }
967 void doFlatten(SkWriteBuffer& buffer) const override;
970 DrawableOpSubmitter fDrawingDrawing;
973 int DrawableSubRun::unflattenSize() const {
974 return sizeof(DrawableSubRun) + fDrawingDrawing.unflattenSize();
977 void DrawableSubRun::doFlatten(SkWriteBuffer& buffer) const {
978 fDrawingDrawing.flatten(buffer);
981 SubRunOwner DrawableSubRun::MakeFromBuffer(const SkMatrix&,
982 SkReadBuffer& buffer,
983 SubRunAllocator* alloc,
984 const SkStrikeClient* client) {
985 auto drawableOpSubmitter = DrawableOpSubmitter::MakeFromBuffer(buffer, alloc, client);
986 if (!buffer.validate(drawableOpSubmitter.has_value())) { return nullptr; }
987 return alloc->makeUnique<DrawableSubRun>(std::move(*drawableOpSubmitter));
990 bool DrawableSubRun::canReuse( const SkPaint& paint, const SkMatrix& positionMatrix) const {
994 const AtlasSubRun* DrawableSubRun::testingOnly_atlasSubRun() const {
999 using DevicePosition = skvx::Vec<2, int16_t>;
1009 std::tuple<ClipMethod, SkIRect>
1010 calculate_clip(const GrClip* clip, SkRect deviceBounds, SkRect glyphBounds) {
1011 if (clip == nullptr && !deviceBounds.intersects(glyphBounds)) {
1012 return {kClippedOut, SkIRect::MakeEmpty()};
1013 } else if (clip != nullptr) {
1014 switch (auto result = clip->preApply(glyphBounds, GrAA::kNo); result.fEffect) {
1015 case GrClip::Effect::kClippedOut:
1016 return {kClippedOut, SkIRect::MakeEmpty()};
1017 case GrClip::Effect::kUnclipped:
1018 return {kUnclipped, SkIRect::MakeEmpty()};
1019 case GrClip::Effect::kClipped: {
1020 if (result.fIsRRect && result.fRRect.isRect()) {
1021 SkRect r = result.fRRect.rect();
1022 if (result.fAA == GrAA::kNo || GrClip::IsPixelAligned(r)) {
1023 SkIRect clipRect = SkIRect::MakeEmpty();
1024 // Clip geometrically during onPrepare using clipRect.
1026 if (clipRect.contains(glyphBounds)) {
1027 // If fully within the clip, signal no clipping using the empty rect.
1028 return {kUnclipped, SkIRect::MakeEmpty()};
1030 // Use the clipRect to clip the geometry.
1031 return {kGeometryClipped, clipRect};
1033 // Partial pixel clipped at this point. Have the GPU handle it.
1039 return {kGPUClipped, SkIRect::MakeEmpty()};
1042 // The 99% case. No clip. Non-color only.
1043 void direct_2D(SkZip<Mask2DVertex[4],
1045 const DevicePosition> quadData,
1047 SkPoint originOffset) {
1048 for (auto[quad, glyph, leftTop] : quadData) {
1049 auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
1050 SkScalar dl = leftTop[0] + originOffset.x(),
1051 dt = leftTop[1] + originOffset.y(),
1052 dr = dl + (ar - al),
1053 db = dt + (ab - at);
1055 quad[0] = {{dl, dt}, color, {al, at}}; // L,T
1056 quad[1] = {{dl, db}, color, {al, ab}}; // L,B
1057 quad[2] = {{dr, dt}, color, {ar, at}}; // R,T
1058 quad[3] = {{dr, db}, color, {ar, ab}}; // R,B
1063 template <typename Rect>
1064 auto ltbr(const Rect& r) {
1065 return std::make_tuple(r.left(), r.top(), r.right(), r.bottom());
1068 // Handle any combination of BW or color and clip or no clip.
1069 template<typename Quad, typename VertexData>
1070 void generalized_direct_2D(SkZip<Quad, const Glyph*, const VertexData> quadData,
1072 SkPoint originOffset,
1073 SkIRect* clip = nullptr) {
1074 for (auto[quad, glyph, leftTop] : quadData) {
1075 auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
1076 uint16_t w = ar - al,
1078 SkScalar l = (SkScalar)leftTop[0] + originOffset.x(),
1079 t = (SkScalar)leftTop[1] + originOffset.y();
1080 if (clip == nullptr) {
1081 auto[dl, dt, dr, db] = SkRect::MakeLTRB(l, t, l + w, t + h);
1082 quad[0] = {{dl, dt}, color, {al, at}}; // L,T
1083 quad[1] = {{dl, db}, color, {al, ab}}; // L,B
1084 quad[2] = {{dr, dt}, color, {ar, at}}; // R,T
1085 quad[3] = {{dr, db}, color, {ar, ab}}; // R,B
1087 SkIRect devIRect = SkIRect::MakeLTRB(l, t, l + w, t + h);
1088 SkScalar dl, dt, dr, db;
1089 if (!clip->containsNoEmptyCheck(devIRect)) {
1090 if (SkIRect clipped; clipped.intersect(devIRect, *clip)) {
1091 al += clipped.left() - devIRect.left();
1092 at += clipped.top() - devIRect.top();
1093 ar += clipped.right() - devIRect.right();
1094 ab += clipped.bottom() - devIRect.bottom();
1095 std::tie(dl, dt, dr, db) = ltbr(clipped);
1097 // TODO: omit generating any vertex data for fully clipped glyphs ?
1098 std::tie(dl, dt, dr, db) = std::make_tuple(0, 0, 0, 0);
1099 std::tie(al, at, ar, ab) = std::make_tuple(0, 0, 0, 0);
1102 std::tie(dl, dt, dr, db) = ltbr(devIRect);
1104 quad[0] = {{dl, dt}, color, {al, at}}; // L,T
1105 quad[1] = {{dl, db}, color, {al, ab}}; // L,B
1106 quad[2] = {{dr, dt}, color, {ar, at}}; // R,T
1107 quad[3] = {{dr, db}, color, {ar, ab}}; // R,B
1110 #endif // SK_SUPPORT_GPU
1113 // -- DirectMaskSubRun -------------------------------------------------------------------------
1114 class DirectMaskSubRun final : public SubRun, public AtlasSubRun {
1116 DirectMaskSubRun(MaskFormat format,
1117 const SkMatrix& initialPositionMatrix,
1118 SkGlyphRect deviceBounds,
1119 SkSpan<const DevicePosition> devicePositions,
1120 GlyphVector&& glyphs,
1121 bool glyphsOutOfBounds);
1123 static SubRunOwner Make(const SkZip<SkGlyphVariant, SkPoint>& accepted,
1124 const SkMatrix& initialPositionMatrix,
1125 sk_sp<SkStrike>&& strike,
1127 SubRunAllocator* alloc);
1129 static SubRunOwner MakeFromBuffer(const SkMatrix& initialPositionMatrix,
1130 SkReadBuffer& buffer,
1131 SubRunAllocator* alloc,
1132 const SkStrikeClient* client);
1134 void draw(SkCanvas*,
1136 const SkMatrixProvider& viewMatrix,
1138 const SkPaint& paint,
1139 sk_sp<SkRefCnt> subRunOwner,
1140 skgpu::v1::SurfaceDrawContext* sdc) const override;
1143 int unflattenSize() const override;
1145 int glyphCount() const override;
1147 void testingOnly_packedGlyphIDToGlyph(StrikeCache *cache) const override;
1150 size_t vertexStride(const SkMatrix& drawMatrix) const override;
1152 std::tuple<const GrClip*, GrOp::Owner>
1153 makeAtlasTextOp(const GrClip*,
1154 const SkMatrixProvider& viewMatrix,
1157 sk_sp<SkRefCnt>&& subRunStorage,
1158 skgpu::v1::SurfaceDrawContext*) const override;
1160 std::tuple<bool, int>
1161 regenerateAtlas(int begin, int end, GrMeshDrawTarget*) const override;
1163 void fillVertexData(void* vertexDst, int offset, int count,
1165 const SkMatrix& drawMatrix, SkPoint drawOrigin,
1166 SkIRect clip) const override;
1169 bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const override;
1171 const AtlasSubRun* testingOnly_atlasSubRun() const override;
1174 SubRunType subRunType() const override { return kDirectMask; }
1175 void doFlatten(SkWriteBuffer& buffer) const override;
1178 // Return true if the positionMatrix represents an integer translation. Return the device
1179 // bounding box of all the glyphs. If the bounding box is empty, then something went singular
1180 // and this operation should be dropped.
1181 std::tuple<bool, SkRect> deviceRectAndCheckTransform(const SkMatrix& positionMatrix) const;
1183 const MaskFormat fMaskFormat;
1184 const SkMatrix& fInitialPositionMatrix;
1186 // The vertex bounds in device space. The bounds are the joined rectangles of all the glyphs.
1187 const SkGlyphRect fGlyphDeviceBounds;
1188 const SkSpan<const DevicePosition> fLeftTopDevicePos;
1189 const bool fSomeGlyphsExcluded;
1191 // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
1192 // be single threaded.
1193 mutable GlyphVector fGlyphs;
1196 DirectMaskSubRun::DirectMaskSubRun(MaskFormat format,
1197 const SkMatrix& initialPositionMatrix,
1198 SkGlyphRect deviceBounds,
1199 SkSpan<const DevicePosition> devicePositions,
1200 GlyphVector&& glyphs,
1201 bool glyphsOutOfBounds)
1202 : fMaskFormat{format}
1203 , fInitialPositionMatrix{initialPositionMatrix}
1204 , fGlyphDeviceBounds{deviceBounds}
1205 , fLeftTopDevicePos{devicePositions}
1206 , fSomeGlyphsExcluded{glyphsOutOfBounds}
1207 , fGlyphs{std::move(glyphs)} { }
1209 SubRunOwner DirectMaskSubRun::Make(const SkZip<SkGlyphVariant, SkPoint>& accepted,
1210 const SkMatrix& initialPositionMatrix,
1211 sk_sp<SkStrike>&& strike,
1213 SubRunAllocator* alloc) {
1214 auto glyphLeftTop = alloc->makePODArray<DevicePosition>(accepted.size());
1215 auto glyphIDs = alloc->makePODArray<GlyphVector::Variant>(accepted.size());
1217 // Because this is the direct case, the maximum width or height is the size that fits in the
1218 // atlas. This boundary is checked below to ensure that the call to SkGlyphRect below will
1220 constexpr SkScalar kMaxPos =
1221 std::numeric_limits<int16_t>::max() - SkStrikeCommon::kSkSideTooBigForAtlas;
1222 SkGlyphRect runBounds = skglyph::empty_rect();
1223 size_t goodPosCount = 0;
1224 for (auto [variant, pos] : accepted) {
1226 // Ensure that the .offset() call below does not overflow. And, at this point none of the
1227 // rectangles are empty because they were culled before the run was created. Basically,
1228 // cull all the glyphs that can't appear on the screen.
1229 if (-kMaxPos < x && x < kMaxPos && -kMaxPos < y && y < kMaxPos) {
1230 const SkGlyph* const skGlyph = variant;
1231 const SkGlyphRect deviceBounds =
1232 skGlyph->glyphRect().offset(SkScalarRoundToInt(x), SkScalarRoundToInt(y));
1233 runBounds = skglyph::rect_union(runBounds, deviceBounds);
1234 glyphLeftTop[goodPosCount] = deviceBounds.leftTop();
1235 glyphIDs[goodPosCount].packedGlyphID = skGlyph->getPackedID();
1240 // Wow! no glyphs are in bounds and had non-empty bounds.
1241 if (goodPosCount == 0) {
1245 // If some glyphs were excluded by the bounds, then this subrun can't be generally be used
1246 // for other draws. Mark the subrun as not general.
1247 bool glyphsExcluded = goodPosCount != accepted.size();
1248 SkSpan<const DevicePosition> leftTop{glyphLeftTop, goodPosCount};
1249 return alloc->makeUnique<DirectMaskSubRun>(
1250 format, initialPositionMatrix, runBounds, leftTop,
1251 GlyphVector{std::move(strike), {glyphIDs, goodPosCount}},
1255 bool DirectMaskSubRun::canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const {
1256 auto [reuse, translation] =
1257 can_use_direct(fInitialPositionMatrix, positionMatrix);
1259 // If glyphs were excluded because of position bounds, then this subrun can only be reused if
1260 // there is no change in position.
1261 if (fSomeGlyphsExcluded) {
1262 return translation.x() == 0 && translation.y() == 0;
1268 SubRunOwner DirectMaskSubRun::MakeFromBuffer(const SkMatrix& initialPositionMatrix,
1269 SkReadBuffer& buffer,
1270 SubRunAllocator* alloc,
1271 const SkStrikeClient* client) {
1272 MaskFormat maskType = (MaskFormat)buffer.readInt();
1273 SkGlyphRect runBounds;
1274 pun_read(buffer, &runBounds);
1276 int glyphCount = buffer.readInt();
1277 if (!buffer.validate(check_glyph_count(buffer, glyphCount))) { return nullptr; }
1278 if (!buffer.validateCanReadN<DevicePosition>(glyphCount)) { return nullptr; }
1279 DevicePosition* positionsData = alloc->makePODArray<DevicePosition>(glyphCount);
1280 for (int i = 0; i < glyphCount; ++i) {
1281 pun_read(buffer, &positionsData[i]);
1283 SkSpan<DevicePosition> positions(positionsData, glyphCount);
1285 auto glyphVector = GlyphVector::MakeFromBuffer(buffer, client, alloc);
1286 if (!buffer.validate(glyphVector.has_value())) { return nullptr; }
1287 if (!buffer.validate(SkCount(glyphVector->glyphs()) == glyphCount)) { return nullptr; }
1288 SkASSERT(buffer.isValid());
1289 return alloc->makeUnique<DirectMaskSubRun>(
1290 maskType, initialPositionMatrix, runBounds, positions,
1291 std::move(glyphVector.value()), false);
1294 void DirectMaskSubRun::doFlatten(SkWriteBuffer& buffer) const {
1295 buffer.writeInt(static_cast<int>(fMaskFormat));
1296 pun_write(buffer, fGlyphDeviceBounds);
1297 int glyphCount = SkTo<int>(fLeftTopDevicePos.size());
1298 buffer.writeInt(glyphCount);
1299 for (auto pos : fLeftTopDevicePos) {
1300 pun_write(buffer, pos);
1302 fGlyphs.flatten(buffer);
1305 int DirectMaskSubRun::unflattenSize() const {
1306 return sizeof(DirectMaskSubRun) +
1307 fGlyphs.unflattenSize() +
1308 sizeof(DevicePosition) * fGlyphs.glyphs().size();
1311 const AtlasSubRun* DirectMaskSubRun::testingOnly_atlasSubRun() const {
1315 int DirectMaskSubRun::glyphCount() const {
1316 return SkCount(fGlyphs.glyphs());
1320 size_t DirectMaskSubRun::vertexStride(const SkMatrix& positionMatrix) const {
1321 if (!positionMatrix.hasPerspective()) {
1322 if (fMaskFormat != MaskFormat::kARGB) {
1323 return sizeof(Mask2DVertex);
1325 return sizeof(ARGB2DVertex);
1328 if (fMaskFormat != MaskFormat::kARGB) {
1329 return sizeof(Mask3DVertex);
1331 return sizeof(ARGB3DVertex);
1336 void DirectMaskSubRun::draw(SkCanvas*,
1338 const SkMatrixProvider& viewMatrix,
1340 const SkPaint& paint,
1341 sk_sp<SkRefCnt> subRunStorage,
1342 skgpu::v1::SurfaceDrawContext* sdc) const {
1343 auto[drawingClip, op] = this->makeAtlasTextOp(
1344 clip, viewMatrix, drawOrigin, paint, std::move(subRunStorage), sdc);
1345 if (op != nullptr) {
1346 sdc->addDrawOp(drawingClip, std::move(op));
1350 std::tuple<const GrClip*, GrOp::Owner> DirectMaskSubRun::makeAtlasTextOp(
1352 const SkMatrixProvider& viewMatrix,
1354 const SkPaint& paint,
1355 sk_sp<SkRefCnt>&& subRunStorage,
1356 skgpu::v1::SurfaceDrawContext* sdc) const {
1357 SkASSERT(this->glyphCount() != 0);
1358 const SkMatrix& drawMatrix = viewMatrix.localToDevice();
1359 const SkMatrix& positionMatrix = position_matrix(drawMatrix, drawOrigin);
1361 auto [integerTranslate, subRunDeviceBounds] = this->deviceRectAndCheckTransform(positionMatrix);
1362 if (subRunDeviceBounds.isEmpty()) {
1363 return {nullptr, nullptr};
1365 // Rect for optimized bounds clipping when doing an integer translate.
1366 SkIRect geometricClipRect = SkIRect::MakeEmpty();
1367 if (integerTranslate) {
1368 // We can clip geometrically using clipRect and ignore clip when an axis-aligned rectangular
1369 // non-AA clip is used. If clipRect is empty, and clip is nullptr, then there is no clipping
1371 const SkRect deviceBounds = SkRect::MakeWH(sdc->width(), sdc->height());
1372 auto [clipMethod, clipRect] = calculate_clip(clip, deviceBounds, subRunDeviceBounds);
1374 switch (clipMethod) {
1376 // Returning nullptr as op means skip this op.
1377 return {nullptr, nullptr};
1379 case kGeometryClipped:
1380 // GPU clip is not needed.
1384 // Use th GPU clip; clipRect is ignored.
1387 geometricClipRect = clipRect;
1389 if (!geometricClipRect.isEmpty()) { SkASSERT(clip == nullptr); }
1393 const SkPMColor4f drawingColor =
1394 calculate_colors(sdc, paint, viewMatrix, fMaskFormat, &grPaint);
1396 auto geometry = AtlasTextOp::Geometry::Make(*this,
1400 std::move(subRunStorage),
1404 GrRecordingContext* const rContext = sdc->recordingContext();
1405 GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext,
1406 op_mask_type(fMaskFormat),
1411 std::move(grPaint));
1412 return {clip, std::move(op)};
1416 void DirectMaskSubRun::testingOnly_packedGlyphIDToGlyph(StrikeCache *cache) const {
1417 fGlyphs.packedGlyphIDToGlyph(cache);
1421 std::tuple<bool, int> DirectMaskSubRun::regenerateAtlas(int begin, int end,
1422 GrMeshDrawTarget* target) const {
1423 return fGlyphs.regenerateAtlas(begin, end, fMaskFormat, 0, target);
1426 template<typename Quad, typename VertexData>
1427 void transformed_direct_2D(SkZip<Quad, const Glyph*, const VertexData> quadData,
1429 const SkMatrix& matrix) {
1430 for (auto[quad, glyph, leftTop] : quadData) {
1431 auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
1432 SkScalar dl = leftTop[0],
1434 dr = dl + (ar - al),
1435 db = dt + (ab - at);
1436 SkPoint lt = matrix.mapXY(dl, dt),
1437 lb = matrix.mapXY(dl, db),
1438 rt = matrix.mapXY(dr, dt),
1439 rb = matrix.mapXY(dr, db);
1440 quad[0] = {lt, color, {al, at}}; // L,T
1441 quad[1] = {lb, color, {al, ab}}; // L,B
1442 quad[2] = {rt, color, {ar, at}}; // R,T
1443 quad[3] = {rb, color, {ar, ab}}; // R,B
1447 template<typename Quad, typename VertexData>
1448 void transformed_direct_3D(SkZip<Quad, const Glyph*, const VertexData> quadData,
1450 const SkMatrix& matrix) {
1451 auto mapXYZ = [&](SkScalar x, SkScalar y) {
1454 matrix.mapHomogeneousPoints(&result, &pt, 1);
1457 for (auto[quad, glyph, leftTop] : quadData) {
1458 auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
1459 SkScalar dl = leftTop[0],
1461 dr = dl + (ar - al),
1462 db = dt + (ab - at);
1463 SkPoint3 lt = mapXYZ(dl, dt),
1464 lb = mapXYZ(dl, db),
1465 rt = mapXYZ(dr, dt),
1466 rb = mapXYZ(dr, db);
1467 quad[0] = {lt, color, {al, at}}; // L,T
1468 quad[1] = {lb, color, {al, ab}}; // L,B
1469 quad[2] = {rt, color, {ar, at}}; // R,T
1470 quad[3] = {rb, color, {ar, ab}}; // R,B
1474 void DirectMaskSubRun::fillVertexData(void* vertexDst, int offset, int count,
1476 const SkMatrix& drawMatrix, SkPoint drawOrigin,
1477 SkIRect clip) const {
1478 auto quadData = [&](auto dst) {
1479 return SkMakeZip(dst,
1480 fGlyphs.glyphs().subspan(offset, count),
1481 fLeftTopDevicePos.subspan(offset, count));
1484 const SkMatrix positionMatrix = position_matrix(drawMatrix, drawOrigin);
1485 auto [noTransformNeeded, originOffset] =
1486 can_use_direct(fInitialPositionMatrix, positionMatrix);
1488 if (noTransformNeeded) {
1489 if (clip.isEmpty()) {
1490 if (fMaskFormat != MaskFormat::kARGB) {
1491 using Quad = Mask2DVertex[4];
1492 SkASSERT(sizeof(Mask2DVertex) == this->vertexStride(SkMatrix::I()));
1493 direct_2D(quadData((Quad*)vertexDst), color, originOffset);
1495 using Quad = ARGB2DVertex[4];
1496 SkASSERT(sizeof(ARGB2DVertex) == this->vertexStride(SkMatrix::I()));
1497 generalized_direct_2D(quadData((Quad*)vertexDst), color, originOffset);
1500 if (fMaskFormat != MaskFormat::kARGB) {
1501 using Quad = Mask2DVertex[4];
1502 SkASSERT(sizeof(Mask2DVertex) == this->vertexStride(SkMatrix::I()));
1503 generalized_direct_2D(quadData((Quad*)vertexDst), color, originOffset, &clip);
1505 using Quad = ARGB2DVertex[4];
1506 SkASSERT(sizeof(ARGB2DVertex) == this->vertexStride(SkMatrix::I()));
1507 generalized_direct_2D(quadData((Quad*)vertexDst), color, originOffset, &clip);
1510 } else if (SkMatrix inverse; fInitialPositionMatrix.invert(&inverse)) {
1511 SkMatrix viewDifference = SkMatrix::Concat(positionMatrix, inverse);
1512 if (!viewDifference.hasPerspective()) {
1513 if (fMaskFormat != MaskFormat::kARGB) {
1514 using Quad = Mask2DVertex[4];
1515 SkASSERT(sizeof(Mask2DVertex) == this->vertexStride(positionMatrix));
1516 transformed_direct_2D(quadData((Quad*)vertexDst), color, viewDifference);
1518 using Quad = ARGB2DVertex[4];
1519 SkASSERT(sizeof(ARGB2DVertex) == this->vertexStride(positionMatrix));
1520 transformed_direct_2D(quadData((Quad*)vertexDst), color, viewDifference);
1523 if (fMaskFormat != MaskFormat::kARGB) {
1524 using Quad = Mask3DVertex[4];
1525 SkASSERT(sizeof(Mask3DVertex) == this->vertexStride(positionMatrix));
1526 transformed_direct_3D(quadData((Quad*)vertexDst), color, viewDifference);
1528 using Quad = ARGB3DVertex[4];
1529 SkASSERT(sizeof(ARGB3DVertex) == this->vertexStride(positionMatrix));
1530 transformed_direct_3D(quadData((Quad*)vertexDst), color, viewDifference);
1537 // true if only need to translate by integer amount, device rect.
1538 std::tuple<bool, SkRect> DirectMaskSubRun::deviceRectAndCheckTransform(
1539 const SkMatrix& positionMatrix) const {
1540 const SkMatrix& initialMatrix = fInitialPositionMatrix;
1541 const SkPoint offset = positionMatrix.mapOrigin() - initialMatrix.mapOrigin();
1543 const bool compatibleMatrix = positionMatrix[0] == initialMatrix[0] &&
1544 positionMatrix[1] == initialMatrix[1] &&
1545 positionMatrix[3] == initialMatrix[3] &&
1546 positionMatrix[4] == initialMatrix[4] &&
1547 !positionMatrix.hasPerspective() &&
1548 !initialMatrix.hasPerspective();
1550 if (compatibleMatrix && SkScalarIsInt(offset.x()) && SkScalarIsInt(offset.y())) {
1551 // Handle the integer offset case.
1552 // The offset should be integer, but make sure.
1553 SkIVector iOffset = {SkScalarRoundToInt(offset.x()), SkScalarRoundToInt(offset.y())};
1555 SkIRect outBounds = fGlyphDeviceBounds.iRect();
1556 return {true, SkRect::Make(outBounds.makeOffset(iOffset))};
1557 } else if (SkMatrix inverse; fInitialPositionMatrix.invert(&inverse)) {
1558 SkMatrix viewDifference = SkMatrix::Concat(positionMatrix, inverse);
1559 return {false, viewDifference.mapRect(fGlyphDeviceBounds.rect())};
1562 // initialPositionMatrix is singular. Do nothing.
1563 return {false, SkRect::MakeEmpty()};
1566 // -- TransformedMaskSubRun ------------------------------------------------------------------------
1567 class TransformedMaskSubRun final : public SubRun, public AtlasSubRun {
1569 TransformedMaskSubRun(const SkMatrix& initialPositionMatrix,
1570 TransformedMaskVertexFiller&& vertexFiller,
1571 GlyphVector&& glyphs);
1573 static SubRunOwner Make(const SkZip<SkGlyphVariant, SkPoint>& accepted,
1574 const SkMatrix& initialPositionMatrix,
1575 sk_sp<SkStrike>&& strike,
1576 SkScalar strikeToSourceScale,
1577 MaskFormat maskType,
1578 SubRunAllocator* alloc);
1580 static SubRunOwner MakeFromBuffer(const SkMatrix& initialPositionMatrix,
1581 SkReadBuffer& buffer,
1582 SubRunAllocator* alloc,
1583 const SkStrikeClient* client);
1585 void draw(SkCanvas*,
1587 const SkMatrixProvider& viewMatrix,
1589 const SkPaint& paint,
1590 sk_sp<SkRefCnt> subRunStorage,
1591 skgpu::v1::SurfaceDrawContext*) const override;
1593 std::tuple<const GrClip*, GrOp::Owner>
1594 makeAtlasTextOp(const GrClip*,
1595 const SkMatrixProvider& viewMatrix,
1598 sk_sp<SkRefCnt>&& subRunStorage,
1599 skgpu::v1::SurfaceDrawContext*) const override;
1602 int unflattenSize() const override;
1604 bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const override;
1606 const AtlasSubRun* testingOnly_atlasSubRun() const override;
1608 void testingOnly_packedGlyphIDToGlyph(StrikeCache *cache) const override;
1611 std::tuple<bool, int> regenerateAtlas(int begin, int end, GrMeshDrawTarget*) const override;
1613 void fillVertexData(
1614 void* vertexDst, int offset, int count,
1616 const SkMatrix& drawMatrix, SkPoint drawOrigin,
1617 SkIRect clip) const override;
1619 size_t vertexStride(const SkMatrix& drawMatrix) const override;
1622 int glyphCount() const override;
1625 SubRunType subRunType() const override { return kTransformMask; }
1626 void doFlatten(SkWriteBuffer& buffer) const override;
1629 // The rectangle that surrounds all the glyph bounding boxes in device space.
1630 SkRect deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const;
1632 const SkMatrix& fInitialPositionMatrix;
1634 const TransformedMaskVertexFiller fVertexFiller;
1636 // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
1637 // be single threaded.
1638 mutable GlyphVector fGlyphs;
1641 TransformedMaskSubRun::TransformedMaskSubRun(const SkMatrix& initialPositionMatrix,
1642 TransformedMaskVertexFiller&& vertexFiller,
1643 GlyphVector&& glyphs)
1644 : fInitialPositionMatrix{initialPositionMatrix}
1645 , fVertexFiller{std::move(vertexFiller)}
1646 , fGlyphs{std::move(glyphs)} { }
1648 SubRunOwner TransformedMaskSubRun::Make(const SkZip<SkGlyphVariant, SkPoint>& accepted,
1649 const SkMatrix& initialPositionMatrix,
1650 sk_sp<SkStrike>&& strike,
1651 SkScalar strikeToSourceScale,
1652 MaskFormat maskType,
1653 SubRunAllocator* alloc) {
1654 auto vertexFiller = TransformedMaskVertexFiller::Make(
1655 maskType, 0, strikeToSourceScale, accepted, alloc);
1657 auto glyphVector = GlyphVector::Make(std::move(strike), accepted.get<0>(), alloc);
1659 return alloc->makeUnique<TransformedMaskSubRun>(
1660 initialPositionMatrix, std::move(vertexFiller), std::move(glyphVector));
1663 SubRunOwner TransformedMaskSubRun::MakeFromBuffer(const SkMatrix& initialPositionMatrix,
1664 SkReadBuffer& buffer,
1665 SubRunAllocator* alloc,
1666 const SkStrikeClient* client) {
1667 auto vertexFiller = TransformedMaskVertexFiller::MakeFromBuffer(buffer, alloc);
1668 if (!buffer.validate(vertexFiller.has_value())) { return {}; }
1670 auto glyphVector = GlyphVector::MakeFromBuffer(buffer, client, alloc);
1671 if (!buffer.validate(glyphVector.has_value())) { return {}; }
1672 if (!buffer.validate(SkCount(glyphVector->glyphs()) == vertexFiller->count())) { return {}; }
1673 return alloc->makeUnique<TransformedMaskSubRun>(
1674 initialPositionMatrix, std::move(*vertexFiller), std::move(*glyphVector));
1677 int TransformedMaskSubRun::unflattenSize() const {
1678 return sizeof(TransformedMaskSubRun) + fGlyphs.unflattenSize() + fVertexFiller.unflattenSize();
1681 void TransformedMaskSubRun::doFlatten(SkWriteBuffer& buffer) const {
1682 fVertexFiller.flatten(buffer);
1683 fGlyphs.flatten(buffer);
1687 void TransformedMaskSubRun::draw(SkCanvas*,
1689 const SkMatrixProvider& viewMatrix,
1691 const SkPaint& paint,
1692 sk_sp<SkRefCnt> subRunStorage,
1693 skgpu::v1::SurfaceDrawContext* sdc) const {
1694 auto[drawingClip, op] = this->makeAtlasTextOp(
1695 clip, viewMatrix, drawOrigin, paint, std::move(subRunStorage), sdc);
1696 if (op != nullptr) {
1697 sdc->addDrawOp(drawingClip, std::move(op));
1701 std::tuple<const GrClip*, GrOp::Owner>
1702 TransformedMaskSubRun::makeAtlasTextOp(const GrClip* clip,
1703 const SkMatrixProvider& viewMatrix,
1705 const SkPaint& paint,
1706 sk_sp<SkRefCnt>&& subRunStorage,
1707 skgpu::v1::SurfaceDrawContext* sdc) const {
1708 SkASSERT(this->glyphCount() != 0);
1710 const SkMatrix& drawMatrix = viewMatrix.localToDevice();
1713 SkPMColor4f drawingColor = calculate_colors(
1714 sdc, paint, viewMatrix, fVertexFiller.grMaskType(), &grPaint);
1716 auto geometry = AtlasTextOp::Geometry::Make(*this,
1719 SkIRect::MakeEmpty(),
1720 std::move(subRunStorage),
1724 GrRecordingContext* const rContext = sdc->recordingContext();
1725 GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext,
1726 fVertexFiller.opMaskType(),
1729 this->deviceRect(drawMatrix, drawOrigin),
1731 std::move(grPaint));
1732 return {clip, std::move(op)};
1736 // If we are not scaling the cache entry to be larger, than a cache with smaller glyphs may be
1738 bool TransformedMaskSubRun::canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const {
1739 if (fInitialPositionMatrix.getMaxScale() < 1) {
1745 void TransformedMaskSubRun::testingOnly_packedGlyphIDToGlyph(StrikeCache *cache) const {
1746 fGlyphs.packedGlyphIDToGlyph(cache);
1750 std::tuple<bool, int> TransformedMaskSubRun::regenerateAtlas(int begin, int end,
1751 GrMeshDrawTarget* target) const {
1752 return fGlyphs.regenerateAtlas(begin, end, fVertexFiller.grMaskType(), 1, target);
1755 void TransformedMaskSubRun::fillVertexData(void* vertexDst, int offset, int count,
1757 const SkMatrix& drawMatrix, SkPoint drawOrigin,
1758 SkIRect clip) const {
1759 const SkMatrix positionMatrix = position_matrix(drawMatrix, drawOrigin);
1760 fVertexFiller.fillVertexData(offset, count,
1768 size_t TransformedMaskSubRun::vertexStride(const SkMatrix& drawMatrix) const {
1769 return fVertexFiller.vertexStride(drawMatrix);
1773 int TransformedMaskSubRun::glyphCount() const {
1774 return SkCount(fGlyphs.glyphs());
1777 SkRect TransformedMaskSubRun::deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const {
1778 return fVertexFiller.deviceRect(drawMatrix, drawOrigin);
1781 const AtlasSubRun* TransformedMaskSubRun::testingOnly_atlasSubRun() const {
1785 // -- SDFTSubRun -----------------------------------------------------------------------------------
1786 class SDFTSubRun final : public SubRun, public AtlasSubRun {
1788 SDFTSubRun(bool useLCDText,
1790 const SDFTMatrixRange& matrixRange,
1791 TransformedMaskVertexFiller&& vertexFiller,
1792 GlyphVector&& glyphs);
1794 static SubRunOwner Make(const SkZip<SkGlyphVariant, SkPoint>& accepted,
1795 const SkFont& runFont,
1796 sk_sp<SkStrike>&& strike,
1797 SkScalar strikeToSourceScale,
1798 const SDFTMatrixRange& matrixRange,
1799 SubRunAllocator* alloc);
1801 static SubRunOwner MakeFromBuffer(const SkMatrix& initialPositionMatrix,
1802 SkReadBuffer& buffer,
1803 SubRunAllocator* alloc,
1804 const SkStrikeClient* client);
1806 void draw(SkCanvas*,
1808 const SkMatrixProvider& viewMatrix,
1811 sk_sp<SkRefCnt> subRunStorage,
1812 skgpu::v1::SurfaceDrawContext*) const override;
1814 std::tuple<const GrClip*, GrOp::Owner>
1815 makeAtlasTextOp(const GrClip*,
1816 const SkMatrixProvider& viewMatrix,
1819 sk_sp<SkRefCnt>&& subRunStorage,
1820 skgpu::v1::SurfaceDrawContext*) const override;
1823 int unflattenSize() const override;
1825 bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const override;
1827 const AtlasSubRun* testingOnly_atlasSubRun() const override;
1829 void testingOnly_packedGlyphIDToGlyph(StrikeCache *cache) const override;
1832 std::tuple<bool, int> regenerateAtlas(int begin, int end, GrMeshDrawTarget*) const override;
1834 void fillVertexData(
1835 void* vertexDst, int offset, int count,
1837 const SkMatrix& drawMatrix, SkPoint drawOrigin,
1838 SkIRect clip) const override;
1840 size_t vertexStride(const SkMatrix& drawMatrix) const override;
1843 int glyphCount() const override;
1846 SubRunType subRunType() const override { return kSDFT; }
1847 void doFlatten(SkWriteBuffer& buffer) const override;
1850 // The rectangle that surrounds all the glyph bounding boxes in device space.
1851 SkRect deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const;
1853 const bool fUseLCDText;
1854 const bool fAntiAliased;
1855 const sktext::gpu::SDFTMatrixRange fMatrixRange;
1857 const TransformedMaskVertexFiller fVertexFiller;
1859 // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
1860 // be single threaded.
1861 mutable GlyphVector fGlyphs;
1864 SDFTSubRun::SDFTSubRun(bool useLCDText,
1866 const SDFTMatrixRange& matrixRange,
1867 TransformedMaskVertexFiller&& vertexFiller,
1868 GlyphVector&& glyphs)
1869 : fUseLCDText{useLCDText}
1870 , fAntiAliased{antiAliased}
1871 , fMatrixRange{matrixRange}
1872 , fVertexFiller{std::move(vertexFiller)}
1873 , fGlyphs{std::move(glyphs)} { }
1875 bool has_some_antialiasing(const SkFont& font ) {
1876 SkFont::Edging edging = font.getEdging();
1877 return edging == SkFont::Edging::kAntiAlias
1878 || edging == SkFont::Edging::kSubpixelAntiAlias;
1881 SubRunOwner SDFTSubRun::Make(const SkZip<SkGlyphVariant, SkPoint>& accepted,
1882 const SkFont& runFont,
1883 sk_sp<SkStrike>&& strike,
1884 SkScalar strikeToSourceScale,
1885 const SDFTMatrixRange& matrixRange,
1886 SubRunAllocator* alloc) {
1887 auto vertexFiller = TransformedMaskVertexFiller::Make(
1889 SK_DistanceFieldInset,
1890 strikeToSourceScale,
1894 auto glyphVector = GlyphVector::Make(std::move(strike), accepted.get<0>(), alloc);
1896 return alloc->makeUnique<SDFTSubRun>(
1897 runFont.getEdging() == SkFont::Edging::kSubpixelAntiAlias,
1898 has_some_antialiasing(runFont),
1900 std::move(vertexFiller),
1901 std::move(glyphVector));
1904 SubRunOwner SDFTSubRun::MakeFromBuffer(const SkMatrix&,
1905 SkReadBuffer& buffer,
1906 SubRunAllocator* alloc,
1907 const SkStrikeClient* client) {
1908 int useLCD = buffer.readInt();
1909 int isAntiAliased = buffer.readInt();
1910 SDFTMatrixRange matrixRange = SDFTMatrixRange::MakeFromBuffer(buffer);
1911 auto vertexFiller = TransformedMaskVertexFiller::MakeFromBuffer(buffer, alloc);
1912 if (!buffer.validate(vertexFiller.has_value())) { return {}; }
1913 auto glyphVector = GlyphVector::MakeFromBuffer(buffer, client, alloc);
1914 if (!buffer.validate(glyphVector.has_value())) { return {}; }
1915 if (!buffer.validate(SkCount(glyphVector->glyphs()) == vertexFiller->count())) { return {}; }
1916 return alloc->makeUnique<SDFTSubRun>(useLCD,
1919 std::move(*vertexFiller),
1920 std::move(*glyphVector));
1923 int SDFTSubRun::unflattenSize() const {
1924 return sizeof(SDFTSubRun) + fGlyphs.unflattenSize() + fVertexFiller.unflattenSize();
1927 void SDFTSubRun::doFlatten(SkWriteBuffer& buffer) const {
1928 buffer.writeInt(fUseLCDText);
1929 buffer.writeInt(fAntiAliased);
1930 fMatrixRange.flatten(buffer);
1931 fVertexFiller.flatten(buffer);
1932 fGlyphs.flatten(buffer);
1936 void SDFTSubRun::draw(SkCanvas*,
1938 const SkMatrixProvider& viewMatrix,
1940 const SkPaint& paint,
1941 sk_sp<SkRefCnt> subRunStorage,
1942 skgpu::v1::SurfaceDrawContext* sdc) const {
1943 auto[drawingClip, op] = this->makeAtlasTextOp(
1944 clip, viewMatrix, drawOrigin, paint, std::move(subRunStorage), sdc);
1945 if (op != nullptr) {
1946 sdc->addDrawOp(drawingClip, std::move(op));
1950 static std::tuple<AtlasTextOp::MaskType, uint32_t, bool> calculate_sdf_parameters(
1951 const skgpu::v1::SurfaceDrawContext& sdc,
1952 const SkMatrix& drawMatrix,
1954 bool isAntiAliased) {
1955 const GrColorInfo& colorInfo = sdc.colorInfo();
1956 const SkSurfaceProps& props = sdc.surfaceProps();
1957 bool isBGR = SkPixelGeometryIsBGR(props.pixelGeometry());
1958 bool isLCD = useLCDText && SkPixelGeometryIsH(props.pixelGeometry());
1959 using MT = AtlasTextOp::MaskType;
1960 MT maskType = !isAntiAliased ? MT::kAliasedDistanceField
1961 : isLCD ? (isBGR ? MT::kLCDBGRDistanceField
1962 : MT::kLCDDistanceField)
1963 : MT::kGrayscaleDistanceField;
1965 bool useGammaCorrectDistanceTable = colorInfo.isLinearlyBlended();
1966 uint32_t DFGPFlags = drawMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
1967 DFGPFlags |= drawMatrix.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0;
1968 DFGPFlags |= useGammaCorrectDistanceTable ? kGammaCorrect_DistanceFieldEffectFlag : 0;
1969 DFGPFlags |= MT::kAliasedDistanceField == maskType ? kAliased_DistanceFieldEffectFlag : 0;
1972 DFGPFlags |= kUseLCD_DistanceFieldEffectFlag;
1973 DFGPFlags |= MT::kLCDBGRDistanceField == maskType ? kBGR_DistanceFieldEffectFlag : 0;
1975 return {maskType, DFGPFlags, useGammaCorrectDistanceTable};
1978 std::tuple<const GrClip*, GrOp::Owner >
1979 SDFTSubRun::makeAtlasTextOp(const GrClip* clip,
1980 const SkMatrixProvider& viewMatrix,
1982 const SkPaint& paint,
1983 sk_sp<SkRefCnt>&& subRunStorage,
1984 skgpu::v1::SurfaceDrawContext* sdc) const {
1985 SkASSERT(this->glyphCount() != 0);
1987 const SkMatrix& drawMatrix = viewMatrix.localToDevice();
1990 SkPMColor4f drawingColor = calculate_colors(sdc, paint, viewMatrix, MaskFormat::kA8,
1993 auto [maskType, DFGPFlags, useGammaCorrectDistanceTable] =
1994 calculate_sdf_parameters(*sdc, drawMatrix, fUseLCDText, fAntiAliased);
1996 auto geometry = AtlasTextOp::Geometry::Make(*this,
1999 SkIRect::MakeEmpty(),
2000 std::move(subRunStorage),
2004 GrRecordingContext* const rContext = sdc->recordingContext();
2005 GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext,
2009 this->deviceRect(drawMatrix, drawOrigin),
2010 SkPaintPriv::ComputeLuminanceColor(paint),
2011 useGammaCorrectDistanceTable,
2014 std::move(grPaint));
2016 return {clip, std::move(op)};
2020 bool SDFTSubRun::canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const {
2021 return fMatrixRange.matrixInRange(positionMatrix);
2024 void SDFTSubRun::testingOnly_packedGlyphIDToGlyph(StrikeCache *cache) const {
2025 fGlyphs.packedGlyphIDToGlyph(cache);
2029 std::tuple<bool, int>
2030 SDFTSubRun::regenerateAtlas(int begin, int end, GrMeshDrawTarget *target) const {
2031 return fGlyphs.regenerateAtlas(begin, end, MaskFormat::kA8, SK_DistanceFieldInset,
2035 size_t SDFTSubRun::vertexStride(const SkMatrix& drawMatrix) const {
2036 return sizeof(Mask2DVertex);
2039 void SDFTSubRun::fillVertexData(
2040 void *vertexDst, int offset, int count,
2042 const SkMatrix& drawMatrix, SkPoint drawOrigin,
2043 SkIRect clip) const {
2044 const SkMatrix positionMatrix = position_matrix(drawMatrix, drawOrigin);
2046 fVertexFiller.fillVertexData(offset, count,
2055 int SDFTSubRun::glyphCount() const {
2056 return fVertexFiller.count();
2059 SkRect SDFTSubRun::deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const {
2060 return fVertexFiller.deviceRect(drawMatrix, drawOrigin);
2063 const AtlasSubRun* SDFTSubRun::testingOnly_atlasSubRun() const {
2067 template<typename AddSingleMaskFormat>
2068 void add_multi_mask_format(
2069 AddSingleMaskFormat addSingleMaskFormat,
2070 const SkZip<SkGlyphVariant, SkPoint>& accepted,
2071 sk_sp<SkStrike>&& strike) {
2072 if (accepted.empty()) { return; }
2074 auto glyphSpan = accepted.get<0>();
2075 const SkGlyph* glyph = glyphSpan[0];
2076 MaskFormat format = Glyph::FormatFromSkGlyph(glyph->maskFormat());
2077 size_t startIndex = 0;
2078 for (size_t i = 1; i < accepted.size(); i++) {
2079 glyph = glyphSpan[i];
2080 MaskFormat nextFormat = Glyph::FormatFromSkGlyph(glyph->maskFormat());
2081 if (format != nextFormat) {
2082 auto glyphsWithSameFormat = accepted.subspan(startIndex, i - startIndex);
2083 // Take a ref on the strike. This should rarely happen.
2084 addSingleMaskFormat(glyphsWithSameFormat, format, sk_sp<SkStrike>(strike));
2085 format = nextFormat;
2089 auto glyphsWithSameFormat = accepted.last(accepted.size() - startIndex);
2090 addSingleMaskFormat(glyphsWithSameFormat, format, std::move(strike));
2095 // -- TextBlob::Key ------------------------------------------------------------------------------
2097 static SkColor compute_canonical_color(const SkPaint& paint, bool lcd) {
2098 SkColor canonicalColor = SkPaintPriv::ComputeLuminanceColor(paint);
2100 // This is the correct computation for canonicalColor, but there are tons of cases where LCD
2101 // can be modified. For now we just regenerate if any run in a textblob has LCD.
2102 // TODO figure out where all of these modifications are and see if we can incorporate that
2103 // logic at a higher level *OR* use sRGB
2104 //canonicalColor = SkMaskGamma::CanonicalColor(canonicalColor);
2106 // TODO we want to figure out a way to be able to use the canonical color on LCD text,
2107 // see the note above. We pick a placeholder value for LCD text to ensure we always match
2109 return SK_ColorTRANSPARENT;
2111 // A8, though can have mixed BMP text but it shouldn't matter because BMP text won't have
2112 // gamma corrected masks anyways, nor color
2113 U8CPU lum = SkComputeLuminance(SkColorGetR(canonicalColor),
2114 SkColorGetG(canonicalColor),
2115 SkColorGetB(canonicalColor));
2116 // reduce to our finite number of bits
2117 canonicalColor = SkMaskGamma::CanonicalColor(SkColorSetRGB(lum, lum, lum));
2119 return canonicalColor;
2122 auto TextBlob::Key::Make(const SkGlyphRunList& glyphRunList,
2123 const SkPaint& paint,
2124 const SkMatrix& drawMatrix,
2125 const SkStrikeDeviceInfo& strikeDevice) -> std::tuple<bool, Key> {
2126 SkASSERT(strikeDevice.fSDFTControl != nullptr);
2127 SkMaskFilterBase::BlurRec blurRec;
2128 // It might be worth caching these things, but its not clear at this time
2129 // TODO for animated mask filters, this will fill up our cache. We need a safeguard here
2130 const SkMaskFilter* maskFilter = paint.getMaskFilter();
2131 bool canCache = glyphRunList.canCache() &&
2132 !(paint.getPathEffect() ||
2133 (maskFilter && !as_MFB(maskFilter)->asABlur(&blurRec)));
2137 bool hasLCD = glyphRunList.anyRunsLCD();
2139 // We canonicalize all non-lcd draws to use kUnknown_SkPixelGeometry
2140 SkPixelGeometry pixelGeometry = hasLCD ? strikeDevice.fSurfaceProps.pixelGeometry()
2141 : kUnknown_SkPixelGeometry;
2143 SkColor canonicalColor = compute_canonical_color(paint, hasLCD);
2145 key.fPixelGeometry = pixelGeometry;
2146 key.fUniqueID = glyphRunList.uniqueID();
2147 key.fStyle = paint.getStyle();
2148 if (key.fStyle != SkPaint::kFill_Style) {
2149 key.fFrameWidth = paint.getStrokeWidth();
2150 key.fMiterLimit = paint.getStrokeMiter();
2151 key.fJoin = paint.getStrokeJoin();
2153 key.fHasBlur = maskFilter != nullptr;
2155 key.fBlurRec = blurRec;
2157 key.fCanonicalColor = canonicalColor;
2158 key.fScalerContextFlags = SkTo<uint32_t>(strikeDevice.fScalerContextFlags);
2160 // Do any runs use direct drawing types?.
2161 key.fHasSomeDirectSubRuns = false;
2162 for (auto& run : glyphRunList) {
2163 SkScalar approximateDeviceTextSize =
2164 SkFontPriv::ApproximateTransformedTextSize(run.font(), drawMatrix);
2165 key.fHasSomeDirectSubRuns |=
2166 strikeDevice.fSDFTControl->isDirect(approximateDeviceTextSize, paint);
2169 if (key.fHasSomeDirectSubRuns) {
2170 // Store the fractional offset of the position. We know that the matrix can't be
2171 // perspective at this point.
2172 SkPoint mappedOrigin = drawMatrix.mapOrigin();
2173 key.fPositionMatrix = drawMatrix;
2174 key.fPositionMatrix.setTranslateX(
2175 mappedOrigin.x() - SkScalarFloorToScalar(mappedOrigin.x()));
2176 key.fPositionMatrix.setTranslateY(
2177 mappedOrigin.y() - SkScalarFloorToScalar(mappedOrigin.y()));
2179 // For path and SDFT, the matrix doesn't matter.
2180 key.fPositionMatrix = SkMatrix::I();
2184 return {canCache, key};
2187 bool TextBlob::Key::operator==(const TextBlob::Key& that) const {
2188 if (fUniqueID != that.fUniqueID) { return false; }
2189 if (fCanonicalColor != that.fCanonicalColor) { return false; }
2190 if (fStyle != that.fStyle) { return false; }
2191 if (fStyle != SkPaint::kFill_Style) {
2192 if (fFrameWidth != that.fFrameWidth ||
2193 fMiterLimit != that.fMiterLimit ||
2194 fJoin != that.fJoin) {
2198 if (fPixelGeometry != that.fPixelGeometry) { return false; }
2199 if (fHasBlur != that.fHasBlur) { return false; }
2201 if (fBlurRec.fStyle != that.fBlurRec.fStyle || fBlurRec.fSigma != that.fBlurRec.fSigma) {
2205 if (fScalerContextFlags != that.fScalerContextFlags) { return false; }
2207 if (fPositionMatrix.hasPerspective()) {
2208 if (fPositionMatrix[SkMatrix::kMPersp0] != that.fPositionMatrix[SkMatrix::kMPersp0] ||
2209 fPositionMatrix[SkMatrix::kMPersp1] != that.fPositionMatrix[SkMatrix::kMPersp1] ||
2210 fPositionMatrix[SkMatrix::kMPersp2] != that.fPositionMatrix[SkMatrix::kMPersp2]) {
2215 if (fHasSomeDirectSubRuns != that.fHasSomeDirectSubRuns) {
2219 if (fHasSomeDirectSubRuns) {
2220 auto [compatible, _] = can_use_direct(fPositionMatrix, that.fPositionMatrix);
2227 // -- TextBlob -----------------------------------------------------------------------------------
2228 void TextBlob::operator delete(void* p) { ::operator delete(p); }
2229 void* TextBlob::operator new(size_t) { SK_ABORT("All blobs are created by placement new."); }
2230 void* TextBlob::operator new(size_t, void* p) { return p; }
2232 TextBlob::~TextBlob() = default;
2234 sk_sp<TextBlob> TextBlob::Make(const SkGlyphRunList& glyphRunList,
2235 const SkPaint& paint,
2236 const SkMatrix& positionMatrix,
2237 SkStrikeDeviceInfo strikeDeviceInfo,
2238 SkStrikeForGPUCacheInterface* strikeCache) {
2239 // The difference in alignment from the per-glyph data to the SubRun;
2240 constexpr size_t alignDiff = alignof(DirectMaskSubRun) - alignof(DevicePosition);
2241 constexpr size_t vertexDataToSubRunPadding = alignDiff > 0 ? alignDiff : 0;
2242 size_t totalGlyphCount = glyphRunList.totalGlyphCount();
2244 // The neededForSubRun is optimized for DirectMaskSubRun which is by far the most common case.
2245 size_t subRunSizeHint =
2246 totalGlyphCount * sizeof(DevicePosition)
2247 + GlyphVector::GlyphVectorSize(totalGlyphCount)
2248 + glyphRunList.runCount() * (sizeof(DirectMaskSubRun) + vertexDataToSubRunPadding);
2250 auto [initializer, totalMemoryAllocated, alloc] =
2251 SubRunAllocator::AllocateClassMemoryAndArena<TextBlob>(subRunSizeHint);
2252 SkColor initialLuminance = SkPaintPriv::ComputeLuminanceColor(paint);
2253 sk_sp<TextBlob> blob = sk_sp<TextBlob>(initializer.initialize(
2254 std::move(alloc), totalMemoryAllocated, positionMatrix, initialLuminance));
2256 // Be sure to pass the ref to the matrix that the SubRuns will capture.
2257 blob->fSomeGlyphsExcluded = SkGlyphRunListPainter::CategorizeGlyphRunList(
2258 glyphRunList, blob->fInitialPositionMatrix, paint,
2259 strikeDeviceInfo, strikeCache, &blob->fSubRunList, &blob->fAlloc, "TextBlob");
2264 void TextBlob::addKey(const Key& key) {
2268 bool TextBlob::hasPerspective() const { return fInitialPositionMatrix.hasPerspective(); }
2270 bool TextBlob::canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const {
2271 // A singular matrix will create a TextBlob with no SubRuns, but unknown glyphs can
2272 // also cause empty runs. If there are no subRuns or some glyphs were excluded or perspective,
2273 // then regenerate when the matrices don't match.
2274 if ((fSubRunList.isEmpty() || fSomeGlyphsExcluded || hasPerspective()) &&
2275 fInitialPositionMatrix != positionMatrix)
2280 // If we have LCD text then our canonical color will be set to transparent, in this case we have
2281 // to regenerate the blob on any color change
2282 // We use the grPaint to get any color filter effects
2283 if (fKey.fCanonicalColor == SK_ColorTRANSPARENT &&
2284 fInitialLuminance != SkPaintPriv::ComputeLuminanceColor(paint)) {
2288 for (const SubRun& subRun : fSubRunList) {
2289 if (!subRun.canReuse(paint, positionMatrix)) {
2297 const TextBlob::Key& TextBlob::key() const { return fKey; }
2300 void TextBlob::draw(SkCanvas* canvas,
2302 const SkMatrixProvider& viewMatrix,
2304 const SkPaint& paint,
2305 skgpu::v1::SurfaceDrawContext* sdc) {
2306 for (const SubRun& subRun : fSubRunList) {
2307 subRun.draw(canvas, clip, viewMatrix, drawOrigin, paint, sk_ref_sp(this), sdc);
2312 const AtlasSubRun* TextBlob::testingOnlyFirstSubRun() const {
2313 if (fSubRunList.isEmpty()) {
2317 return fSubRunList.front().testingOnly_atlasSubRun();
2320 TextBlob::TextBlob(SubRunAllocator&& alloc,
2321 int totalMemorySize,
2322 const SkMatrix& positionMatrix,
2323 SkColor initialLuminance)
2324 : fAlloc{std::move(alloc)}
2325 , fSize(totalMemorySize)
2326 , fInitialPositionMatrix{positionMatrix}
2327 , fInitialLuminance{initialLuminance} { }
2330 // -- SlugImpl -------------------------------------------------------------------------------------
2331 class SlugImpl final : public Slug {
2333 SlugImpl(SubRunAllocator&& alloc,
2334 SkRect sourceBounds,
2335 const SkPaint& paint,
2336 const SkMatrix& positionMatrix,
2338 ~SlugImpl() override = default;
2340 static sk_sp<SlugImpl> Make(const SkMatrixProvider& viewMatrix,
2341 const SkGlyphRunList& glyphRunList,
2342 const SkPaint& initialPaint,
2343 const SkPaint& drawingPaint,
2344 SkStrikeDeviceInfo strikeDeviceInfo,
2345 SkStrikeForGPUCacheInterface* strikeCache);
2346 static sk_sp<Slug> MakeFromBuffer(SkReadBuffer& buffer,
2347 const SkStrikeClient* client);
2350 void surfaceDraw(SkCanvas*,
2352 const SkMatrixProvider& viewMatrix,
2353 const SkPaint& paint,
2354 skgpu::v1::SurfaceDrawContext* sdc) const;
2357 void doFlatten(SkWriteBuffer& buffer) const override;
2358 SkRect sourceBounds() const override { return fSourceBounds; }
2359 const SkPaint& initialPaint() const override { return fInitialPaint; }
2361 const SkMatrix& initialPositionMatrix() const { return fInitialPositionMatrix; }
2362 SkPoint origin() const { return fOrigin; }
2364 // Change memory management to handle the data after Slug, but in the same allocation
2365 // of memory. Only allow placement new.
2366 void operator delete(void* p) { ::operator delete(p); }
2367 void* operator new(size_t) { SK_ABORT("All slugs are created by placement new."); }
2368 void* operator new(size_t, void* p) { return p; }
2370 std::tuple<int, int> subRunCountAndUnflattenSizeHint() const {
2371 int unflattenSizeHint = 0;
2372 int subRunCount = 0;
2373 for (auto& subrun : fSubRuns) {
2375 unflattenSizeHint += subrun.unflattenSize();
2377 return {subRunCount, unflattenSizeHint};
2381 // The allocator must come first because it needs to be destroyed last. Other fields of this
2382 // structure may have pointers into it.
2383 SubRunAllocator fAlloc;
2384 const SkRect fSourceBounds;
2385 const SkPaint fInitialPaint;
2386 const SkMatrix fInitialPositionMatrix;
2387 const SkPoint fOrigin;
2388 SubRunList fSubRuns;
2391 SlugImpl::SlugImpl(SubRunAllocator&& alloc,
2392 SkRect sourceBounds,
2393 const SkPaint& initialPaint,
2394 const SkMatrix& positionMatrix,
2396 : fAlloc {std::move(alloc)}
2397 , fSourceBounds{sourceBounds}
2398 , fInitialPaint{initialPaint}
2399 , fInitialPositionMatrix{positionMatrix}
2400 , fOrigin{origin} {}
2403 void SlugImpl::surfaceDraw(SkCanvas* canvas, const GrClip* clip, const SkMatrixProvider& viewMatrix,
2404 const SkPaint& drawingPaint, skgpu::v1::SurfaceDrawContext* sdc) const {
2405 for (const SubRun& subRun : fSubRuns) {
2406 subRun.draw(canvas, clip, viewMatrix, fOrigin, drawingPaint, sk_ref_sp(this), sdc);
2411 void SlugImpl::doFlatten(SkWriteBuffer& buffer) const {
2412 buffer.writeRect(fSourceBounds);
2413 SkPaintPriv::Flatten(fInitialPaint, buffer);
2414 buffer.writeMatrix(fInitialPositionMatrix);
2415 buffer.writePoint(fOrigin);
2416 auto [subRunCount, subRunsUnflattenSizeHint] = this->subRunCountAndUnflattenSizeHint();
2417 buffer.writeInt(subRunCount);
2418 buffer.writeInt(subRunsUnflattenSizeHint);
2419 for (auto& subRun : fSubRuns) {
2420 subRun.flatten(buffer);
2424 sk_sp<Slug> SlugImpl::MakeFromBuffer(SkReadBuffer& buffer, const SkStrikeClient* client) {
2425 SkRect sourceBounds = buffer.readRect();
2426 SkASSERT(!sourceBounds.isEmpty());
2427 if (!buffer.validate(!sourceBounds.isEmpty())) { return nullptr; }
2429 SkPaint paint = buffer.readPaint();
2430 SkMatrix positionMatrix;
2431 buffer.readMatrix(&positionMatrix);
2432 SkPoint origin = buffer.readPoint();
2433 int subRunCount = buffer.readInt();
2434 SkASSERT(subRunCount > 0);
2435 if (!buffer.validate(subRunCount > 0)) { return nullptr; }
2436 int subRunsSizeHint = buffer.readInt();
2438 // Since the hint doesn't affect performance, then if it looks fishy just pick a reasonable
2440 if (subRunsSizeHint < 0 || (1 << 16) < subRunsSizeHint) {
2441 subRunsSizeHint = 128;
2444 auto [initializer, _, alloc] =
2445 SubRunAllocator::AllocateClassMemoryAndArena<SlugImpl>(subRunsSizeHint);
2447 sk_sp<SlugImpl> slug = sk_sp<SlugImpl>(
2448 initializer.initialize(std::move(alloc), sourceBounds, paint, positionMatrix, origin));
2450 for (int i = 0; i < subRunCount; ++i) {
2451 auto subRun = SubRun::MakeFromBuffer(
2452 slug->initialPositionMatrix(), buffer, &slug->fAlloc, client);
2453 if (!buffer.validate(subRun != nullptr)) { return nullptr; }
2454 if (subRun != nullptr) {
2455 slug->fSubRuns.append(std::move(subRun));
2459 // Something went wrong while reading.
2460 SkASSERT(buffer.isValid());
2461 if (!buffer.isValid()) { return nullptr;}
2463 return std::move(slug);
2466 sk_sp<SlugImpl> SlugImpl::Make(const SkMatrixProvider& viewMatrix,
2467 const SkGlyphRunList& glyphRunList,
2468 const SkPaint& initialPaint,
2469 const SkPaint& drawingPaint,
2470 SkStrikeDeviceInfo strikeDeviceInfo,
2471 SkStrikeForGPUCacheInterface* strikeCache) {
2472 // The difference in alignment from the per-glyph data to the SubRun;
2473 constexpr size_t alignDiff = alignof(DirectMaskSubRun) - alignof(DevicePosition);
2474 constexpr size_t vertexDataToSubRunPadding = alignDiff > 0 ? alignDiff : 0;
2475 size_t totalGlyphCount = glyphRunList.totalGlyphCount();
2476 // The bytesNeededForSubRun is optimized for DirectMaskSubRun which is by far the most
2478 size_t subRunSizeHint =
2479 totalGlyphCount * sizeof(DevicePosition)
2480 + GlyphVector::GlyphVectorSize(totalGlyphCount)
2481 + glyphRunList.runCount() * (sizeof(DirectMaskSubRun) + vertexDataToSubRunPadding);
2483 auto [initializer, _, alloc] =
2484 SubRunAllocator::AllocateClassMemoryAndArena<SlugImpl>(subRunSizeHint);
2486 const SkMatrix positionMatrix =
2487 position_matrix(viewMatrix.localToDevice(), glyphRunList.origin());
2489 sk_sp<SlugImpl> slug = sk_sp<SlugImpl>(initializer.initialize(
2490 std::move(alloc), glyphRunList.sourceBounds(), initialPaint, positionMatrix,
2491 glyphRunList.origin()));
2493 // Be sure to pass the ref to the initialPositionMatrix that the SubRuns will capture.
2494 SkGlyphRunListPainter::CategorizeGlyphRunList(
2495 glyphRunList, slug->fInitialPositionMatrix, drawingPaint,
2496 strikeDeviceInfo, strikeCache, &slug->fSubRuns, &slug->fAlloc, "Make Slug");
2498 // There is nothing to draw here. This is particularly a problem with RSX form blobs where a
2499 // single space becomes a run with no glyphs.
2500 if (slug->fSubRuns.isEmpty()) { return nullptr; }
2507 namespace skgpu::v1 {
2509 Device::convertGlyphRunListToSlug(const SkGlyphRunList& glyphRunList,
2510 const SkPaint& initialPaint,
2511 const SkPaint& drawingPaint) {
2512 return SlugImpl::Make(this->asMatrixProvider(),
2516 this->strikeDeviceInfo(),
2517 SkStrikeCache::GlobalStrikeCache());
2520 void Device::drawSlug(SkCanvas* canvas, const Slug* slug, const SkPaint& drawingPaint) {
2521 const SlugImpl* slugImpl = static_cast<const SlugImpl*>(slug);
2522 auto matrixProvider = this->asMatrixProvider();
2523 #if defined(SK_DEBUG)
2524 if (!fContext->priv().options().fSupportBilerpFromGlyphAtlas) {
2525 // We can draw a slug if the atlas has padding or if the creation matrix and the
2526 // drawing matrix are the same. If they are the same, then the Slug will use the direct
2527 // drawing code and not use bi-lerp.
2528 SkMatrix slugMatrix = slugImpl->initialPositionMatrix();
2529 SkMatrix positionMatrix = matrixProvider.localToDevice();
2530 positionMatrix.preTranslate(slugImpl->origin().x(), slugImpl->origin().y());
2531 SkASSERT(slugMatrix == positionMatrix);
2534 slugImpl->surfaceDraw(
2535 canvas, this->clip(), matrixProvider, drawingPaint, fSurfaceDrawContext.get());
2538 sk_sp<Slug> MakeSlug(const SkMatrixProvider& drawMatrix,
2539 const SkGlyphRunList& glyphRunList,
2540 const SkPaint& initialPaint,
2541 const SkPaint& drawingPaint,
2542 SkStrikeDeviceInfo strikeDeviceInfo,
2543 SkStrikeForGPUCacheInterface* strikeCache) {
2544 return SlugImpl::Make(
2545 drawMatrix, glyphRunList, initialPaint, drawingPaint, strikeDeviceInfo, strikeCache);
2547 } // namespace skgpu::v1
2550 namespace sktext::gpu {
2552 // -- SubRun -------------------------------------------------------------------------------------
2553 void SubRun::flatten(SkWriteBuffer& buffer) const {
2554 buffer.writeInt(this->subRunType());
2555 this->doFlatten(buffer);
2558 SubRunOwner SubRun::MakeFromBuffer(const SkMatrix& initialPositionMatrix,
2559 SkReadBuffer& buffer,
2560 SubRunAllocator* alloc,
2561 const SkStrikeClient* client) {
2562 using Maker = SubRunOwner (*)(const SkMatrix&,
2565 const SkStrikeClient*);
2567 static Maker makers[kSubRunTypeCount] = {
2568 nullptr, // 0 index is bad.
2569 DirectMaskSubRun::MakeFromBuffer,
2570 SDFTSubRun::MakeFromBuffer,
2571 TransformedMaskSubRun::MakeFromBuffer,
2572 PathSubRun::MakeFromBuffer,
2573 DrawableSubRun::MakeFromBuffer,
2575 int subRunTypeInt = buffer.readInt();
2576 SkASSERT(kBad < subRunTypeInt && subRunTypeInt < kSubRunTypeCount);
2577 if (!buffer.validate(kBad < subRunTypeInt && subRunTypeInt < kSubRunTypeCount)) {
2580 auto maker = makers[subRunTypeInt];
2581 if (!buffer.validate(maker != nullptr)) { return nullptr; }
2582 return maker(initialPositionMatrix, buffer, alloc, client);
2585 sk_sp<Slug> SkMakeSlugFromBuffer(SkReadBuffer& buffer, const SkStrikeClient* client) {
2586 return SlugImpl::MakeFromBuffer(buffer, client);
2589 } // namespace sktext::gpu
2591 // -- SkGlyphRunListPainter ------------------------------------------------------------------------
2592 // Use the following in your args.gn to dump telemetry for diagnosing chrome Renderer/GPU
2594 // extra_cflags = ["-D", "SK_TRACE_GLYPH_RUN_PROCESS"]
2596 #if defined(SK_TRACE_GLYPH_RUN_PROCESS)
2597 static const constexpr bool kTrace = true;
2599 static const constexpr bool kTrace = false;
2603 bool SkGlyphRunListPainter::CategorizeGlyphRunList(const SkGlyphRunList& glyphRunList,
2604 const SkMatrix& positionMatrix,
2605 const SkPaint& runPaint,
2606 SkStrikeDeviceInfo strikeDeviceInfo,
2607 SkStrikeForGPUCacheInterface* strikeCache,
2608 sktext::gpu::SubRunList* subRunList,
2609 sktext::gpu::SubRunAllocator* alloc,
2611 [[maybe_unused]] SkString msg;
2612 if constexpr (kTrace) {
2613 const uint64_t uniqueID = glyphRunList.uniqueID();
2614 msg.appendf("\nStart glyph run processing");
2615 if (tag != nullptr) {
2616 msg.appendf(" for %s ", tag);
2617 if (uniqueID != SK_InvalidUniqueID) {
2618 msg.appendf(" uniqueID: %" PRIu64, uniqueID);
2621 msg.appendf("\n matrix\n");
2622 msg.appendf(" %7.3g %7.3g %7.3g\n %7.3g %7.3g %7.3g\n",
2623 positionMatrix[0], positionMatrix[1], positionMatrix[2],
2624 positionMatrix[3], positionMatrix[4], positionMatrix[5]);
2627 SkASSERT(strikeDeviceInfo.fSDFTControl != nullptr);
2628 if (strikeDeviceInfo.fSDFTControl == nullptr) {
2632 const SkSurfaceProps deviceProps = strikeDeviceInfo.fSurfaceProps;
2633 const SkScalerContextFlags scalerContextFlags = strikeDeviceInfo.fScalerContextFlags;
2634 const sktext::gpu::SDFTControl SDFTControl = *strikeDeviceInfo.fSDFTControl;
2636 auto bufferScope = SkSubRunBuffers::EnsureBuffers(glyphRunList);
2637 auto [accepted, rejected] = bufferScope.buffers();
2638 bool someGlyphExcluded = false;
2639 for (auto& glyphRun : glyphRunList) {
2640 rejected->setSource(glyphRun.source());
2641 const SkFont& runFont = glyphRun.font();
2643 // Only consider using direct or SDFT drawing if not drawing hairlines and not perspective.
2644 if ((runPaint.getStyle() != SkPaint::kStroke_Style || runPaint.getStrokeWidth() != 0)
2645 && !positionMatrix.hasPerspective()) {
2646 SkScalar approximateDeviceTextSize =
2647 SkFontPriv::ApproximateTransformedTextSize(runFont, positionMatrix);
2649 if (SDFTControl.isSDFT(approximateDeviceTextSize, runPaint)) {
2650 // Process SDFT - This should be the .009% case.
2651 const auto& [strikeSpec, strikeToSourceScale, matrixRange] =
2652 SkStrikeSpec::MakeSDFT(
2653 runFont, runPaint, deviceProps, positionMatrix, SDFTControl);
2655 if constexpr(kTrace) {
2656 msg.appendf(" SDFT case:\n%s", strikeSpec.dump().c_str());
2659 if (!SkScalarNearlyZero(strikeToSourceScale)) {
2660 SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(strikeCache);
2662 accepted->startSource(rejected->source());
2663 if constexpr (kTrace) {
2664 msg.appendf(" glyphs:(x,y):\n %s\n", accepted->dumpInput().c_str());
2666 strike->prepareForSDFTDrawing(accepted, rejected);
2667 rejected->flipRejectsToSource();
2669 if (subRunList && !accepted->empty()) {
2670 subRunList->append(SDFTSubRun::Make(
2671 accepted->accepted(),
2673 strike->getUnderlyingStrike(),
2674 strikeToSourceScale,
2675 matrixRange, alloc));
2680 if (!rejected->source().empty()) {
2681 // Process masks including ARGB - this should be the 99.99% case.
2682 // This will handle medium size emoji that are sharing the run with SDFT drawn text.
2683 // If things are too big they will be passed along to the drawing of last resort
2685 SkStrikeSpec strikeSpec = SkStrikeSpec::MakeMask(
2686 runFont, runPaint, deviceProps, scalerContextFlags, positionMatrix);
2688 if constexpr (kTrace) {
2689 msg.appendf(" Mask case:\n%s", strikeSpec.dump().c_str());
2692 SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(strikeCache);
2694 accepted->startDevicePositioning(
2695 rejected->source(), positionMatrix, strike->roundingSpec());
2696 if constexpr (kTrace) {
2697 msg.appendf(" glyphs:(x,y):\n %s\n", accepted->dumpInput().c_str());
2699 strike->prepareForMaskDrawing(accepted, rejected);
2700 rejected->flipRejectsToSource();
2702 if (subRunList && !accepted->empty()) {
2703 auto addGlyphsWithSameFormat = [&] (
2704 const SkZip<SkGlyphVariant, SkPoint>& acceptedGlyphsAndLocations,
2706 sk_sp<SkStrike>&& runStrike) {
2707 SubRunOwner subRun = DirectMaskSubRun::Make(
2708 acceptedGlyphsAndLocations,
2710 std::move(runStrike),
2713 if (subRun != nullptr) {
2714 subRunList->append(std::move(subRun));
2716 someGlyphExcluded |= true;
2719 add_multi_mask_format(addGlyphsWithSameFormat,
2720 accepted->accepted(),
2721 strike->getUnderlyingStrike());
2726 // Glyphs are generated in different scales relative to the source space. Masks are drawn
2727 // in device space, and SDFT and Paths are draw in a fixed constant space. The
2728 // maxDimensionInSourceSpace is used to calculate the factor from strike space to source
2730 SkScalar maxDimensionInSourceSpace = 0.0;
2731 if (!rejected->source().empty()) {
2732 // Drawable case - handle big things with that have a drawable.
2733 auto [strikeSpec, strikeToSourceScale] =
2734 SkStrikeSpec::MakePath(runFont, runPaint, deviceProps, scalerContextFlags);
2736 if constexpr (kTrace) {
2737 msg.appendf(" Drawable case:\n%s", strikeSpec.dump().c_str());
2740 if (!SkScalarNearlyZero(strikeToSourceScale)) {
2741 SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(strikeCache);
2743 accepted->startSource(rejected->source());
2744 if constexpr (kTrace) {
2745 msg.appendf(" glyphs:(x,y):\n %s\n", accepted->dumpInput().c_str());
2747 strike->prepareForDrawableDrawing(accepted, rejected);
2748 rejected->flipRejectsToSource();
2749 auto [minHint, maxHint] = rejected->maxDimensionHint();
2750 maxDimensionInSourceSpace = SkScalarCeilToScalar(maxHint * strikeToSourceScale);
2752 if (subRunList && !accepted->empty()) {
2753 subRunList->append(make_drawable_sub_run<DrawableSubRun>(
2754 accepted->accepted(),
2755 strike->getUnderlyingStrike(),
2756 strikeToSourceScale,
2757 strikeSpec.descriptor(),
2762 if (!rejected->source().empty()) {
2763 // Path case - handle big things without color and that have a path.
2764 auto [strikeSpec, strikeToSourceScale] =
2765 SkStrikeSpec::MakePath(runFont, runPaint, deviceProps, scalerContextFlags);
2767 #if defined(SK_TRACE_GLYPH_RUN_PROCESS)
2768 msg.appendf(" Path case:\n%s", strikeSpec.dump().c_str());
2771 if (!SkScalarNearlyZero(strikeToSourceScale)) {
2772 SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(strikeCache);
2774 accepted->startSource(rejected->source());
2775 if constexpr (kTrace) {
2776 msg.appendf(" glyphs:(x,y):\n %s\n", accepted->dumpInput().c_str());
2778 strike->prepareForPathDrawing(accepted, rejected);
2779 rejected->flipRejectsToSource();
2780 auto [minHint, maxHint] = rejected->maxDimensionHint();
2781 maxDimensionInSourceSpace = SkScalarCeilToScalar(maxHint * strikeToSourceScale);
2783 if (subRunList && !accepted->empty()) {
2784 subRunList->append(PathSubRun::Make(accepted->accepted(),
2785 has_some_antialiasing(runFont),
2786 strikeToSourceScale,
2787 strikeSpec.descriptor(),
2793 if (!rejected->source().empty() && maxDimensionInSourceSpace != 0) {
2794 // Draw of last resort. Scale the bitmap to the screen.
2795 auto [strikeSpec, strikeToSourceScaleTemp] = SkStrikeSpec::MakeSourceFallback(
2796 runFont, runPaint, deviceProps,
2797 scalerContextFlags, maxDimensionInSourceSpace);
2799 if constexpr (kTrace) {
2800 msg.appendf("Transformed case:\n%s", strikeSpec.dump().c_str());
2802 // Get around fake binding from structural decomposition. Bad c++.
2803 auto strikeToSourceScale = strikeToSourceScaleTemp;
2805 if (!SkScalarNearlyZero(strikeToSourceScale)) {
2806 SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(strikeCache);
2808 accepted->startSource(rejected->source());
2809 if constexpr (kTrace) {
2810 msg.appendf("glyphs:(x,y):\n %s\n", accepted->dumpInput().c_str());
2812 strike->prepareForMaskDrawing(accepted, rejected);
2813 rejected->flipRejectsToSource();
2814 SkASSERT(rejected->source().empty());
2816 if (subRunList && !accepted->empty()) {
2817 auto addGlyphsWithSameFormat = [&] (
2818 const SkZip<SkGlyphVariant, SkPoint>& acceptedGlyphsAndLocations,
2820 sk_sp<SkStrike>&& runStrike) {
2821 SubRunOwner subRun = TransformedMaskSubRun::Make(
2822 acceptedGlyphsAndLocations, positionMatrix, std::move(runStrike),
2823 strikeToSourceScale, format, alloc);
2824 if (subRun != nullptr) {
2825 subRunList->append(std::move(subRun));
2827 someGlyphExcluded |= true;
2830 add_multi_mask_format(
2831 addGlyphsWithSameFormat,
2832 accepted->accepted(),
2833 strike->getUnderlyingStrike());
2838 if constexpr (kTrace) {
2839 msg.appendf("End glyph run processing");
2840 if (tag != nullptr) {
2841 msg.appendf(" for %s ", tag);
2843 SkDebugf("%s\n", msg.c_str());
2845 return someGlyphExcluded;