Flutter Engine
The Flutter Engine
SubRunContainer.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2022 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
9
12#include "include/core/SkFont.h"
16#include "include/core/SkPath.h"
20#include "include/core/SkRect.h"
33#include "src/base/SkZip.h"
34#include "src/core/SkDevice.h"
37#include "src/core/SkFontPriv.h"
38#include "src/core/SkGlyph.h"
39#include "src/core/SkMask.h"
45#include "src/core/SkStrike.h"
49#include "src/gpu/AtlasTypes.h"
50#include "src/text/GlyphRun.h"
52#include "src/text/gpu/Glyph.h"
58
59#include <algorithm>
60#include <climits>
61#include <cstdint>
62#include <initializer_list>
63#include <new>
64#include <optional>
65#include <vector>
66
68
69#if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
74#include "src/gpu/ganesh/SkGr.h"
78using AtlasTextOp = skgpu::ganesh::AtlasTextOp;
79#endif // defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
80
81using namespace skia_private;
82using namespace skglyph;
83
84// -- GPU Text -------------------------------------------------------------------------------------
85// Naming conventions
86// * drawMatrix - the CTM from the canvas.
87// * drawOrigin - the x, y location of the drawTextBlob call.
88// * positionMatrix - this is the combination of the drawMatrix and the drawOrigin:
89// positionMatrix = drawMatrix * TranslationMatrix(drawOrigin.x, drawOrigin.y);
90//
91// Note:
92// In order to transform Slugs, you need to set the fSupportBilerpFromGlyphAtlas on
93// GrContextOptions.
94
95namespace sktext::gpu {
96// -- SubRunStreamTag ------------------------------------------------------------------------------
98 kBad = 0, // Make this 0 to line up with errors from readInt.
100#if !defined(SK_DISABLE_SDF_TEXT)
102#endif
107};
108
109} // namespace sktext::gpu
110
112
113using namespace sktext;
114using namespace sktext::gpu;
115
116namespace {
117#if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
119 const SkPaint& paint,
120 const SkMatrix& matrix,
121 MaskFormat maskFormat,
122 GrPaint* grPaint) {
123 GrRecordingContext* rContext = sdc->recordingContext();
124 const GrColorInfo& colorInfo = sdc->colorInfo();
125 const SkSurfaceProps& props = sdc->surfaceProps();
126 if (maskFormat == MaskFormat::kARGB) {
127 SkPaintToGrPaintReplaceShader(rContext, colorInfo, paint, matrix, nullptr, props, grPaint);
128 float a = grPaint->getColor4f().fA;
129 return {a, a, a, a};
130 }
131 SkPaintToGrPaint(rContext, colorInfo, paint, matrix, props, grPaint);
132 return grPaint->getColor4f();
133}
134
135SkMatrix position_matrix(const SkMatrix& drawMatrix, SkPoint drawOrigin) {
136 SkMatrix position_matrix = drawMatrix;
137 return position_matrix.preTranslate(drawOrigin.x(), drawOrigin.y());
138}
139#endif // defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
140
142 return accepted.get<0>();
143}
144
146 return accepted.get<0>();
147}
148
149template <typename U>
150SkSpan<const SkPoint> get_positions(SkZip<U, const SkPoint> accepted) {
151 return accepted.template get<1>();
152}
153
154// -- PathOpSubmitter ------------------------------------------------------------------------------
155// PathOpSubmitter holds glyph ids until ready to draw. During drawing, the glyph ids are
156// converted to SkPaths. PathOpSubmitter can only be serialized when it is holding glyph ids;
157// it can only be serialized before submitDraws has been called.
158class PathOpSubmitter {
159public:
160 PathOpSubmitter() = delete;
161 PathOpSubmitter(const PathOpSubmitter&) = delete;
162 const PathOpSubmitter& operator=(const PathOpSubmitter&) = delete;
163 PathOpSubmitter(PathOpSubmitter&& that)
164 // Transfer ownership of fIDsOrPaths from that to this.
165 : fIDsOrPaths{std::exchange(
166 const_cast<SkSpan<IDOrPath>&>(that.fIDsOrPaths), SkSpan<IDOrPath>{})}
167 , fPositions{that.fPositions}
168 , fStrikeToSourceScale{that.fStrikeToSourceScale}
169 , fIsAntiAliased{that.fIsAntiAliased}
170 , fStrikePromise{std::move(that.fStrikePromise)} {}
171 PathOpSubmitter& operator=(PathOpSubmitter&& that) {
172 this->~PathOpSubmitter();
173 new (this) PathOpSubmitter{std::move(that)};
174 return *this;
175 }
176 PathOpSubmitter(bool isAntiAliased,
177 SkScalar strikeToSourceScale,
178 SkSpan<SkPoint> positions,
179 SkSpan<IDOrPath> idsOrPaths,
180 SkStrikePromise&& strikePromise);
181
182 ~PathOpSubmitter();
183
184 static PathOpSubmitter Make(SkZip<const SkGlyphID, const SkPoint> accepted,
185 bool isAntiAliased,
186 SkScalar strikeToSourceScale,
187 SkStrikePromise&& strikePromise,
188 SubRunAllocator* alloc);
189
190 int unflattenSize() const;
191 void flatten(SkWriteBuffer& buffer) const;
192 static std::optional<PathOpSubmitter> MakeFromBuffer(SkReadBuffer& buffer,
193 SubRunAllocator* alloc,
194 const SkStrikeClient* client);
195
196 // submitDraws is not thread safe. It only occurs the single thread drawing portion of the GPU
197 // rendering.
198 void submitDraws(SkCanvas*,
199 SkPoint drawOrigin,
200 const SkPaint& paint) const;
201
202private:
203 // When PathOpSubmitter is created only the glyphIDs are needed, during the submitDraws call,
204 // the glyphIDs are converted to SkPaths.
205 const SkSpan<IDOrPath> fIDsOrPaths;
206 const SkSpan<const SkPoint> fPositions;
207 const SkScalar fStrikeToSourceScale;
208 const bool fIsAntiAliased;
209
210 mutable SkStrikePromise fStrikePromise;
211 mutable SkOnce fConvertIDsToPaths;
212 mutable bool fPathsAreCreated{false};
213};
214
215int PathOpSubmitter::unflattenSize() const {
216 return fPositions.size_bytes() + fIDsOrPaths.size_bytes();
217}
218
219void PathOpSubmitter::flatten(SkWriteBuffer& buffer) const {
220 fStrikePromise.flatten(buffer);
221
222 buffer.writeInt(fIsAntiAliased);
223 buffer.writeScalar(fStrikeToSourceScale);
224 buffer.writePointArray(fPositions.data(), SkCount(fPositions));
225 for (IDOrPath& idOrPath : fIDsOrPaths) {
226 buffer.writeInt(idOrPath.fGlyphID);
227 }
228}
229
230std::optional<PathOpSubmitter> PathOpSubmitter::MakeFromBuffer(SkReadBuffer& buffer,
231 SubRunAllocator* alloc,
232 const SkStrikeClient* client) {
233 std::optional<SkStrikePromise> strikePromise =
234 SkStrikePromise::MakeFromBuffer(buffer, client, SkStrikeCache::GlobalStrikeCache());
235 if (!buffer.validate(strikePromise.has_value())) {
236 return std::nullopt;
237 }
238
239 bool isAntiAlias = buffer.readInt();
240
241 SkScalar strikeToSourceScale = buffer.readScalar();
242 if (!buffer.validate(0 < strikeToSourceScale)) { return std::nullopt; }
243
244 SkSpan<SkPoint> positions = MakePointsFromBuffer(buffer, alloc);
245 if (positions.empty()) { return std::nullopt; }
246 const int glyphCount = SkCount(positions);
247
248 // Remember, we stored an int for glyph id.
249 if (!buffer.validateCanReadN<int>(glyphCount)) { return std::nullopt; }
250 auto idsOrPaths = SkSpan(alloc->makeUniqueArray<IDOrPath>(glyphCount).release(), glyphCount);
251 for (auto& idOrPath : idsOrPaths) {
252 idOrPath.fGlyphID = SkTo<SkGlyphID>(buffer.readInt());
253 }
254
255 if (!buffer.isValid()) { return std::nullopt; }
256
257 return PathOpSubmitter{isAntiAlias,
258 strikeToSourceScale,
259 positions,
260 idsOrPaths,
261 std::move(strikePromise.value())};
262}
263
264PathOpSubmitter::PathOpSubmitter(
265 bool isAntiAliased,
266 SkScalar strikeToSourceScale,
267 SkSpan<SkPoint> positions,
268 SkSpan<IDOrPath> idsOrPaths,
269 SkStrikePromise&& strikePromise)
270 : fIDsOrPaths{idsOrPaths}
271 , fPositions{positions}
272 , fStrikeToSourceScale{strikeToSourceScale}
273 , fIsAntiAliased{isAntiAliased}
274 , fStrikePromise{std::move(strikePromise)} {
275 SkASSERT(!fPositions.empty());
276}
277
278PathOpSubmitter::~PathOpSubmitter() {
279 // If we have converted glyph IDs to paths, then clean up the SkPaths.
280 if (fPathsAreCreated) {
281 for (auto& idOrPath : fIDsOrPaths) {
282 idOrPath.fPath.~SkPath();
283 }
284 }
285}
286
288 bool isAntiAliased,
289 SkScalar strikeToSourceScale,
290 SkStrikePromise&& strikePromise,
291 SubRunAllocator* alloc) {
292 auto mapToIDOrPath = [](SkGlyphID glyphID) { return IDOrPath{glyphID}; };
293
294 IDOrPath* const rawIDsOrPaths =
295 alloc->makeUniqueArray<IDOrPath>(get_glyphIDs(accepted), mapToIDOrPath).release();
296
297 return PathOpSubmitter{isAntiAliased,
298 strikeToSourceScale,
299 alloc->makePODSpan(get_positions(accepted)),
300 SkSpan(rawIDsOrPaths, accepted.size()),
301 std::move(strikePromise)};
302}
303
304void
305PathOpSubmitter::submitDraws(SkCanvas* canvas, SkPoint drawOrigin, const SkPaint& paint) const {
306 // Convert the glyph IDs to paths if it hasn't been done yet. This is thread safe.
307 fConvertIDsToPaths([&]() {
308 if (SkStrike* strike = fStrikePromise.strike()) {
309 strike->glyphIDsToPaths(fIDsOrPaths);
310
311 // Drop ref to strike so that it can be purged from the cache if needed.
312 fStrikePromise.resetStrike();
313 fPathsAreCreated = true;
314 }
315 });
316
317 SkPaint runPaint{paint};
318 runPaint.setAntiAlias(fIsAntiAliased);
319
320 SkMaskFilterBase* maskFilter = as_MFB(runPaint.getMaskFilter());
321
322 // Calculate the matrix that maps the path glyphs from their size in the strike to
323 // the graphics source space.
324 SkMatrix strikeToSource = SkMatrix::Scale(fStrikeToSourceScale, fStrikeToSourceScale);
325 strikeToSource.postTranslate(drawOrigin.x(), drawOrigin.y());
326
327 // If there are shaders, non-blur mask filters or styles, the path must be scaled into source
328 // space independently of the CTM. This allows the CTM to be correct for the different effects.
329 SkStrokeRec style(runPaint);
330 bool needsExactCTM = runPaint.getShader()
331 || runPaint.getPathEffect()
332 || (!style.isFillStyle() && !style.isHairlineStyle())
333 || (maskFilter != nullptr && !maskFilter->asABlur(nullptr));
334 if (!needsExactCTM) {
336
337 // If there is a blur mask filter, then sigma needs to be adjusted to account for the
338 // scaling of fStrikeToSourceScale.
339 if (maskFilter != nullptr && maskFilter->asABlur(&blurRec)) {
340 runPaint.setMaskFilter(
341 SkMaskFilter::MakeBlur(blurRec.fStyle, blurRec.fSigma / fStrikeToSourceScale));
342 }
343 for (auto [idOrPath, pos] : SkMakeZip(fIDsOrPaths, fPositions)) {
344 // Transform the glyph to source space.
345 SkMatrix pathMatrix = strikeToSource;
346 pathMatrix.postTranslate(pos.x(), pos.y());
347
348 SkAutoCanvasRestore acr(canvas, true);
349 canvas->concat(pathMatrix);
350 canvas->drawPath(idOrPath.fPath, runPaint);
351 }
352 } else {
353 // Transform the path to device because the deviceMatrix must be unchanged to
354 // draw effect, filter or shader paths.
355 for (auto [idOrPath, pos] : SkMakeZip(fIDsOrPaths, fPositions)) {
356 // Transform the glyph to source space.
357 SkMatrix pathMatrix = strikeToSource;
358 pathMatrix.postTranslate(pos.x(), pos.y());
359
360 SkPath deviceOutline;
361 idOrPath.fPath.transform(pathMatrix, &deviceOutline);
362 deviceOutline.setIsVolatile(true);
363 canvas->drawPath(deviceOutline, runPaint);
364 }
365 }
366}
367
368// -- PathSubRun -----------------------------------------------------------------------------------
369class PathSubRun final : public SubRun {
370public:
371 PathSubRun(PathOpSubmitter&& pathDrawing) : fPathDrawing(std::move(pathDrawing)) {}
372
374 bool isAntiAliased,
375 SkScalar strikeToSourceScale,
376 SkStrikePromise&& strikePromise,
377 SubRunAllocator* alloc) {
378 return alloc->makeUnique<PathSubRun>(
380 accepted, isAntiAliased, strikeToSourceScale, std::move(strikePromise), alloc));
381 }
382
383 void draw(SkCanvas* canvas,
384 SkPoint drawOrigin,
385 const SkPaint& paint,
387 const AtlasDrawDelegate&) const override {
388 fPathDrawing.submitDraws(canvas, drawOrigin, paint);
389 }
390
391 int unflattenSize() const override;
392
393 bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const override {
394 return true;
395 }
396 const AtlasSubRun* testingOnly_atlasSubRun() const override { return nullptr; }
398 SubRunAllocator* alloc,
399 const SkStrikeClient* client);
400
401protected:
402 SubRunStreamTag subRunStreamTag() const override { return SubRunStreamTag::kPathStreamTag; }
403 void doFlatten(SkWriteBuffer& buffer) const override;
404
405private:
406 PathOpSubmitter fPathDrawing;
407};
408
409int PathSubRun::unflattenSize() const {
410 return sizeof(PathSubRun) + fPathDrawing.unflattenSize();
411}
412
413void PathSubRun::doFlatten(SkWriteBuffer& buffer) const {
414 fPathDrawing.flatten(buffer);
415}
416
417SubRunOwner PathSubRun::MakeFromBuffer(SkReadBuffer& buffer,
418 SubRunAllocator* alloc,
419 const SkStrikeClient* client) {
420 auto pathOpSubmitter = PathOpSubmitter::MakeFromBuffer(buffer, alloc, client);
421 if (!buffer.validate(pathOpSubmitter.has_value())) { return nullptr; }
422 return alloc->makeUnique<PathSubRun>(std::move(*pathOpSubmitter));
423}
424
425// -- DrawableOpSubmitter --------------------------------------------------------------------------
426// Shared code for submitting GPU ops for drawing glyphs as drawables.
427class DrawableOpSubmitter {
428public:
429 DrawableOpSubmitter() = delete;
430 DrawableOpSubmitter(const DrawableOpSubmitter&) = delete;
431 const DrawableOpSubmitter& operator=(const DrawableOpSubmitter&) = delete;
432 DrawableOpSubmitter(DrawableOpSubmitter&& that)
433 : fStrikeToSourceScale{that.fStrikeToSourceScale}
434 , fPositions{that.fPositions}
435 , fIDsOrDrawables{that.fIDsOrDrawables}
436 , fStrikePromise{std::move(that.fStrikePromise)} {}
437 DrawableOpSubmitter& operator=(DrawableOpSubmitter&& that) {
438 this->~DrawableOpSubmitter();
439 new (this) DrawableOpSubmitter{std::move(that)};
440 return *this;
441 }
442 DrawableOpSubmitter(SkScalar strikeToSourceScale,
443 SkSpan<SkPoint> positions,
444 SkSpan<IDOrDrawable> idsOrDrawables,
445 SkStrikePromise&& strikePromise);
446
447 static DrawableOpSubmitter Make(SkZip<const SkGlyphID, const SkPoint> accepted,
448 SkScalar strikeToSourceScale,
449 SkStrikePromise&& strikePromise,
450 SubRunAllocator* alloc) {
451 auto mapToIDOrDrawable = [](const SkGlyphID glyphID) { return IDOrDrawable{glyphID}; };
452
453 return DrawableOpSubmitter{
454 strikeToSourceScale,
455 alloc->makePODSpan(get_positions(accepted)),
456 alloc->makePODArray<IDOrDrawable>(get_glyphIDs(accepted), mapToIDOrDrawable),
457 std::move(strikePromise)};
458 }
459
460 int unflattenSize() const;
461 void flatten(SkWriteBuffer& buffer) const;
462 static std::optional<DrawableOpSubmitter> MakeFromBuffer(SkReadBuffer& buffer,
463 SubRunAllocator* alloc,
464 const SkStrikeClient* client);
465 void submitDraws(SkCanvas* canvas, SkPoint drawOrigin, const SkPaint& paint) const;
466
467private:
468 const SkScalar fStrikeToSourceScale;
469 const SkSpan<SkPoint> fPositions;
470 const SkSpan<IDOrDrawable> fIDsOrDrawables;
471 // When the promise is converted to a strike it acts as the ref on the strike to keep the
472 // SkDrawable data alive.
473 mutable SkStrikePromise fStrikePromise;
474 mutable SkOnce fConvertIDsToDrawables;
475};
476
477int DrawableOpSubmitter::unflattenSize() const {
478 return fPositions.size_bytes() + fIDsOrDrawables.size_bytes();
479}
480
481void DrawableOpSubmitter::flatten(SkWriteBuffer& buffer) const {
482 fStrikePromise.flatten(buffer);
483
484 buffer.writeScalar(fStrikeToSourceScale);
485 buffer.writePointArray(fPositions.data(), SkCount(fPositions));
486 for (IDOrDrawable idOrDrawable : fIDsOrDrawables) {
487 buffer.writeInt(idOrDrawable.fGlyphID);
488 }
489}
490
491std::optional<DrawableOpSubmitter> DrawableOpSubmitter::MakeFromBuffer(
492 SkReadBuffer& buffer, SubRunAllocator* alloc, const SkStrikeClient* client) {
493 std::optional<SkStrikePromise> strikePromise =
494 SkStrikePromise::MakeFromBuffer(buffer, client, SkStrikeCache::GlobalStrikeCache());
495 if (!buffer.validate(strikePromise.has_value())) {
496 return std::nullopt;
497 }
498
499 SkScalar strikeToSourceScale = buffer.readScalar();
500 if (!buffer.validate(0 < strikeToSourceScale)) { return std::nullopt; }
501
502 SkSpan<SkPoint> positions = MakePointsFromBuffer(buffer, alloc);
503 if (positions.empty()) { return std::nullopt; }
504 const int glyphCount = SkCount(positions);
505
506 if (!buffer.validateCanReadN<int>(glyphCount)) { return std::nullopt; }
507 auto idsOrDrawables = alloc->makePODArray<IDOrDrawable>(glyphCount);
508 for (int i = 0; i < SkToInt(glyphCount); ++i) {
509 // Remember, we stored an int for glyph id.
510 idsOrDrawables[i].fGlyphID = SkTo<SkGlyphID>(buffer.readInt());
511 }
512
513 SkASSERT(buffer.isValid());
514 return DrawableOpSubmitter{strikeToSourceScale,
515 positions,
516 SkSpan(idsOrDrawables, glyphCount),
517 std::move(strikePromise.value())};
518}
519
520DrawableOpSubmitter::DrawableOpSubmitter(
521 SkScalar strikeToSourceScale,
522 SkSpan<SkPoint> positions,
523 SkSpan<IDOrDrawable> idsOrDrawables,
524 SkStrikePromise&& strikePromise)
525 : fStrikeToSourceScale{strikeToSourceScale}
526 , fPositions{positions}
527 , fIDsOrDrawables{idsOrDrawables}
528 , fStrikePromise(std::move(strikePromise)) {
529 SkASSERT(!fPositions.empty());
530}
531
532void
533DrawableOpSubmitter::submitDraws(SkCanvas* canvas, SkPoint drawOrigin,const SkPaint& paint) const {
534 // Convert glyph IDs to Drawables if it hasn't been done yet.
535 fConvertIDsToDrawables([&]() {
536 fStrikePromise.strike()->glyphIDsToDrawables(fIDsOrDrawables);
537 // Do not call resetStrike() because the strike must remain owned to ensure the Drawable
538 // data is not freed.
539 });
540
541 // Calculate the matrix that maps the path glyphs from their size in the strike to
542 // the graphics source space.
543 SkMatrix strikeToSource = SkMatrix::Scale(fStrikeToSourceScale, fStrikeToSourceScale);
544 strikeToSource.postTranslate(drawOrigin.x(), drawOrigin.y());
545
546 // Transform the path to device because the deviceMatrix must be unchanged to
547 // draw effect, filter or shader paths.
548 for (auto [i, position] : SkMakeEnumerate(fPositions)) {
549 SkDrawable* drawable = fIDsOrDrawables[i].fDrawable;
550
551 if (drawable == nullptr) {
552 // This better be pinned to keep the drawable data alive.
553 fStrikePromise.strike()->verifyPinnedStrike();
554 SkDEBUGFAIL("Drawable should not be nullptr.");
555 continue;
556 }
557
558 // Transform the glyph to source space.
559 SkMatrix pathMatrix = strikeToSource;
560 pathMatrix.postTranslate(position.x(), position.y());
561
562 SkAutoCanvasRestore acr(canvas, false);
563 SkRect drawableBounds = drawable->getBounds();
564 pathMatrix.mapRect(&drawableBounds);
565 canvas->saveLayer(&drawableBounds, &paint);
566 drawable->draw(canvas, &pathMatrix);
567 }
568}
569
570// -- DrawableSubRun -------------------------------------------------------------------------------
571class DrawableSubRun : public SubRun {
572public:
573 DrawableSubRun(DrawableOpSubmitter&& drawingDrawing)
574 : fDrawingDrawing(std::move(drawingDrawing)) {}
575
577 SkScalar strikeToSourceScale,
578 SkStrikePromise&& strikePromise,
579 SubRunAllocator* alloc) {
580 return alloc->makeUnique<DrawableSubRun>(
582 strikeToSourceScale,
583 std::move(strikePromise),
584 alloc));
585 }
586
588 SubRunAllocator* alloc,
589 const SkStrikeClient* client);
590
591 void draw(SkCanvas* canvas,
592 SkPoint drawOrigin,
593 const SkPaint& paint,
595 const AtlasDrawDelegate&) const override {
596 fDrawingDrawing.submitDraws(canvas, drawOrigin, paint);
597 }
598
599 int unflattenSize() const override;
600
601 bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const override;
602
603 const AtlasSubRun* testingOnly_atlasSubRun() const override;
604
605protected:
606 SubRunStreamTag subRunStreamTag() const override { return SubRunStreamTag::kDrawableStreamTag; }
607 void doFlatten(SkWriteBuffer& buffer) const override;
608
609private:
610 DrawableOpSubmitter fDrawingDrawing;
611};
612
613int DrawableSubRun::unflattenSize() const {
614 return sizeof(DrawableSubRun) + fDrawingDrawing.unflattenSize();
615}
616
617void DrawableSubRun::doFlatten(SkWriteBuffer& buffer) const {
618 fDrawingDrawing.flatten(buffer);
619}
620
621SubRunOwner DrawableSubRun::MakeFromBuffer(SkReadBuffer& buffer,
622 SubRunAllocator* alloc,
623 const SkStrikeClient* client) {
624 auto drawableOpSubmitter = DrawableOpSubmitter::MakeFromBuffer(buffer, alloc, client);
625 if (!buffer.validate(drawableOpSubmitter.has_value())) { return nullptr; }
626 return alloc->makeUnique<DrawableSubRun>(std::move(*drawableOpSubmitter));
627}
628
629bool DrawableSubRun::canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const {
630 return true;
631}
632
633const AtlasSubRun* DrawableSubRun::testingOnly_atlasSubRun() const {
634 return nullptr;
635}
636
637#if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
638enum ClipMethod {
639 kClippedOut,
640 kUnclipped,
641 kGPUClipped,
642 kGeometryClipped
643};
644
645std::tuple<ClipMethod, SkIRect>
646calculate_clip(const GrClip* clip, SkRect deviceBounds, SkRect glyphBounds) {
647 if (clip == nullptr && !deviceBounds.intersects(glyphBounds)) {
648 return {kClippedOut, SkIRect::MakeEmpty()};
649 } else if (clip != nullptr) {
650 switch (auto result = clip->preApply(glyphBounds, GrAA::kNo); result.fEffect) {
652 return {kClippedOut, SkIRect::MakeEmpty()};
654 return {kUnclipped, SkIRect::MakeEmpty()};
656 if (result.fIsRRect && result.fRRect.isRect()) {
657 SkRect r = result.fRRect.rect();
658 if (result.fAA == GrAA::kNo || GrClip::IsPixelAligned(r)) {
660 // Clip geometrically during onPrepare using clipRect.
661 r.round(&clipRect);
662 if (clipRect.contains(glyphBounds)) {
663 // If fully within the clip, signal no clipping using the empty rect.
664 return {kUnclipped, SkIRect::MakeEmpty()};
665 }
666 // Use the clipRect to clip the geometry.
667 return {kGeometryClipped, clipRect};
668 }
669 // Partial pixel clipped at this point. Have the GPU handle it.
670 }
671 }
672 break;
673 }
674 }
675 return {kGPUClipped, SkIRect::MakeEmpty()};
676}
677#endif // defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
678
679// -- DirectMaskSubRun -----------------------------------------------------------------------------
680class DirectMaskSubRun final : public SubRun, public AtlasSubRun {
681public:
682 DirectMaskSubRun(VertexFiller&& vertexFiller,
684 : fVertexFiller{std::move(vertexFiller)}
685 , fGlyphs{std::move(glyphs)} {}
686
687 static SubRunOwner Make(SkRect creationBounds,
689 const SkMatrix& creationMatrix,
690 SkStrikePromise&& strikePromise,
691 MaskFormat maskType,
692 SubRunAllocator* alloc) {
693 auto vertexFiller = VertexFiller::Make(maskType,
694 creationMatrix,
695 creationBounds,
696 get_positions(accepted),
697 alloc,
698 kIsDirect);
699
700 auto glyphVector =
701 GlyphVector::Make(std::move(strikePromise), get_packedIDs(accepted), alloc);
702
703 return alloc->makeUnique<DirectMaskSubRun>(std::move(vertexFiller), std::move(glyphVector));
704 }
705
707 SubRunAllocator* alloc,
708 const SkStrikeClient* client) {
709 auto vertexFiller = VertexFiller::MakeFromBuffer(buffer, alloc);
710 if (!buffer.validate(vertexFiller.has_value())) { return nullptr; }
711
712 auto glyphVector = GlyphVector::MakeFromBuffer(buffer, client, alloc);
713 if (!buffer.validate(glyphVector.has_value())) { return nullptr; }
714 if (!buffer.validate(SkCount(glyphVector->glyphs()) == vertexFiller->count())) {
715 return nullptr;
716 }
717
718 SkASSERT(buffer.isValid());
719 return alloc->makeUnique<DirectMaskSubRun>(
720 std::move(*vertexFiller), std::move(*glyphVector));
721 }
722
723 void draw(SkCanvas*,
724 SkPoint drawOrigin,
725 const SkPaint& paint,
726 sk_sp<SkRefCnt> subRunStorage,
727 const AtlasDrawDelegate& drawAtlas) const override {
728 drawAtlas(this, drawOrigin, paint, std::move(subRunStorage),
729 {/* isSDF = */false, fVertexFiller.isLCD()});
730 }
731
732 int unflattenSize() const override {
733 return sizeof(DirectMaskSubRun) +
734 fGlyphs.unflattenSize() +
735 fVertexFiller.unflattenSize();
736 }
737
738 int glyphCount() const override {
739 return SkCount(fGlyphs.glyphs());
740 }
741
742 SkSpan<const Glyph*> glyphs() const override {
743 return fGlyphs.glyphs();
744 }
745
746 MaskFormat maskFormat() const override { return fVertexFiller.grMaskType(); }
747
748 int glyphSrcPadding() const override { return 0; }
749
750 unsigned short instanceFlags() const override {
751 return (unsigned short)fVertexFiller.grMaskType();
752 }
753
755 fGlyphs.packedGlyphIDToGlyph(cache);
756 }
757
758#if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
759 size_t vertexStride(const SkMatrix& drawMatrix) const override {
760 return fVertexFiller.vertexStride(drawMatrix);
761 }
762
763 std::tuple<const GrClip*, GrOp::Owner> makeAtlasTextOp(
764 const GrClip* clip,
765 const SkMatrix& viewMatrix,
766 SkPoint drawOrigin,
767 const SkPaint& paint,
768 sk_sp<SkRefCnt>&& subRunStorage,
769 skgpu::ganesh::SurfaceDrawContext* sdc) const override {
770 SkASSERT(this->glyphCount() != 0);
771 const SkMatrix& positionMatrix = position_matrix(viewMatrix, drawOrigin);
772
773 auto [integerTranslate, subRunDeviceBounds] =
774 fVertexFiller.deviceRectAndCheckTransform(positionMatrix);
775 if (subRunDeviceBounds.isEmpty()) {
776 return {nullptr, nullptr};
777 }
778 // Rect for optimized bounds clipping when doing an integer translate.
779 SkIRect geometricClipRect = SkIRect::MakeEmpty();
780 if (integerTranslate) {
781 // We can clip geometrically using clipRect and ignore clip when an axis-aligned
782 // rectangular non-AA clip is used. If clipRect is empty, and clip is nullptr, then
783 // there is no clipping needed.
784 const SkRect deviceBounds = SkRect::MakeWH(sdc->width(), sdc->height());
785 auto [clipMethod, clipRect] = calculate_clip(clip, deviceBounds, subRunDeviceBounds);
786
787 switch (clipMethod) {
788 case kClippedOut:
789 // Returning nullptr as op means skip this op.
790 return {nullptr, nullptr};
791 case kUnclipped:
792 case kGeometryClipped:
793 // GPU clip is not needed.
794 clip = nullptr;
795 break;
796 case kGPUClipped:
797 // Use th GPU clip; clipRect is ignored.
798 break;
799 }
800 geometricClipRect = clipRect;
801
802 if (!geometricClipRect.isEmpty()) { SkASSERT(clip == nullptr); }
803 }
804
805 GrPaint grPaint;
806 const SkPMColor4f drawingColor = calculate_colors(sdc,
807 paint,
808 viewMatrix,
809 fVertexFiller.grMaskType(),
810 &grPaint);
811
812 auto geometry = AtlasTextOp::Geometry::Make(*this,
813 viewMatrix,
814 drawOrigin,
815 geometricClipRect,
816 std::move(subRunStorage),
817 drawingColor,
818 sdc->arenaAlloc());
819
820 GrRecordingContext* const rContext = sdc->recordingContext();
821
822 GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext,
823 fVertexFiller.opMaskType(),
824 !integerTranslate,
825 this->glyphCount(),
826 subRunDeviceBounds,
827 geometry,
828 sdc->colorInfo(),
829 std::move(grPaint));
830 return {clip, std::move(op)};
831 }
832
833 void fillVertexData(void* vertexDst, int offset, int count,
835 const SkMatrix& drawMatrix, SkPoint drawOrigin,
836 SkIRect clip) const override {
837 const SkMatrix positionMatrix = position_matrix(drawMatrix, drawOrigin);
838 fVertexFiller.fillVertexData(offset, count,
839 fGlyphs.glyphs(),
840 color,
841 positionMatrix,
842 clip,
843 vertexDst);
844 }
845#endif // defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
846
847 std::tuple<bool, int> regenerateAtlas(int begin, int end,
848 RegenerateAtlasDelegate regenerateAtlas) const override {
849 return regenerateAtlas(
850 &fGlyphs, begin, end, fVertexFiller.grMaskType(), this->glyphSrcPadding());
851 }
852
853 const VertexFiller& vertexFiller() const override { return fVertexFiller; }
854
855 bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const override {
856 auto [reuse, _] = fVertexFiller.deviceRectAndCheckTransform(positionMatrix);
857 return reuse;
858 }
859
860 const AtlasSubRun* testingOnly_atlasSubRun() const override {
861 return this;
862 }
863
864protected:
865 SubRunStreamTag subRunStreamTag() const override {
867 }
868
869 void doFlatten(SkWriteBuffer& buffer) const override {
870 fVertexFiller.flatten(buffer);
871 fGlyphs.flatten(buffer);
872 }
873
874private:
875 const VertexFiller fVertexFiller;
876
877 // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
878 // be single threaded.
879 mutable GlyphVector fGlyphs;
880};
881
882// -- TransformedMaskSubRun ------------------------------------------------------------------------
883class TransformedMaskSubRun final : public SubRun, public AtlasSubRun {
884public:
885 TransformedMaskSubRun(bool isBigEnough,
886 VertexFiller&& vertexFiller,
888 : fIsBigEnough{isBigEnough}
889 , fVertexFiller{std::move(vertexFiller)}
890 , fGlyphs{std::move(glyphs)} {}
891
893 const SkMatrix& initialPositionMatrix,
894 SkStrikePromise&& strikePromise,
895 SkMatrix creationMatrix,
896 SkRect creationBounds,
897 MaskFormat maskType,
898 SubRunAllocator* alloc) {
899 auto vertexFiller = VertexFiller::Make(maskType,
900 creationMatrix,
901 creationBounds,
902 get_positions(accepted),
903 alloc,
905
906 auto glyphVector = GlyphVector::Make(
907 std::move(strikePromise), get_packedIDs(accepted), alloc);
908
909 return alloc->makeUnique<TransformedMaskSubRun>(
910 initialPositionMatrix.getMaxScale() >= 1,
911 std::move(vertexFiller),
912 std::move(glyphVector));
913 }
914
916 SubRunAllocator* alloc,
917 const SkStrikeClient* client) {
918 auto vertexFiller = VertexFiller::MakeFromBuffer(buffer, alloc);
919 if (!buffer.validate(vertexFiller.has_value())) { return nullptr; }
920
921 auto glyphVector = GlyphVector::MakeFromBuffer(buffer, client, alloc);
922 if (!buffer.validate(glyphVector.has_value())) { return nullptr; }
923 if (!buffer.validate(SkCount(glyphVector->glyphs()) == vertexFiller->count())) {
924 return nullptr;
925 }
926 const bool isBigEnough = buffer.readBool();
927 return alloc->makeUnique<TransformedMaskSubRun>(
928 isBigEnough, std::move(*vertexFiller), std::move(*glyphVector));
929 }
930
931 int unflattenSize() const override {
932 return sizeof(TransformedMaskSubRun) +
933 fGlyphs.unflattenSize() +
934 fVertexFiller.unflattenSize();
935 }
936
937 bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const override {
938 // If we are not scaling the cache entry to be larger, than a cache with smaller glyphs may
939 // be better.
940 return fIsBigEnough;
941 }
942
943 const AtlasSubRun* testingOnly_atlasSubRun() const override { return this; }
944
945 void testingOnly_packedGlyphIDToGlyph(StrikeCache *cache) const override {
946 fGlyphs.packedGlyphIDToGlyph(cache);
947 }
948
949 int glyphCount() const override { return SkCount(fGlyphs.glyphs()); }
950
951 SkSpan<const Glyph*> glyphs() const override {
952 return fGlyphs.glyphs();
953 }
954
955 unsigned short instanceFlags() const override {
956 return (unsigned short)fVertexFiller.grMaskType();
957 }
958
959 MaskFormat maskFormat() const override { return fVertexFiller.grMaskType(); }
960
961 int glyphSrcPadding() const override { return 1; }
962
963 void draw(SkCanvas*,
964 SkPoint drawOrigin,
965 const SkPaint& paint,
966 sk_sp<SkRefCnt> subRunStorage,
967 const AtlasDrawDelegate& drawAtlas) const override {
968 drawAtlas(this, drawOrigin, paint, std::move(subRunStorage),
969 {/* isSDF = */false, fVertexFiller.isLCD()});
970 }
971
972#if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
973
974 size_t vertexStride(const SkMatrix& drawMatrix) const override {
975 return fVertexFiller.vertexStride(drawMatrix);
976 }
977
978 std::tuple<const GrClip*, GrOp::Owner> makeAtlasTextOp(
979 const GrClip* clip,
980 const SkMatrix& viewMatrix,
981 SkPoint drawOrigin,
982 const SkPaint& paint,
983 sk_sp<SkRefCnt>&& subRunStorage,
984 skgpu::ganesh::SurfaceDrawContext* sdc) const override {
985 SkASSERT(this->glyphCount() != 0);
986
987 GrPaint grPaint;
988 SkPMColor4f drawingColor = calculate_colors(sdc,
989 paint,
990 viewMatrix,
991 fVertexFiller.grMaskType(),
992 &grPaint);
993
994 auto geometry = AtlasTextOp::Geometry::Make(*this,
995 viewMatrix,
996 drawOrigin,
998 std::move(subRunStorage),
999 drawingColor,
1000 sdc->arenaAlloc());
1001
1002 GrRecordingContext* const rContext = sdc->recordingContext();
1003 SkMatrix positionMatrix = position_matrix(viewMatrix, drawOrigin);
1004 auto [_, deviceRect] = fVertexFiller.deviceRectAndCheckTransform(positionMatrix);
1005 GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext,
1006 fVertexFiller.opMaskType(),
1007 true,
1008 this->glyphCount(),
1009 deviceRect,
1010 geometry,
1011 sdc->colorInfo(),
1012 std::move(grPaint));
1013 return {clip, std::move(op)};
1014 }
1015
1016 void fillVertexData(
1017 void* vertexDst, int offset, int count,
1018 GrColor color,
1019 const SkMatrix& drawMatrix, SkPoint drawOrigin,
1020 SkIRect clip) const override {
1021 const SkMatrix positionMatrix = position_matrix(drawMatrix, drawOrigin);
1022 fVertexFiller.fillVertexData(offset, count,
1023 fGlyphs.glyphs(),
1024 color,
1025 positionMatrix,
1026 clip,
1027 vertexDst);
1028 }
1029#endif // defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
1030
1031 std::tuple<bool, int> regenerateAtlas(int begin, int end,
1032 RegenerateAtlasDelegate regenerateAtlas) const override {
1033 return regenerateAtlas(
1034 &fGlyphs, begin, end, fVertexFiller.grMaskType(), this->glyphSrcPadding());
1035 }
1036
1037 const VertexFiller& vertexFiller() const override { return fVertexFiller; }
1038
1039protected:
1040 SubRunStreamTag subRunStreamTag() const override {
1042 }
1043
1044 void doFlatten(SkWriteBuffer& buffer) const override {
1045 fVertexFiller.flatten(buffer);
1046 fGlyphs.flatten(buffer);
1047 buffer.writeBool(fIsBigEnough);
1048 }
1049
1050private:
1051 const bool fIsBigEnough;
1052
1053 const VertexFiller fVertexFiller;
1054
1055 // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
1056 // be single threaded.
1057 mutable GlyphVector fGlyphs;
1058}; // class TransformedMaskSubRun
1059
1060// -- SDFTSubRun -----------------------------------------------------------------------------------
1061
1062bool has_some_antialiasing(const SkFont& font ) {
1063 SkFont::Edging edging = font.getEdging();
1064 return edging == SkFont::Edging::kAntiAlias
1066}
1067
1068#if !defined(SK_DISABLE_SDF_TEXT)
1069
1070#if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
1071
1072static std::tuple<AtlasTextOp::MaskType, uint32_t, bool> calculate_sdf_parameters(
1074 const SkMatrix& drawMatrix,
1075 bool useLCDText,
1076 bool isAntiAliased) {
1077 const GrColorInfo& colorInfo = sdc.colorInfo();
1078 const SkSurfaceProps& props = sdc.surfaceProps();
1079 using MT = AtlasTextOp::MaskType;
1080 bool isLCD = useLCDText && props.pixelGeometry() != kUnknown_SkPixelGeometry;
1081 MT maskType = !isAntiAliased ? MT::kAliasedDistanceField
1082 : isLCD ? MT::kLCDDistanceField
1083 : MT::kGrayscaleDistanceField;
1084
1085 bool useGammaCorrectDistanceTable = colorInfo.isLinearlyBlended();
1086 uint32_t DFGPFlags = drawMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
1087 DFGPFlags |= drawMatrix.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0;
1088 DFGPFlags |= useGammaCorrectDistanceTable ? kGammaCorrect_DistanceFieldEffectFlag : 0;
1089 DFGPFlags |= MT::kAliasedDistanceField == maskType ? kAliased_DistanceFieldEffectFlag : 0;
1090 DFGPFlags |= drawMatrix.hasPerspective() ? kPerspective_DistanceFieldEffectFlag : 0;
1091
1092 if (isLCD) {
1093 bool isBGR = SkPixelGeometryIsBGR(props.pixelGeometry());
1094 bool isVertical = SkPixelGeometryIsV(props.pixelGeometry());
1096 DFGPFlags |= isBGR ? kBGR_DistanceFieldEffectFlag : 0;
1097 DFGPFlags |= isVertical ? kPortrait_DistanceFieldEffectFlag : 0;
1098 }
1099 return {maskType, DFGPFlags, useGammaCorrectDistanceTable};
1100}
1101
1102#endif // defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
1103
1104class SDFTSubRun final : public SubRun, public AtlasSubRun {
1105public:
1106 SDFTSubRun(bool useLCDText,
1107 bool antiAliased,
1108 const SDFTMatrixRange& matrixRange,
1109 VertexFiller&& vertexFiller,
1111 : fUseLCDText{useLCDText}
1112 , fAntiAliased{antiAliased}
1113 , fMatrixRange{matrixRange}
1114 , fVertexFiller{std::move(vertexFiller)}
1115 , fGlyphs{std::move(glyphs)} { }
1116
1118 const SkFont& runFont,
1119 SkStrikePromise&& strikePromise,
1120 const SkMatrix& creationMatrix,
1121 SkRect creationBounds,
1122 const SDFTMatrixRange& matrixRange,
1123 SubRunAllocator* alloc) {
1124 auto vertexFiller = VertexFiller::Make(MaskFormat::kA8,
1125 creationMatrix,
1126 creationBounds,
1127 get_positions(accepted),
1128 alloc,
1130
1131 auto glyphVector = GlyphVector::Make(
1132 std::move(strikePromise), get_packedIDs(accepted), alloc);
1133
1134 return alloc->makeUnique<SDFTSubRun>(
1136 has_some_antialiasing(runFont),
1137 matrixRange,
1138 std::move(vertexFiller),
1139 std::move(glyphVector));
1140 }
1141
1143 SubRunAllocator* alloc,
1144 const SkStrikeClient* client) {
1145 int useLCD = buffer.readInt();
1146 int isAntiAliased = buffer.readInt();
1148 auto vertexFiller = VertexFiller::MakeFromBuffer(buffer, alloc);
1149 if (!buffer.validate(vertexFiller.has_value())) { return nullptr; }
1150 if (!buffer.validate(vertexFiller.value().grMaskType() == MaskFormat::kA8)) {
1151 return nullptr;
1152 }
1153 auto glyphVector = GlyphVector::MakeFromBuffer(buffer, client, alloc);
1154 if (!buffer.validate(glyphVector.has_value())) { return nullptr; }
1155 if (!buffer.validate(SkCount(glyphVector->glyphs()) == vertexFiller->count())) {
1156 return nullptr;
1157 }
1158 return alloc->makeUnique<SDFTSubRun>(useLCD,
1159 isAntiAliased,
1160 matrixRange,
1161 std::move(*vertexFiller),
1162 std::move(*glyphVector));
1163 }
1164
1165 int unflattenSize() const override {
1166 return sizeof(SDFTSubRun) + fGlyphs.unflattenSize() + fVertexFiller.unflattenSize();
1167 }
1168
1169 bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const override {
1170 return fMatrixRange.matrixInRange(positionMatrix);
1171 }
1172
1173 const AtlasSubRun* testingOnly_atlasSubRun() const override { return this; }
1174
1175 void testingOnly_packedGlyphIDToGlyph(StrikeCache *cache) const override {
1176 fGlyphs.packedGlyphIDToGlyph(cache);
1177 }
1178
1179 int glyphCount() const override { return fVertexFiller.count(); }
1180 MaskFormat maskFormat() const override {
1181 SkASSERT(fVertexFiller.grMaskType() == MaskFormat::kA8);
1182 return MaskFormat::kA8;
1183 }
1184 int glyphSrcPadding() const override { return SK_DistanceFieldInset; }
1185
1186 SkSpan<const Glyph*> glyphs() const override {
1187 return fGlyphs.glyphs();
1188 }
1189
1190 unsigned short instanceFlags() const override {
1191 return (unsigned short)MaskFormat::kA8;
1192 }
1193
1194 void draw(SkCanvas*,
1195 SkPoint drawOrigin,
1196 const SkPaint& paint,
1197 sk_sp<SkRefCnt> subRunStorage,
1198 const AtlasDrawDelegate& drawAtlas) const override {
1199 drawAtlas(this, drawOrigin, paint, std::move(subRunStorage),
1200 {/* isSDF = */true, /* isLCD = */fUseLCDText});
1201 }
1202
1203#if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
1204 size_t vertexStride(const SkMatrix& drawMatrix) const override {
1205 return fVertexFiller.vertexStride(drawMatrix);
1206 }
1207
1208 std::tuple<const GrClip*, GrOp::Owner> makeAtlasTextOp(
1209 const GrClip* clip,
1210 const SkMatrix& viewMatrix,
1211 SkPoint drawOrigin,
1212 const SkPaint& paint,
1213 sk_sp<SkRefCnt>&& subRunStorage,
1214 skgpu::ganesh::SurfaceDrawContext* sdc) const override {
1215 SkASSERT(this->glyphCount() != 0);
1216
1217 GrPaint grPaint;
1218 SkPMColor4f drawingColor = calculate_colors(sdc,
1219 paint,
1220 viewMatrix,
1221 MaskFormat::kA8,
1222 &grPaint);
1223
1224 auto [maskType, DFGPFlags, useGammaCorrectDistanceTable] =
1225 calculate_sdf_parameters(*sdc, viewMatrix, fUseLCDText, fAntiAliased);
1226
1227 auto geometry = AtlasTextOp::Geometry::Make(*this,
1228 viewMatrix,
1229 drawOrigin,
1231 std::move(subRunStorage),
1232 drawingColor,
1233 sdc->arenaAlloc());
1234
1235 GrRecordingContext* const rContext = sdc->recordingContext();
1236 SkMatrix positionMatrix = position_matrix(viewMatrix, drawOrigin);
1237 auto [_, deviceRect] = fVertexFiller.deviceRectAndCheckTransform(positionMatrix);
1238 GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext,
1239 maskType,
1240 true,
1241 this->glyphCount(),
1242 deviceRect,
1244 useGammaCorrectDistanceTable,
1245 DFGPFlags,
1246 geometry,
1247 std::move(grPaint));
1248
1249 return {clip, std::move(op)};
1250 }
1251
1252 void fillVertexData(
1253 void *vertexDst, int offset, int count,
1254 GrColor color,
1255 const SkMatrix& drawMatrix, SkPoint drawOrigin,
1256 SkIRect clip) const override {
1257 const SkMatrix positionMatrix = position_matrix(drawMatrix, drawOrigin);
1258
1259 fVertexFiller.fillVertexData(offset, count,
1260 fGlyphs.glyphs(),
1261 color,
1262 positionMatrix,
1263 clip,
1264 vertexDst);
1265 }
1266
1267#endif // defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
1268
1269 std::tuple<bool, int> regenerateAtlas(int begin, int end,
1270 RegenerateAtlasDelegate regenerateAtlas) const override {
1271 return regenerateAtlas(&fGlyphs, begin, end, MaskFormat::kA8, this->glyphSrcPadding());
1272 }
1273
1274 const VertexFiller& vertexFiller() const override { return fVertexFiller; }
1275
1276protected:
1277 SubRunStreamTag subRunStreamTag() const override { return SubRunStreamTag::kSDFTStreamTag; }
1278 void doFlatten(SkWriteBuffer& buffer) const override {
1279 buffer.writeInt(fUseLCDText);
1280 buffer.writeInt(fAntiAliased);
1281 fMatrixRange.flatten(buffer);
1282 fVertexFiller.flatten(buffer);
1283 fGlyphs.flatten(buffer);
1284 }
1285
1286private:
1287 const bool fUseLCDText;
1288 const bool fAntiAliased;
1289 const SDFTMatrixRange fMatrixRange;
1290
1291 const VertexFiller fVertexFiller;
1292
1293 // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must
1294 // be single threaded.
1295 mutable GlyphVector fGlyphs;
1296}; // class SDFTSubRun
1297
1298#endif // !defined(SK_DISABLE_SDF_TEXT)
1299
1300// -- SubRun ---------------------------------------------------------------------------------------
1301
1302template<typename AddSingleMaskFormat>
1303void add_multi_mask_format(
1304 AddSingleMaskFormat addSingleMaskFormat,
1306 if (accepted.empty()) { return; }
1307
1308 auto maskSpan = accepted.get<2>();
1310 size_t startIndex = 0;
1311 for (size_t i = 1; i < accepted.size(); i++) {
1312 MaskFormat nextFormat = Glyph::FormatFromSkGlyph(maskSpan[i]);
1313 if (format != nextFormat) {
1314 auto interval = accepted.subspan(startIndex, i - startIndex);
1315 // Only pass the packed glyph ids and positions.
1316 auto glyphsWithSameFormat = SkMakeZip(interval.get<0>(), interval.get<1>());
1317 // Take a ref on the strike. This should rarely happen.
1318 addSingleMaskFormat(glyphsWithSameFormat, format);
1319 format = nextFormat;
1320 startIndex = i;
1321 }
1322 }
1323 auto interval = accepted.last(accepted.size() - startIndex);
1324 auto glyphsWithSameFormat = SkMakeZip(interval.get<0>(), interval.get<1>());
1325 addSingleMaskFormat(glyphsWithSameFormat, format);
1326}
1327} // namespace
1328
1329namespace sktext::gpu {
1330SubRun::~SubRun() = default;
1332 buffer.writeInt(this->subRunStreamTag());
1333 this->doFlatten(buffer);
1334}
1335
1337 SubRunAllocator* alloc,
1338 const SkStrikeClient* client) {
1339 using Maker = SubRunOwner (*)(SkReadBuffer&,
1341 const SkStrikeClient*);
1342
1343 static Maker makers[kSubRunStreamTagCount] = {
1344 nullptr, // 0 index is bad.
1345 DirectMaskSubRun::MakeFromBuffer,
1346#if !defined(SK_DISABLE_SDF_TEXT)
1347 SDFTSubRun::MakeFromBuffer,
1348#endif
1349 TransformedMaskSubRun::MakeFromBuffer,
1350 PathSubRun::MakeFromBuffer,
1351 DrawableSubRun::MakeFromBuffer,
1352 };
1353 int subRunTypeInt = buffer.readInt();
1354 SkASSERT(kBad < subRunTypeInt && subRunTypeInt < kSubRunStreamTagCount);
1355 if (!buffer.validate(kBad < subRunTypeInt && subRunTypeInt < kSubRunStreamTagCount)) {
1356 return nullptr;
1357 }
1358 auto maker = makers[subRunTypeInt];
1359 if (!buffer.validate(maker != nullptr)) { return nullptr; }
1360 return maker(buffer, alloc, client);
1361}
1362
1363// -- SubRunContainer ------------------------------------------------------------------------------
1364SubRunContainer::SubRunContainer(const SkMatrix& initialPositionMatrix)
1365 : fInitialPositionMatrix{initialPositionMatrix} {}
1366
1368 int unflattenSizeHint = 0;
1369 for (auto& subrun : fSubRuns) {
1370 unflattenSizeHint += subrun.unflattenSize();
1371 }
1372 buffer.writeInt(unflattenSizeHint);
1373}
1374
1376 int subRunsSizeHint = buffer.readInt();
1377
1378 // Since the hint doesn't affect correctness, if it looks fishy just pick a reasonable
1379 // value.
1380 if (subRunsSizeHint < 0 || (1 << 16) < subRunsSizeHint) {
1381 subRunsSizeHint = 128;
1382 }
1383 return subRunsSizeHint;
1384}
1385
1387 buffer.writeMatrix(fInitialPositionMatrix);
1388 int subRunCount = 0;
1389 for ([[maybe_unused]] auto& subRun : fSubRuns) {
1390 subRunCount += 1;
1391 }
1392 buffer.writeInt(subRunCount);
1393 for (auto& subRun : fSubRuns) {
1394 subRun.flatten(buffer);
1395 }
1396}
1397
1399 const SkStrikeClient* client,
1400 SubRunAllocator* alloc) {
1401 SkMatrix positionMatrix;
1402 buffer.readMatrix(&positionMatrix);
1403 if (!buffer.isValid()) { return nullptr; }
1404 SubRunContainerOwner container = alloc->makeUnique<SubRunContainer>(positionMatrix);
1405
1406 int subRunCount = buffer.readInt();
1407 SkASSERT(subRunCount > 0);
1408 if (!buffer.validate(subRunCount > 0)) { return nullptr; }
1409 for (int i = 0; i < subRunCount; ++i) {
1410 auto subRunOwner = SubRun::MakeFromBuffer(buffer, alloc, client);
1411 if (!buffer.validate(subRunOwner != nullptr)) { return nullptr; }
1412 if (subRunOwner != nullptr) {
1413 container->fSubRuns.append(std::move(subRunOwner));
1414 }
1415 }
1416 return container;
1417}
1418
1420 // The difference in alignment from the per-glyph data to the SubRun;
1421 constexpr size_t alignDiff = alignof(DirectMaskSubRun) - alignof(SkPoint);
1422 constexpr size_t vertexDataToSubRunPadding = alignDiff > 0 ? alignDiff : 0;
1423 size_t totalGlyphCount = glyphRunList.totalGlyphCount();
1424 // This is optimized for DirectMaskSubRun which is by far the most common case.
1425 return totalGlyphCount * sizeof(SkPoint)
1426 + GlyphVector::GlyphVectorSize(totalGlyphCount)
1427 + glyphRunList.runCount() * (sizeof(DirectMaskSubRun) + vertexDataToSubRunPadding)
1428 + sizeof(SubRunContainer);
1429}
1430
1432 StrikeMutationMonitor m{strike};
1433 SkScalar maxDimension = 0;
1434 for (SkGlyphID glyphID : glyphs) {
1435 SkGlyphDigest digest = strike->digestFor(kMask, SkPackedGlyphID{glyphID});
1436 maxDimension = std::max(static_cast<SkScalar>(digest.maxDimension()), maxDimension);
1437 }
1438
1439 return maxDimension;
1440}
1441
1442#if !defined(SK_DISABLE_SDF_TEXT)
1443std::tuple<SkZip<const SkPackedGlyphID, const SkPoint>, SkZip<SkGlyphID, SkPoint>, SkRect>
1445 const SkMatrix& creationMatrix,
1447 SkZip<SkPackedGlyphID, SkPoint> acceptedBuffer,
1448 SkZip<SkGlyphID, SkPoint> rejectedBuffer) {
1449 int acceptedSize = 0,
1450 rejectedSize = 0;
1451 SkGlyphRect boundingRect = skglyph::empty_rect();
1452 StrikeMutationMonitor m{strike};
1453 for (const auto [glyphID, pos] : source) {
1454 if (!SkIsFinite(pos.x(), pos.y())) {
1455 continue;
1456 }
1457
1458 const SkPackedGlyphID packedID{glyphID};
1459 switch (const SkGlyphDigest digest = strike->digestFor(skglyph::kSDFT, packedID);
1460 digest.actionFor(skglyph::kSDFT)) {
1461 case GlyphAction::kAccept: {
1462 SkPoint mappedPos = creationMatrix.mapPoint(pos);
1463 const SkGlyphRect glyphBounds =
1464 digest.bounds()
1465 // The SDFT glyphs have 2-pixel wide padding that should
1466 // not be used in calculating the source rectangle.
1468 .offset(mappedPos);
1469 boundingRect = skglyph::rect_union(boundingRect, glyphBounds);
1470 acceptedBuffer[acceptedSize++] = std::make_tuple(packedID, glyphBounds.leftTop());
1471 break;
1472 }
1473 case GlyphAction::kReject:
1474 rejectedBuffer[rejectedSize++] = std::make_tuple(glyphID, pos);
1475 break;
1476 default:
1477 break;
1478 }
1479 }
1480
1481 return {acceptedBuffer.first(acceptedSize),
1482 rejectedBuffer.first(rejectedSize),
1483 boundingRect.rect()};
1484}
1485#endif
1486
1487std::tuple<SkZip<const SkPackedGlyphID, const SkPoint, const SkMask::Format>,
1489 SkRect>
1491 const SkMatrix& positionMatrix,
1494 SkZip<SkGlyphID, SkPoint> rejectedBuffer) {
1495 const SkIPoint mask = strike->roundingSpec().ignorePositionFieldMask;
1496 const SkPoint halfSampleFreq = strike->roundingSpec().halfAxisSampleFreq;
1497
1498 // Build up the mapping from source space to device space. Add the rounding constant
1499 // halfSampleFreq, so we just need to floor to get the device result.
1500 SkMatrix positionMatrixWithRounding = positionMatrix;
1501 positionMatrixWithRounding.postTranslate(halfSampleFreq.x(), halfSampleFreq.y());
1502
1503 int acceptedSize = 0,
1504 rejectedSize = 0;
1505 SkGlyphRect boundingRect = skglyph::empty_rect();
1506 StrikeMutationMonitor m{strike};
1507 for (auto [glyphID, pos] : source) {
1508 if (!SkIsFinite(pos.x(), pos.y())) {
1509 continue;
1510 }
1511
1512 const SkPoint mappedPos = positionMatrixWithRounding.mapPoint(pos);
1513 const SkPackedGlyphID packedID{glyphID, mappedPos, mask};
1514 switch (const SkGlyphDigest digest = strike->digestFor(skglyph::kDirectMask, packedID);
1516 case GlyphAction::kAccept: {
1517 const SkPoint roundedPos{SkScalarFloorToScalar(mappedPos.x()),
1518 SkScalarFloorToScalar(mappedPos.y())};
1519 const SkGlyphRect glyphBounds = digest.bounds().offset(roundedPos);
1520 boundingRect = skglyph::rect_union(boundingRect, glyphBounds);
1521 acceptedBuffer[acceptedSize++] =
1522 std::make_tuple(packedID, glyphBounds.leftTop(), digest.maskFormat());
1523 break;
1524 }
1525 case GlyphAction::kReject:
1526 rejectedBuffer[rejectedSize++] = std::make_tuple(glyphID, pos);
1527 break;
1528 default:
1529 break;
1530 }
1531 }
1532
1533 return {acceptedBuffer.first(acceptedSize),
1534 rejectedBuffer.first(rejectedSize),
1535 boundingRect.rect()};
1536}
1537
1538std::tuple<SkZip<const SkPackedGlyphID, const SkPoint, const SkMask::Format>,
1540 SkRect>
1542 const SkMatrix& creationMatrix,
1545 SkZip<SkGlyphID, SkPoint> rejectedBuffer) {
1546 int acceptedSize = 0,
1547 rejectedSize = 0;
1548 SkGlyphRect boundingRect = skglyph::empty_rect();
1549 StrikeMutationMonitor m{strike};
1550 for (auto [glyphID, pos] : source) {
1551 if (!SkIsFinite(pos.x(), pos.y())) {
1552 continue;
1553 }
1554
1555 const SkPackedGlyphID packedID{glyphID};
1556 switch (const SkGlyphDigest digest = strike->digestFor(kMask, packedID);
1557 digest.actionFor(kMask)) {
1558 case GlyphAction::kAccept: {
1559 const SkPoint mappedPos = creationMatrix.mapPoint(pos);
1560 const SkGlyphRect glyphBounds = digest.bounds().offset(mappedPos);
1561 boundingRect = skglyph::rect_union(boundingRect, glyphBounds);
1562 acceptedBuffer[acceptedSize++] =
1563 std::make_tuple(packedID, glyphBounds.leftTop(), digest.maskFormat());
1564 break;
1565 }
1566 case GlyphAction::kReject:
1567 rejectedBuffer[rejectedSize++] = std::make_tuple(glyphID, pos);
1568 break;
1569 default:
1570 break;
1571 }
1572 }
1573
1574 return {acceptedBuffer.first(acceptedSize),
1575 rejectedBuffer.first(rejectedSize),
1576 boundingRect.rect()};
1577}
1578
1579std::tuple<SkZip<const SkGlyphID, const SkPoint>, SkZip<SkGlyphID, SkPoint>>
1582 SkZip<SkGlyphID, SkPoint> acceptedBuffer,
1583 SkZip<SkGlyphID, SkPoint> rejectedBuffer) {
1584 int acceptedSize = 0;
1585 int rejectedSize = 0;
1586 StrikeMutationMonitor m{strike};
1587 for (const auto [glyphID, pos] : source) {
1588 if (!SkIsFinite(pos.x(), pos.y())) {
1589 continue;
1590 }
1591
1592 switch (strike->digestFor(skglyph::kPath, SkPackedGlyphID{glyphID})
1594 case GlyphAction::kAccept:
1595 acceptedBuffer[acceptedSize++] = std::make_tuple(glyphID, pos);
1596 break;
1597 case GlyphAction::kReject:
1598 rejectedBuffer[rejectedSize++] = std::make_tuple(glyphID, pos);
1599 break;
1600 default:
1601 break;
1602 }
1603 }
1604 return {acceptedBuffer.first(acceptedSize), rejectedBuffer.first(rejectedSize)};
1605}
1606
1607std::tuple<SkZip<const SkGlyphID, const SkPoint>, SkZip<SkGlyphID, SkPoint>>
1610 SkZip<SkGlyphID, SkPoint> acceptedBuffer,
1611 SkZip<SkGlyphID, SkPoint> rejectedBuffer) {
1612 int acceptedSize = 0;
1613 int rejectedSize = 0;
1614 StrikeMutationMonitor m{strike};
1615 for (const auto [glyphID, pos] : source) {
1616 if (!SkIsFinite(pos.x(), pos.y())) {
1617 continue;
1618 }
1619
1620 switch (strike->digestFor(skglyph::kDrawable, SkPackedGlyphID{glyphID})
1622 case GlyphAction::kAccept:
1623 acceptedBuffer[acceptedSize++] = std::make_tuple(glyphID, pos);
1624 break;
1625 case GlyphAction::kReject:
1626 rejectedBuffer[rejectedSize++] = std::make_tuple(glyphID, pos);
1627 break;
1628 default:
1629 break;
1630 }
1631 }
1632 return {acceptedBuffer.first(acceptedSize), rejectedBuffer.first(rejectedSize)};
1633}
1634
1635#if !defined(SK_DISABLE_SDF_TEXT)
1636static std::tuple<SkStrikeSpec, SkScalar, sktext::gpu::SDFTMatrixRange>
1638 const SkSurfaceProps& surfaceProps, const SkMatrix& deviceMatrix,
1639 const SkPoint& textLocation, const sktext::gpu::SDFTControl& control) {
1640 // Add filter to the paint which creates the SDFT data for A8 masks.
1641 SkPaint dfPaint{paint};
1642 dfPaint.setMaskFilter(sktext::gpu::SDFMaskFilter::Make());
1643
1644 auto [dfFont, strikeToSourceScale, matrixRange] = control.getSDFFont(font, deviceMatrix,
1645 textLocation);
1646
1647 // Adjust the stroke width by the scale factor for drawing the SDFT.
1648 dfPaint.setStrokeWidth(paint.getStrokeWidth() / strikeToSourceScale);
1649
1650 // Check for dashing and adjust the intervals.
1651 if (SkPathEffect* pathEffect = paint.getPathEffect(); pathEffect != nullptr) {
1652 SkPathEffect::DashInfo dashInfo;
1653 if (pathEffect->asADash(&dashInfo) == SkPathEffect::kDash_DashType) {
1654 if (dashInfo.fCount > 0) {
1655 // Allocate the intervals.
1656 std::vector<SkScalar> scaledIntervals(dashInfo.fCount);
1657 dashInfo.fIntervals = scaledIntervals.data();
1658 // Call again to get the interval data.
1659 (void)pathEffect->asADash(&dashInfo);
1660 for (SkScalar& interval : scaledIntervals) {
1661 interval /= strikeToSourceScale;
1662 }
1663 auto scaledDashes = SkDashPathEffect::Make(scaledIntervals.data(),
1664 scaledIntervals.size(),
1665 dashInfo.fPhase / strikeToSourceScale);
1666 dfPaint.setPathEffect(scaledDashes);
1667 }
1668 }
1669 }
1670
1671 // Fake-gamma and subpixel antialiasing are applied in the shader, so we ignore the
1672 // passed-in scaler context flags. (It's only used when we fall-back to bitmap text).
1674 SkStrikeSpec strikeSpec = SkStrikeSpec::MakeMask(dfFont, dfPaint, surfaceProps, flags,
1675 SkMatrix::I());
1676
1677 return std::make_tuple(std::move(strikeSpec), strikeToSourceScale, matrixRange);
1678}
1679#endif
1680
1682 const GlyphRunList& glyphRunList,
1683 const SkMatrix& positionMatrix,
1684 const SkPaint& runPaint,
1685 SkStrikeDeviceInfo strikeDeviceInfo,
1686 StrikeForGPUCacheInterface* strikeCache,
1687 SubRunAllocator* alloc,
1688 SubRunCreationBehavior creationBehavior,
1689 const char* tag) {
1690 SkASSERT(alloc != nullptr);
1691 SkASSERT(strikeDeviceInfo.fSDFTControl != nullptr);
1692
1693 SubRunContainerOwner container = alloc->makeUnique<SubRunContainer>(positionMatrix);
1694 // If there is no SDFT description ignore all SubRuns.
1695 if (strikeDeviceInfo.fSDFTControl == nullptr) {
1696 return container;
1697 }
1698
1699 const SkSurfaceProps deviceProps = strikeDeviceInfo.fSurfaceProps;
1700 const SkScalerContextFlags scalerContextFlags = strikeDeviceInfo.fScalerContextFlags;
1701#if !defined(SK_DISABLE_SDF_TEXT)
1702 const SDFTControl SDFTControl = *strikeDeviceInfo.fSDFTControl;
1703 const SkScalar maxMaskSize = SDFTControl.maxSize();
1704#else
1705 const SkScalar maxMaskSize = 256;
1706#endif
1707
1708 // TODO: hoist the buffer structure to the GlyphRunBuilder. The buffer structure here is
1709 // still begin tuned, and this is expected to be slower until tuned.
1710 const int maxGlyphRunSize = glyphRunList.maxGlyphRunSize();
1711
1712 // Accepted buffers.
1713 STArray<64, SkPackedGlyphID> acceptedPackedGlyphIDs;
1714 STArray<64, SkGlyphID> acceptedGlyphIDs;
1715 STArray<64, SkPoint> acceptedPositions;
1716 STArray<64, SkMask::Format> acceptedFormats;
1717 acceptedPackedGlyphIDs.resize(maxGlyphRunSize);
1718 acceptedGlyphIDs.resize(maxGlyphRunSize);
1719 acceptedPositions.resize(maxGlyphRunSize);
1720 acceptedFormats.resize(maxGlyphRunSize);
1721
1722 // Rejected buffers.
1723 STArray<64, SkGlyphID> rejectedGlyphIDs;
1724 STArray<64, SkPoint> rejectedPositions;
1725 rejectedGlyphIDs.resize(maxGlyphRunSize);
1726 rejectedPositions.resize(maxGlyphRunSize);
1727 const auto rejectedBuffer = SkMakeZip(rejectedGlyphIDs, rejectedPositions);
1728
1729 const SkPoint glyphRunListLocation = glyphRunList.sourceBounds().center();
1730
1731 // Handle all the runs in the glyphRunList
1732 for (auto& glyphRun : glyphRunList) {
1734 const SkFont& runFont = glyphRun.font();
1735
1736 const SkScalar approximateDeviceTextSize =
1737 // Since the positionMatrix has the origin prepended, use the plain
1738 // sourceBounds from above.
1739 SkFontPriv::ApproximateTransformedTextSize(runFont, positionMatrix,
1740 glyphRunListLocation);
1741
1742 // Atlas mask cases - SDFT and direct mask
1743 // Only consider using direct or SDFT drawing if not drawing hairlines and not too big.
1744 if ((runPaint.getStyle() != SkPaint::kStroke_Style || runPaint.getStrokeWidth() != 0) &&
1745 approximateDeviceTextSize < maxMaskSize) {
1746
1747#if !defined(SK_DISABLE_SDF_TEXT)
1748 // SDFT case
1749 if (SDFTControl.isSDFT(approximateDeviceTextSize, runPaint, positionMatrix)) {
1750 // Process SDFT - This should be the .009% case.
1751 const auto& [strikeSpec, strikeToSourceScale, matrixRange] =
1753 runFont, runPaint, deviceProps, positionMatrix,
1754 glyphRunListLocation, SDFTControl);
1755
1756 if (!SkScalarNearlyZero(strikeToSourceScale)) {
1757 sk_sp<StrikeForGPU> strike = strikeSpec.findOrCreateScopedStrike(strikeCache);
1758
1759 // The creationMatrix needs to scale the strike data when inverted and
1760 // multiplied by the positionMatrix. The final CTM should be:
1761 // [positionMatrix][scale by strikeToSourceScale],
1762 // which should equal the following because of the transform during the vertex
1763 // calculation,
1764 // [positionMatrix][creationMatrix]^-1.
1765 // So, the creation matrix needs to be
1766 // [scale by 1/strikeToSourceScale].
1767 SkMatrix creationMatrix =
1768 SkMatrix::Scale(1.f/strikeToSourceScale, 1.f/strikeToSourceScale);
1769
1770 auto acceptedBuffer = SkMakeZip(acceptedPackedGlyphIDs, acceptedPositions);
1771 auto [accepted, rejected, creationBounds] = prepare_for_SDFT_drawing(
1772 strike.get(), creationMatrix, source, acceptedBuffer, rejectedBuffer);
1773 source = rejected;
1774
1775 if (creationBehavior == kAddSubRuns && !accepted.empty()) {
1776 container->fSubRuns.append(SDFTSubRun::Make(
1777 accepted,
1778 runFont,
1779 strike->strikePromise(),
1780 creationMatrix,
1781 creationBounds,
1782 matrixRange,
1783 alloc));
1784 }
1785 }
1786 }
1787#endif // !defined(SK_DISABLE_SDF_TEXT)
1788
1789 // Direct Mask case
1790 // Handle all the directly mapped mask subruns.
1791 if (!source.empty() && !positionMatrix.hasPerspective()) {
1792 // Process masks including ARGB - this should be the 99.99% case.
1793 // This will handle medium size emoji that are sharing the run with SDFT drawn text.
1794 // If things are too big they will be passed along to the drawing of last resort
1795 // below.
1797 runFont, runPaint, deviceProps, scalerContextFlags, positionMatrix);
1798
1799 sk_sp<StrikeForGPU> strike = strikeSpec.findOrCreateScopedStrike(strikeCache);
1800
1801 auto acceptedBuffer = SkMakeZip(acceptedPackedGlyphIDs,
1802 acceptedPositions,
1803 acceptedFormats);
1804 auto [accepted, rejected, creationBounds] = prepare_for_direct_mask_drawing(
1805 strike.get(), positionMatrix, source, acceptedBuffer, rejectedBuffer);
1806 source = rejected;
1807
1808 if (creationBehavior == kAddSubRuns && !accepted.empty()) {
1809 auto addGlyphsWithSameFormat =
1810 [&, bounds = creationBounds](
1813 container->fSubRuns.append(
1815 subrun,
1816 container->initialPosition(),
1817 strike->strikePromise(),
1818 format,
1819 alloc));
1820 };
1821 add_multi_mask_format(addGlyphsWithSameFormat, accepted);
1822 }
1823 }
1824 }
1825
1826 // Drawable case
1827 // Handle all the drawable glyphs - usually large or perspective color glyphs.
1828 if (!source.empty()) {
1829 auto [strikeSpec, strikeToSourceScale] =
1830 SkStrikeSpec::MakePath(runFont, runPaint, deviceProps, scalerContextFlags);
1831
1832 if (!SkScalarNearlyZero(strikeToSourceScale)) {
1833 sk_sp<StrikeForGPU> strike = strikeSpec.findOrCreateScopedStrike(strikeCache);
1834
1835 auto acceptedBuffer = SkMakeZip(acceptedGlyphIDs, acceptedPositions);
1836 auto [accepted, rejected] =
1837 prepare_for_drawable_drawing(strike.get(), source, acceptedBuffer, rejectedBuffer);
1838 source = rejected;
1839
1840 if (creationBehavior == kAddSubRuns && !accepted.empty()) {
1841 container->fSubRuns.append(
1843 accepted,
1844 strikeToSourceScale,
1845 strike->strikePromise(),
1846 alloc));
1847 }
1848 }
1849 }
1850
1851 // Path case
1852 // Handle path subruns. Mainly, large or large perspective glyphs with no color.
1853 if (!source.empty()) {
1854 auto [strikeSpec, strikeToSourceScale] =
1855 SkStrikeSpec::MakePath(runFont, runPaint, deviceProps, scalerContextFlags);
1856
1857 if (!SkScalarNearlyZero(strikeToSourceScale)) {
1858 sk_sp<StrikeForGPU> strike = strikeSpec.findOrCreateScopedStrike(strikeCache);
1859
1860 auto acceptedBuffer = SkMakeZip(acceptedGlyphIDs, acceptedPositions);
1861 auto [accepted, rejected] =
1862 prepare_for_path_drawing(strike.get(), source, acceptedBuffer, rejectedBuffer);
1863 source = rejected;
1864
1865 if (creationBehavior == kAddSubRuns && !accepted.empty()) {
1866 container->fSubRuns.append(
1867 PathSubRun::Make(accepted,
1868 has_some_antialiasing(runFont),
1869 strikeToSourceScale,
1870 strike->strikePromise(),
1871 alloc));
1872 }
1873 }
1874 }
1875
1876 // Drawing of last resort case
1877 // Draw all the rest of the rejected glyphs from above. This scales out of the atlas to
1878 // the screen, so quality will suffer. This mainly handles large color or perspective
1879 // color not handled by Drawables.
1880 if (!source.empty() && !SkScalarNearlyZero(approximateDeviceTextSize)) {
1881 // Creation matrix will be changed below to meet the following criteria:
1882 // * No perspective - the font scaler and the strikes can't handle perspective masks.
1883 // * Fits atlas - creationMatrix will be conditioned so that the maximum glyph
1884 // dimension for this run will be < kMaxBilerpAtlasDimension.
1885 SkMatrix creationMatrix = positionMatrix;
1886
1887 // Condition creationMatrix for perspective.
1888 if (creationMatrix.hasPerspective()) {
1889 // Find a scale factor that reduces pixelation caused by keystoning.
1890 SkPoint center = glyphRunList.sourceBounds().center();
1891 SkScalar maxAreaScale = SkMatrixPriv::DifferentialAreaScale(creationMatrix, center);
1892 SkScalar perspectiveFactor = 1;
1893 if (SkIsFinite(maxAreaScale) && !SkScalarNearlyZero(maxAreaScale)) {
1894 perspectiveFactor = SkScalarSqrt(maxAreaScale);
1895 }
1896
1897 // Masks can not be created in perspective. Create a non-perspective font with a
1898 // scale that will support the perspective keystoning.
1899 creationMatrix = SkMatrix::Scale(perspectiveFactor, perspectiveFactor);
1900 }
1901
1902 // Reduce to make a one pixel border for the bilerp padding.
1903 static const constexpr SkScalar kMaxBilerpAtlasDimension =
1905
1906 // Get the raw glyph IDs to simulate device drawing to figure the maximum device
1907 // dimension.
1908 const SkSpan<const SkGlyphID> glyphs = get_glyphIDs(source);
1909
1910 // maxGlyphDimension always returns an integer even though the return type is SkScalar.
1911 auto maxGlyphDimension = [&](const SkMatrix& m) {
1913 runFont, runPaint, deviceProps, scalerContextFlags, m);
1914 const sk_sp<StrikeForGPU> gaugingStrike =
1915 strikeSpec.findOrCreateScopedStrike(strikeCache);
1916 const SkScalar maxDimension =
1917 find_maximum_glyph_dimension(gaugingStrike.get(), glyphs);
1918 // TODO: There is a problem where a small character (say .) and a large
1919 // character (say M) are in the same run. If the run is scaled to be very
1920 // large, then the M may return 0 because its dimensions are > 65535, but
1921 // the small character produces regular result because its largest dimension
1922 // is < 65535. This will create an improper scale factor causing the M to be
1923 // too large to fit in the atlas. Tracked by skia:13714.
1924 return maxDimension;
1925 };
1926
1927 // Condition the creationMatrix so that glyphs fit in the atlas.
1928 for (SkScalar maxDimension = maxGlyphDimension(creationMatrix);
1929 kMaxBilerpAtlasDimension < maxDimension;
1930 maxDimension = maxGlyphDimension(creationMatrix))
1931 {
1932 // The SkScalerContext has a limit of 65536 maximum dimension.
1933 // reductionFactor will always be < 1 because
1934 // maxDimension > kMaxBilerpAtlasDimension, and because maxDimension will always
1935 // be an integer the reduction factor will always be at most 254 / 255.
1936 SkScalar reductionFactor = kMaxBilerpAtlasDimension / maxDimension;
1937 creationMatrix.postScale(reductionFactor, reductionFactor);
1938 }
1939
1940 // Draw using the creationMatrix.
1942 runFont, runPaint, deviceProps, scalerContextFlags, creationMatrix);
1943
1944 sk_sp<StrikeForGPU> strike = strikeSpec.findOrCreateScopedStrike(strikeCache);
1945
1946 auto acceptedBuffer =
1947 SkMakeZip(acceptedPackedGlyphIDs, acceptedPositions, acceptedFormats);
1948 auto [accepted, rejected, creationBounds] =
1950 strike.get(), creationMatrix, source, acceptedBuffer, rejectedBuffer);
1951 source = rejected;
1952
1953 if (creationBehavior == kAddSubRuns && !accepted.empty()) {
1954
1955 auto addGlyphsWithSameFormat =
1956 [&, bounds = creationBounds](
1959 container->fSubRuns.append(
1961 container->initialPosition(),
1962 strike->strikePromise(),
1963 creationMatrix,
1964 bounds,
1965 format,
1966 alloc));
1967 };
1968 add_multi_mask_format(addGlyphsWithSameFormat, accepted);
1969 }
1970 }
1971 }
1972
1973 return container;
1974}
1975
1977 SkPoint drawOrigin,
1978 const SkPaint& paint,
1979 const SkRefCnt* subRunStorage,
1980 const AtlasDrawDelegate& atlasDelegate) const {
1981 for (auto& subRun : fSubRuns) {
1982 subRun.draw(canvas, drawOrigin, paint, sk_ref_sp(subRunStorage), atlasDelegate);
1983 }
1984}
1985
1986bool SubRunContainer::canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const {
1987 for (const SubRun& subRun : fSubRuns) {
1988 if (!subRun.canReuse(paint, positionMatrix)) {
1989 return false;
1990 }
1991 }
1992 return true;
1993}
1994
1995// Returns the empty span if there is a problem reading the positions.
1997 uint32_t glyphCount = buffer.getArrayCount();
1998
1999 // Zero indicates a problem with serialization.
2000 if (!buffer.validate(glyphCount != 0)) { return {}; }
2001
2002 // Check that the count will not overflow the arena.
2003 if (!buffer.validate(glyphCount <= INT_MAX &&
2004 BagOfBytes::WillCountFit<SkPoint>(glyphCount))) { return {}; }
2005
2006 SkPoint* positionsData = alloc->makePODArray<SkPoint>(glyphCount);
2007 if (!buffer.readPointArray(positionsData, glyphCount)) { return {}; }
2008 return {positionsData, glyphCount};
2009}
2010
2011} // namespace sktext::gpu
uint16_t glyphs[5]
Definition: FontMgrTest.cpp:46
int count
Definition: FontMgrTest.cpp:50
uint32_t GrColor
Definition: GrColor.h:25
@ kGammaCorrect_DistanceFieldEffectFlag
@ kUseLCD_DistanceFieldEffectFlag
@ kPerspective_DistanceFieldEffectFlag
@ kSimilarity_DistanceFieldEffectFlag
@ kBGR_DistanceFieldEffectFlag
@ kScaleOnly_DistanceFieldEffectFlag
@ kAliased_DistanceFieldEffectFlag
@ kPortrait_DistanceFieldEffectFlag
SkPoint pos
#define SkDEBUGFAIL(message)
Definition: SkAssert.h:118
#define SkASSERT(cond)
Definition: SkAssert.h:116
#define SK_DistanceFieldInset
constexpr SkEnumerate< Iter > SkMakeEnumerate(C &c)
Definition: SkEnumerate.h:102
static bool SkIsFinite(T x, Pack... values)
static bool isLCD(const SkScalerContextRec &rec)
bool SkPaintToGrPaintReplaceShader(GrRecordingContext *context, const GrColorInfo &dstColorInfo, const SkPaint &skPaint, const SkMatrix &ctm, std::unique_ptr< GrFragmentProcessor > shaderFP, const SkSurfaceProps &surfaceProps, GrPaint *grPaint)
Definition: SkGr.cpp:570
bool SkPaintToGrPaint(GrRecordingContext *context, const GrColorInfo &dstColorInfo, const SkPaint &skPaint, const SkMatrix &ctm, const SkSurfaceProps &surfaceProps, GrPaint *grPaint)
Definition: SkGr.cpp:553
SkMaskFilterBase * as_MFB(SkMaskFilter *mf)
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition: SkPath.cpp:3892
sk_sp< T > sk_ref_sp(T *obj)
Definition: SkRefCnt.h:381
#define SkScalarFloorToScalar(x)
Definition: SkScalar.h:30
static bool SkScalarNearlyZero(SkScalar x, SkScalar tolerance=SK_ScalarNearlyZero)
Definition: SkScalar.h:101
#define SkScalarSqrt(x)
Definition: SkScalar.h:42
SkScalerContextFlags
SkSpan(Container &&) -> SkSpan< std::remove_pointer_t< decltype(std::data(std::declval< Container >()))> >
@ kUnknown_SkPixelGeometry
static bool SkPixelGeometryIsBGR(SkPixelGeometry geo)
static bool SkPixelGeometryIsV(SkPixelGeometry geo)
constexpr int SkCount(const Container &c)
Definition: SkTLogic.h:54
constexpr int SkToInt(S x)
Definition: SkTo.h:29
uint16_t SkGlyphID
Definition: SkTypes.h:179
constexpr auto SkMakeZip(Ts &&... ts)
Definition: SkZip.h:212
static void draw(SkCanvas *canvas, SkRect &target, int x, int y)
Definition: aaclip.cpp:27
Definition: GrClip.h:29
static bool IsPixelAligned(const SkRect &rect)
Definition: GrClip.h:202
bool isLinearlyBlended() const
Definition: GrColorInfo.cpp:44
std::unique_ptr< GrOp > Owner
Definition: GrOp.h:72
const SkPMColor4f & getColor4f() const
Definition: GrPaint.h:51
bool empty() const
Definition: SkBitmap.h:210
int saveLayer(const SkRect *bounds, const SkPaint *paint)
Definition: SkCanvas.cpp:496
void drawPath(const SkPath &path, const SkPaint &paint)
Definition: SkCanvas.cpp:1747
void concat(const SkMatrix &matrix)
Definition: SkCanvas.cpp:1318
static sk_sp< SkPathEffect > Make(const SkScalar intervals[], int count, SkScalar phase)
SkRect getBounds()
Definition: SkDrawable.cpp:71
void draw(SkCanvas *, const SkMatrix *=nullptr)
Definition: SkDrawable.cpp:43
static SkScalar ApproximateTransformedTextSize(const SkFont &font, const SkMatrix &matrix, const SkPoint &textLocation)
Definition: SkFont.cpp:366
Definition: SkFont.h:35
Edging getEdging() const
Definition: SkFont.h:180
Edging
Definition: SkFont.h:39
@ kAntiAlias
may have transparent pixels on glyph edges
@ kSubpixelAntiAlias
glyph positioned in pixel using transparency
uint16_t maxDimension() const
Definition: SkGlyph.h:349
static constexpr uint16_t kSkSideTooBigForAtlas
Definition: SkGlyph.h:333
skglyph::GlyphAction actionFor(skglyph::ActionType actionType) const
Definition: SkGlyph.h:343
SkPoint leftTop() const
Definition: SkGlyph.h:275
SkRect rect() const
Definition: SkGlyph.h:259
SkGlyphRect offset(SkScalar x, SkScalar y) const
Definition: SkGlyph.h:262
SkGlyphRect inset(SkScalar dx, SkScalar dy) const
Definition: SkGlyph.h:272
virtual bool asABlur(BlurRec *) const
static sk_sp< SkMaskFilter > MakeBlur(SkBlurStyle style, SkScalar sigma, bool respectCTM=true)
static SkScalar DifferentialAreaScale(const SkMatrix &m, const SkPoint &p)
Definition: SkMatrix.cpp:1786
static SkMatrix Scale(SkScalar sx, SkScalar sy)
Definition: SkMatrix.h:75
SkMatrix & postTranslate(SkScalar dx, SkScalar dy)
Definition: SkMatrix.cpp:281
SkMatrix & postScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py)
Definition: SkMatrix.cpp:360
SkPoint mapPoint(SkPoint pt) const
Definition: SkMatrix.h:1374
static const SkMatrix & I()
Definition: SkMatrix.cpp:1544
SkMatrix & preTranslate(SkScalar dx, SkScalar dy)
Definition: SkMatrix.cpp:263
bool isScaleTranslate() const
Definition: SkMatrix.h:236
SkScalar getMaxScale() const
Definition: SkMatrix.cpp:1531
bool hasPerspective() const
Definition: SkMatrix.h:312
bool isSimilarity(SkScalar tol=SK_ScalarNearlyZero) const
Definition: SkMatrix.cpp:180
bool mapRect(SkRect *dst, const SkRect &src, SkApplyPerspectiveClip pc=SkApplyPerspectiveClip::kYes) const
Definition: SkMatrix.cpp:1141
Definition: SkOnce.h:22
static SkColor ComputeLuminanceColor(const SkPaint &)
Style getStyle() const
Definition: SkPaint.h:204
@ kStroke_Style
set to stroke geometry
Definition: SkPaint.h:194
SkScalar getStrokeWidth() const
Definition: SkPaint.h:300
@ kDash_DashType
fills in all of the info parameter
Definition: SkPathEffect.h:62
Definition: SkPath.h:59
SkPath & setIsVolatile(bool isVolatile)
Definition: SkPath.h:370
void transform(const SkMatrix &matrix, SkPath *dst, SkApplyPerspectiveClip pc=SkApplyPerspectiveClip::kYes) const
Definition: SkPath.cpp:1711
constexpr bool empty() const
Definition: SkSpan_impl.h:96
static SkStrikeCache * GlobalStrikeCache()
sk_sp< sktext::StrikeForGPU > findOrCreateScopedStrike(sktext::StrikeForGPUCacheInterface *cache) const
static SkStrikeSpec MakeTransformMask(const SkFont &font, const SkPaint &paint, const SkSurfaceProps &surfaceProps, SkScalerContextFlags scalerContextFlags, const SkMatrix &deviceMatrix)
static SkStrikeSpec MakeMask(const SkFont &font, const SkPaint &paint, const SkSurfaceProps &surfaceProps, SkScalerContextFlags scalerContextFlags, const SkMatrix &deviceMatrix)
static std::tuple< SkStrikeSpec, SkScalar > MakePath(const SkFont &font, const SkPaint &paint, const SkSurfaceProps &surfaceProps, SkScalerContextFlags scalerContextFlags)
bool isHairlineStyle() const
Definition: SkStrokeRec.h:47
bool isFillStyle() const
Definition: SkStrokeRec.h:51
SkPixelGeometry pixelGeometry() const
Definition: SkZip.h:25
constexpr SkZip last(size_t n) const
Definition: SkZip.h:91
constexpr size_t size() const
Definition: SkZip.h:76
constexpr SkZip first(size_t n) const
Definition: SkZip.h:86
constexpr SkZip subspan(size_t offset, size_t count) const
Definition: SkZip.h:96
constexpr bool empty() const
Definition: SkZip.h:77
constexpr auto get() const
Definition: SkZip.h:82
T * get() const
Definition: SkRefCnt.h:303
GrRecordingContext * recordingContext() const
const GrColorInfo & colorInfo() const
const SkSurfaceProps & surfaceProps() const
void resize(size_t count)
Definition: SkTArray.h:423
SkRect sourceBounds() const
Definition: GlyphRun.h:115
size_t maxGlyphRunSize() const
Definition: GlyphRun.h:97
size_t runCount() const
Definition: GlyphRun.h:89
size_t totalGlyphCount() const
Definition: GlyphRun.h:90
virtual SkGlyphDigest digestFor(skglyph::ActionType, SkPackedGlyphID)=0
virtual const SkGlyphPositionRoundingSpec & roundingSpec() const =0
virtual skgpu::MaskFormat maskFormat() const =0
virtual const VertexFiller & vertexFiller() const =0
virtual unsigned short instanceFlags() const =0
virtual int glyphSrcPadding() const =0
virtual void testingOnly_packedGlyphIDToGlyph(StrikeCache *cache) const =0
virtual std::tuple< bool, int > regenerateAtlas(int begin, int end, RegenerateAtlasDelegate) const =0
virtual int glyphCount() const =0
static GlyphVector Make(SkStrikePromise &&promise, SkSpan< const SkPackedGlyphID > glyphs, SubRunAllocator *alloc)
Definition: GlyphVector.cpp:38
static size_t GlyphVectorSize(size_t count)
Definition: GlyphVector.h:72
static std::optional< GlyphVector > MakeFromBuffer(SkReadBuffer &buffer, const SkStrikeClient *strikeClient, SubRunAllocator *alloc)
Definition: GlyphVector.cpp:50
static skgpu::MaskFormat FormatFromSkGlyph(SkMask::Format format)
Definition: Glyph.h:19
static sk_sp< SkMaskFilter > Make()
std::tuple< SkFont, SkScalar, SDFTMatrixRange > getSDFFont(const SkFont &font, const SkMatrix &viewMatrix, const SkPoint &textLocation) const
Definition: SDFTControl.cpp:83
bool isSDFT(SkScalar approximateDeviceTextSize, const SkPaint &paint, const SkMatrix &matrix) const
Definition: SDFTControl.cpp:69
SkScalar maxSize() const
Definition: SDFTControl.h:54
static SDFTMatrixRange MakeFromBuffer(SkReadBuffer &buffer)
std::unique_ptr< T[], ArrayDestroyer > makeUniqueArray(int n)
std::unique_ptr< T, Destroyer > makeUnique(Args &&... args)
SkSpan< T > makePODSpan(SkSpan< const T > s)
static SubRunContainerOwner MakeInAlloc(const GlyphRunList &glyphRunList, const SkMatrix &positionMatrix, const SkPaint &runPaint, SkStrikeDeviceInfo strikeDeviceInfo, StrikeForGPUCacheInterface *strikeCache, sktext::gpu::SubRunAllocator *alloc, SubRunCreationBehavior creationBehavior, const char *tag)
static size_t EstimateAllocSize(const GlyphRunList &glyphRunList)
void flattenAllocSizeHint(SkWriteBuffer &buffer) const
static SubRunContainerOwner MakeFromBufferInAlloc(SkReadBuffer &buffer, const SkStrikeClient *client, SubRunAllocator *alloc)
void draw(SkCanvas *, SkPoint drawOrigin, const SkPaint &, const SkRefCnt *subRunStorage, const AtlasDrawDelegate &) const
void flattenRuns(SkWriteBuffer &buffer) const
static int AllocSizeHintFromBuffer(SkReadBuffer &buffer)
bool canReuse(const SkPaint &paint, const SkMatrix &positionMatrix) const
virtual void doFlatten(SkWriteBuffer &buffer) const =0
virtual int unflattenSize() const =0
virtual void draw(SkCanvas *, SkPoint drawOrigin, const SkPaint &, sk_sp< SkRefCnt > subRunStorage, const AtlasDrawDelegate &) const =0
virtual bool canReuse(const SkPaint &paint, const SkMatrix &positionMatrix) const =0
void flatten(SkWriteBuffer &buffer) const
virtual SubRunStreamTag subRunStreamTag() const =0
virtual const AtlasSubRun * testingOnly_atlasSubRun() const =0
static SubRunOwner MakeFromBuffer(SkReadBuffer &buffer, sktext::gpu::SubRunAllocator *alloc, const SkStrikeClient *client)
static std::optional< VertexFiller > MakeFromBuffer(SkReadBuffer &buffer, SubRunAllocator *alloc)
static VertexFiller Make(skgpu::MaskFormat maskType, const SkMatrix &creationMatrix, SkRect creationBounds, SkSpan< const SkPoint > positions, SubRunAllocator *alloc, FillerType fillerType)
const Paint & paint
Definition: color_source.cc:38
DlColor color
static const char * begin(const StringSlice &s)
Definition: editor.cpp:252
SkBitmap source
Definition: examples.cpp:28
float SkScalar
Definition: extension.cpp:12
struct MyStruct a[10]
FlutterSemanticsFlag flags
glong glong end
GAsyncResult * result
uint32_t uint32_t * format
static float max(float r, float g, float b)
Definition: hsl.cpp:49
SK_API sk_sp< SkDocument > Make(SkWStream *dst, const SkSerialProcs *=nullptr, std::function< void(const SkPicture *)> onEndPage=nullptr)
unsigned useCenter Optional< SkMatrix > matrix
Definition: SkRecords.h:258
Optional< SkRect > bounds
Definition: SkRecords.h:189
clipRect(r.rect, r.opAA.op(), r.opAA.aa())) template<> void Draw
drawAtlas(r.atlas.get(), r.xforms, r.texs, r.colors, r.count, r.mode, r.sampling, r.cull, r.paint)) DRAW(DrawAnnotation
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets Path to the Flutter assets directory enable service port Allow the VM service to fallback to automatic port selection if binding to a specified port fails trace Trace early application lifecycle Automatically switches to an endless trace buffer trace skia Filters out all Skia trace event categories except those that are specified in this comma separated list dump skp on shader Automatically dump the skp that triggers new shader compilations This is useful for writing custom ShaderWarmUp to reduce jank By this is not enabled to reduce the overhead purge persistent cache
Definition: switches.h:191
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
font
Font Metadata and Metrics.
const myers::Point & get< 1 >(const myers::Segment &s)
Definition: Myers.h:81
SkGlyphRect empty_rect()
Definition: SkGlyph.h:289
SkGlyphRect rect_union(SkGlyphRect, SkGlyphRect)
Definition: SkGlyph.h:297
@ kSDFT
Definition: SkGlyph.h:316
@ kPath
Definition: SkGlyph.h:317
@ kMask
Definition: SkGlyph.h:315
@ kDirectMask
Definition: SkGlyph.h:313
@ kDrawable
Definition: SkGlyph.h:318
MaskFormat
Definition: AtlasTypes.h:98
std::unique_ptr< SubRun, SubRunAllocator::Destroyer > SubRunOwner
std::function< std::tuple< bool, int >(GlyphVector *, int begin, int end, skgpu::MaskFormat, int padding)> RegenerateAtlasDelegate
std::unique_ptr< SubRunContainer, SubRunAllocator::Destroyer > SubRunContainerOwner
std::tuple< SkZip< const SkPackedGlyphID, const SkPoint >, SkZip< SkGlyphID, SkPoint >, SkRect > prepare_for_SDFT_drawing(StrikeForGPU *strike, const SkMatrix &creationMatrix, SkZip< const SkGlyphID, const SkPoint > source, SkZip< SkPackedGlyphID, SkPoint > acceptedBuffer, SkZip< SkGlyphID, SkPoint > rejectedBuffer)
SkSpan< SkPoint > MakePointsFromBuffer(SkReadBuffer &buffer, SubRunAllocator *alloc)
std::tuple< SkZip< const SkGlyphID, const SkPoint >, SkZip< SkGlyphID, SkPoint > > prepare_for_drawable_drawing(StrikeForGPU *strike, SkZip< const SkGlyphID, const SkPoint > source, SkZip< SkGlyphID, SkPoint > acceptedBuffer, SkZip< SkGlyphID, SkPoint > rejectedBuffer)
SkScalar find_maximum_glyph_dimension(StrikeForGPU *strike, SkSpan< const SkGlyphID > glyphs)
std::tuple< SkZip< const SkGlyphID, const SkPoint >, SkZip< SkGlyphID, SkPoint > > prepare_for_path_drawing(StrikeForGPU *strike, SkZip< const SkGlyphID, const SkPoint > source, SkZip< SkGlyphID, SkPoint > acceptedBuffer, SkZip< SkGlyphID, SkPoint > rejectedBuffer)
std::tuple< SkZip< const SkPackedGlyphID, const SkPoint, const SkMask::Format >, SkZip< SkGlyphID, SkPoint >, SkRect > prepare_for_direct_mask_drawing(StrikeForGPU *strike, const SkMatrix &positionMatrix, SkZip< const SkGlyphID, const SkPoint > source, SkZip< SkPackedGlyphID, SkPoint, SkMask::Format > acceptedBuffer, SkZip< SkGlyphID, SkPoint > rejectedBuffer)
std::tuple< SkZip< const SkPackedGlyphID, const SkPoint, const SkMask::Format >, SkZip< SkGlyphID, SkPoint >, SkRect > prepare_for_mask_drawing(StrikeForGPU *strike, const SkMatrix &creationMatrix, SkZip< const SkGlyphID, const SkPoint > source, SkZip< SkPackedGlyphID, SkPoint, SkMask::Format > acceptedBuffer, SkZip< SkGlyphID, SkPoint > rejectedBuffer)
std::function< void(const sktext::gpu::AtlasSubRun *subRun, SkPoint drawOrigin, const SkPaint &paint, sk_sp< SkRefCnt > subRunStorage, sktext::gpu::RendererData)> AtlasDrawDelegate
static std::tuple< SkStrikeSpec, SkScalar, sktext::gpu::SDFTMatrixRange > make_sdft_strike_spec(const SkFont &font, const SkPaint &paint, const SkSurfaceProps &surfaceProps, const SkMatrix &deviceMatrix, const SkPoint &textLocation, const sktext::gpu::SDFTControl &control)
SkMatrix position_matrix(const SkMatrix &drawMatrix, SkPoint drawOrigin)
Definition: SlugImpl.cpp:69
SeparatedVector2 offset
const SkVector halfAxisSampleFreq
Definition: SkGlyph.h:233
const SkIPoint ignorePositionFieldMask
Definition: SkGlyph.h:235
Definition: SkRect.h:32
static constexpr SkIRect MakeEmpty()
Definition: SkRect.h:45
bool isEmpty() const
Definition: SkRect.h:202
SkScalar fPhase
Offset into the dashed interval pattern.
Definition: SkPathEffect.h:73
int32_t fCount
Number of intervals in the dash. Should be even number.
Definition: SkPathEffect.h:72
SkScalar * fIntervals
Length of on/off intervals for dashed lines.
Definition: SkPathEffect.h:70
constexpr float y() const
Definition: SkPoint_impl.h:187
constexpr float x() const
Definition: SkPoint_impl.h:181
float fA
alpha component
Definition: SkColor.h:266
bool intersects(const SkRect &r) const
Definition: SkRect.h:1121
void round(SkIRect *dst) const
Definition: SkRect.h:1228
constexpr SkPoint center() const
Definition: SkRect.h:792
static constexpr SkRect MakeWH(float w, float h)
Definition: SkRect.h:609
const SkSurfaceProps fSurfaceProps
Definition: SkDevice.h:80
const SkScalerContextFlags fScalerContextFlags
Definition: SkDevice.h:81
const sktext::gpu::SDFTControl *const fSDFTControl
Definition: SkDevice.h:83