Flutter Engine
The Flutter Engine
TextBlob.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2015 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
9
12#include "include/core/SkRect.h"
17#include "src/core/SkDevice.h"
18#include "src/core/SkFontPriv.h"
22#include "src/text/GlyphRun.h"
27
28#include <memory>
29#include <utility>
30
31class SkMaskFilter;
32
33using namespace sktext::gpu;
34namespace {
35
36// Check for integer translate with the same 2x2 matrix.
37// Returns the translation, and true if the change from initial matrix to the position matrix
38// support using direct glyph masks.
39std::tuple<bool, SkVector> can_use_direct(
40 const SkMatrix& initialPositionMatrix, const SkMatrix& positionMatrix) {
41 // The existing direct glyph info can be used if the initialPositionMatrix, and the
42 // positionMatrix have the same 2x2, and the translation between them is integer.
43 // Calculate the translation in source space to a translation in device space by mapping
44 // (0, 0) through both the initial position matrix and the position matrix; take the difference.
45 SkVector translation = positionMatrix.mapOrigin() - initialPositionMatrix.mapOrigin();
46 return {initialPositionMatrix.getScaleX() == positionMatrix.getScaleX() &&
47 initialPositionMatrix.getScaleY() == positionMatrix.getScaleY() &&
48 initialPositionMatrix.getSkewX() == positionMatrix.getSkewX() &&
49 initialPositionMatrix.getSkewY() == positionMatrix.getSkewY() &&
50 SkScalarIsInt(translation.x()) && SkScalarIsInt(translation.y()),
51 translation};
52}
53
54
55static SkColor compute_canonical_color(const SkPaint& paint, bool lcd) {
57 if (lcd) {
58 // This is the correct computation for canonicalColor, but there are tons of cases where LCD
59 // can be modified. For now we just regenerate if any run in a textblob has LCD.
60 // TODO figure out where all of these modifications are and see if we can incorporate that
61 // logic at a higher level *OR* use sRGB
62 //canonicalColor = SkMaskGamma::CanonicalColor(canonicalColor);
63
64 // TODO we want to figure out a way to be able to use the canonical color on LCD text,
65 // see the note above. We pick a placeholder value for LCD text to ensure we always match
66 // the same key
68 } else {
69 // A8, though can have mixed BMP text but it shouldn't matter because BMP text won't have
70 // gamma corrected masks anyways, nor color
71 U8CPU lum = SkComputeLuminance(SkColorGetR(canonicalColor),
72 SkColorGetG(canonicalColor),
73 SkColorGetB(canonicalColor));
74 // reduce to our finite number of bits
76 }
77 return canonicalColor;
78}
79
80} // namespace
81
82namespace sktext::gpu {
83// -- TextBlob::Key ------------------------------------------------------------------------------
84auto TextBlob::Key::Make(const GlyphRunList& glyphRunList,
85 const SkPaint& paint,
86 const SkMatrix& drawMatrix,
87 const SkStrikeDeviceInfo& strikeDevice) -> std::tuple<bool, Key> {
88 SkASSERT(strikeDevice.fSDFTControl != nullptr);
90 // It might be worth caching these things, but its not clear at this time
91 // TODO for animated mask filters, this will fill up our cache. We need a safeguard here
92 const SkMaskFilter* maskFilter = paint.getMaskFilter();
93 bool canCache = glyphRunList.canCache() &&
94 !(paint.getPathEffect() ||
95 (maskFilter && !as_MFB(maskFilter)->asABlur(&blurRec)));
96
98 if (canCache) {
99 bool hasLCD = glyphRunList.anyRunsLCD();
100
101 // We canonicalize all non-lcd draws to use kUnknown_SkPixelGeometry
102 SkPixelGeometry pixelGeometry = hasLCD ? strikeDevice.fSurfaceProps.pixelGeometry()
104
105 SkColor canonicalColor = compute_canonical_color(paint, hasLCD);
106
107 key.fPixelGeometry = pixelGeometry;
108 key.fUniqueID = glyphRunList.uniqueID();
109 key.fStyle = paint.getStyle();
110 if (key.fStyle != SkPaint::kFill_Style) {
111 key.fFrameWidth = paint.getStrokeWidth();
112 key.fMiterLimit = paint.getStrokeMiter();
113 key.fJoin = paint.getStrokeJoin();
114 }
115 key.fHasBlur = maskFilter != nullptr;
116 if (key.fHasBlur) {
117 key.fBlurRec = blurRec;
118 }
119 key.fCanonicalColor = canonicalColor;
120 key.fScalerContextFlags = SkTo<uint32_t>(strikeDevice.fScalerContextFlags);
121
122 // Do any runs use direct drawing types?.
123 key.fHasSomeDirectSubRuns = false;
124 SkPoint glyphRunListLocation = glyphRunList.sourceBoundsWithOrigin().center();
125 for (auto& run : glyphRunList) {
126 SkScalar approximateDeviceTextSize =
128 glyphRunListLocation);
129 key.fHasSomeDirectSubRuns |=
130 strikeDevice.fSDFTControl->isDirect(approximateDeviceTextSize, paint,
131 drawMatrix);
132 }
133
134 if (key.fHasSomeDirectSubRuns) {
135 // Store the fractional offset of the position. We know that the matrix can't be
136 // perspective at this point.
137 SkPoint mappedOrigin = drawMatrix.mapOrigin();
138 key.fPositionMatrix = drawMatrix;
139 key.fPositionMatrix.setTranslateX(
140 mappedOrigin.x() - SkScalarFloorToScalar(mappedOrigin.x()));
141 key.fPositionMatrix.setTranslateY(
142 mappedOrigin.y() - SkScalarFloorToScalar(mappedOrigin.y()));
143 } else {
144 // For path and SDFT, the matrix doesn't matter.
145 key.fPositionMatrix = SkMatrix::I();
146 }
147 }
148
149 return {canCache, key};
150}
151
153 if (fUniqueID != that.fUniqueID) { return false; }
154 if (fCanonicalColor != that.fCanonicalColor) { return false; }
155 if (fStyle != that.fStyle) { return false; }
157 if (fFrameWidth != that.fFrameWidth ||
158 fMiterLimit != that.fMiterLimit ||
159 fJoin != that.fJoin) {
160 return false;
161 }
162 }
163 if (fPixelGeometry != that.fPixelGeometry) { return false; }
164 if (fHasBlur != that.fHasBlur) { return false; }
165 if (fHasBlur) {
166 if (fBlurRec.fStyle != that.fBlurRec.fStyle || fBlurRec.fSigma != that.fBlurRec.fSigma) {
167 return false;
168 }
169 }
170
171 if (fScalerContextFlags != that.fScalerContextFlags) { return false; }
172
173 // DirectSubRuns do not support perspective when used with a TextBlob. SDFT, Transformed,
174 // Path, and Drawable do support perspective.
175 if (fPositionMatrix.hasPerspective() && fHasSomeDirectSubRuns) { return false; }
176
177 if (fHasSomeDirectSubRuns != that.fHasSomeDirectSubRuns) { return false; }
178
181 return compatible;
182 }
183
184 return true;
185}
186
187// -- TextBlob -----------------------------------------------------------------------------------
188void TextBlob::operator delete(void* p) { ::operator delete(p); }
189void* TextBlob::operator new(size_t) { SK_ABORT("All blobs are created by placement new."); }
190void* TextBlob::operator new(size_t, void* p) { return p; }
191
192TextBlob::~TextBlob() = default;
193
195 const SkPaint& paint,
196 const SkMatrix& positionMatrix,
197 SkStrikeDeviceInfo strikeDeviceInfo,
198 StrikeForGPUCacheInterface* strikeCache) {
199 size_t subRunSizeHint = SubRunContainer::EstimateAllocSize(glyphRunList);
200 auto [initializer, totalMemoryAllocated, alloc] =
201 SubRunAllocator::AllocateClassMemoryAndArena<TextBlob>(subRunSizeHint);
202
203 auto container = SubRunContainer::MakeInAlloc(
204 glyphRunList, positionMatrix, paint,
205 strikeDeviceInfo, strikeCache, &alloc, SubRunContainer::kAddSubRuns, "TextBlob");
206
208 sk_sp<TextBlob> blob = sk_sp<TextBlob>(initializer.initialize(std::move(alloc),
209 std::move(container),
210 totalMemoryAllocated,
211 initialLuminance));
212 return blob;
213}
214
216 fKey = key;
217}
218
219bool TextBlob::canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const {
220 // A singular matrix will create a TextBlob with no SubRuns, but unknown glyphs can also
221 // cause empty runs. If there are no subRuns, then regenerate when the matrices don't match.
222 if (fSubRuns->isEmpty() && fSubRuns->initialPosition() != positionMatrix) {
223 return false;
224 }
225
226 // If we have LCD text then our canonical color will be set to transparent, in this case we have
227 // to regenerate the blob on any color change
228 // We use the grPaint to get any color filter effects
230 fInitialLuminance != SkPaintPriv::ComputeLuminanceColor(paint)) {
231 return false;
232 }
233
234 return fSubRuns->canReuse(paint, positionMatrix);
235}
236
237const TextBlob::Key& TextBlob::key() const { return fKey; }
238
240 SkPoint drawOrigin,
241 const SkPaint& paint,
242 const AtlasDrawDelegate& atlasDelegate) {
243 fSubRuns->draw(canvas, drawOrigin, paint, this, atlasDelegate);
244}
245
247 SubRunContainerOwner subRuns,
248 int totalMemorySize,
249 SkColor initialLuminance)
250 : fAlloc{std::move(alloc)}
251 , fSubRuns{std::move(subRuns)}
252 , fSize(totalMemorySize)
253 , fInitialLuminance{initialLuminance} { }
254
255sk_sp<Slug> MakeSlug(const SkMatrix& drawMatrix,
256 const sktext::GlyphRunList& glyphRunList,
257 const SkPaint& paint,
258 SkStrikeDeviceInfo strikeDeviceInfo,
260 return SlugImpl::Make(drawMatrix, glyphRunList, paint, strikeDeviceInfo, strikeCache);
261}
262} // namespace sktext::gpu
static struct Initializer initializer
static bool compatible(const MTLRenderPassAttachmentDescriptor *first, const MTLRenderPassAttachmentDescriptor *second, const GrMtlPipelineState *pipelineState)
#define SK_ABORT(message,...)
Definition: SkAssert.h:70
#define SkASSERT(cond)
Definition: SkAssert.h:116
unsigned U8CPU
Definition: SkCPUTypes.h:18
static U8CPU SkComputeLuminance(U8CPU r, U8CPU g, U8CPU b)
Definition: SkColorData.h:118
#define SkColorGetR(color)
Definition: SkColor.h:65
#define SkColorGetG(color)
Definition: SkColor.h:69
uint32_t SkColor
Definition: SkColor.h:37
#define SkColorSetRGB(r, g, b)
Definition: SkColor.h:57
constexpr SkColor SK_ColorTRANSPARENT
Definition: SkColor.h:99
#define SkColorGetB(color)
Definition: SkColor.h:73
SkMaskFilterBase * as_MFB(SkMaskFilter *mf)
#define SkScalarFloorToScalar(x)
Definition: SkScalar.h:30
static bool SkScalarIsInt(SkScalar x)
Definition: SkScalar.h:80
SkPixelGeometry
@ kUnknown_SkPixelGeometry
static SkScalar ApproximateTransformedTextSize(const SkFont &font, const SkMatrix &matrix, const SkPoint &textLocation)
Definition: SkFont.cpp:366
virtual bool asABlur(BlurRec *) const
SkScalar getSkewY() const
Definition: SkMatrix.h:430
SkScalar getSkewX() const
Definition: SkMatrix.h:438
SkScalar getScaleX() const
Definition: SkMatrix.h:415
static const SkMatrix & I()
Definition: SkMatrix.cpp:1544
SkScalar getScaleY() const
Definition: SkMatrix.h:422
SkPoint mapOrigin() const
Definition: SkMatrix.h:1437
bool hasPerspective() const
Definition: SkMatrix.h:312
static SkColor ComputeLuminanceColor(const SkPaint &)
@ kFill_Style
set to fill geometry
Definition: SkPaint.h:193
static SkColor CanonicalColor(SkColor color)
Definition: SkMaskGamma.h:124
static sk_sp< SlugImpl > Make(const SkMatrix &viewMatrix, const sktext::GlyphRunList &glyphRunList, const SkPaint &paint, SkStrikeDeviceInfo strikeDeviceInfo, sktext::StrikeForGPUCacheInterface *strikeCache)
Definition: SlugImpl.cpp:74
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)
TextBlob(SubRunAllocator &&alloc, SubRunContainerOwner subRuns, int totalMemorySize, SkColor initialLuminance)
Definition: TextBlob.cpp:246
const Key & key()
Definition: TextBlob.h:106
bool canReuse(const SkPaint &paint, const SkMatrix &positionMatrix) const
Definition: TextBlob.cpp:219
void addKey(const Key &key)
Definition: TextBlob.cpp:215
void draw(SkCanvas *, SkPoint drawOrigin, const SkPaint &paint, const AtlasDrawDelegate &)
Definition: TextBlob.cpp:239
static sk_sp< TextBlob > Make(const sktext::GlyphRunList &glyphRunList, const SkPaint &paint, const SkMatrix &positionMatrix, SkStrikeDeviceInfo strikeDeviceInfo, StrikeForGPUCacheInterface *strikeCache)
Definition: TextBlob.cpp:194
const Paint & paint
Definition: color_source.cc:38
float SkScalar
Definition: extension.cpp:12
static float lum(float r, float g, float b)
Definition: hsl.cpp:52
Definition: run.py:1
static std::tuple< bool, SkVector > can_use_direct(const SkMatrix &creationMatrix, const SkMatrix &positionMatrix)
std::unique_ptr< SubRunContainer, SubRunAllocator::Destroyer > SubRunContainerOwner
sk_sp< Slug > MakeSlug(const SkMatrix &drawMatrix, const sktext::GlyphRunList &glyphRunList, const SkPaint &paint, SkStrikeDeviceInfo strikeDeviceInfo, sktext::StrikeForGPUCacheInterface *strikeCache)
Definition: TextBlob.cpp:255
std::function< void(const sktext::gpu::AtlasSubRun *subRun, SkPoint drawOrigin, const SkPaint &paint, sk_sp< SkRefCnt > subRunStorage, sktext::gpu::RendererData)> AtlasDrawDelegate
Definition: ref_ptr.h:256
constexpr float y() const
Definition: SkPoint_impl.h:187
constexpr float x() const
Definition: SkPoint_impl.h:181
SkPaint::Join fJoin
Definition: TextBlob.h:79
SkPixelGeometry fPixelGeometry
Definition: TextBlob.h:71
SkPaint::Style fStyle
Definition: TextBlob.h:78
uint32_t fScalerContextFlags
Definition: TextBlob.h:73
static std::tuple< bool, Key > Make(const GlyphRunList &glyphRunList, const SkPaint &paint, const SkMatrix &drawMatrix, const SkStrikeDeviceInfo &strikeDevice)
Definition: TextBlob.cpp:84
SkMaskFilterBase::BlurRec fBlurRec
Definition: TextBlob.h:72
bool operator==(const Key &other) const
Definition: TextBlob.cpp:152