/src/skia/src/text/gpu/SubRunContainer.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2022 Google LLC |
3 | | * |
4 | | * Use of this source code is governed by a BSD-style license that can be |
5 | | * found in the LICENSE file. |
6 | | */ |
7 | | |
8 | | #include "src/text/gpu/SubRunContainer.h" |
9 | | |
10 | | #include "include/core/SkCanvas.h" |
11 | | #include "include/core/SkDrawable.h" |
12 | | #include "include/core/SkFont.h" |
13 | | #include "include/core/SkMaskFilter.h" |
14 | | #include "include/core/SkMatrix.h" |
15 | | #include "include/core/SkPaint.h" |
16 | | #include "include/core/SkPath.h" |
17 | | #include "include/core/SkPathEffect.h" |
18 | | #include "include/core/SkPoint.h" |
19 | | #include "include/core/SkRRect.h" |
20 | | #include "include/core/SkRect.h" |
21 | | #include "include/core/SkScalar.h" |
22 | | #include "include/core/SkStrokeRec.h" |
23 | | #include "include/core/SkSurfaceProps.h" |
24 | | #include "include/core/SkTypes.h" |
25 | | #include "include/effects/SkDashPathEffect.h" |
26 | | #include "include/private/SkColorData.h" |
27 | | #include "include/private/base/SkFloatingPoint.h" |
28 | | #include "include/private/base/SkOnce.h" |
29 | | #include "include/private/base/SkTArray.h" |
30 | | #include "include/private/base/SkTLogic.h" |
31 | | #include "include/private/base/SkTo.h" |
32 | | #include "include/private/gpu/ganesh/GrTypesPriv.h" |
33 | | #include "src/base/SkZip.h" |
34 | | #include "src/core/SkDevice.h" |
35 | | #include "src/core/SkDistanceFieldGen.h" |
36 | | #include "src/core/SkEnumerate.h" |
37 | | #include "src/core/SkFontPriv.h" |
38 | | #include "src/core/SkGlyph.h" |
39 | | #include "src/core/SkMask.h" |
40 | | #include "src/core/SkMaskFilterBase.h" |
41 | | #include "src/core/SkMatrixPriv.h" |
42 | | #include "src/core/SkPaintPriv.h" |
43 | | #include "src/core/SkReadBuffer.h" |
44 | | #include "src/core/SkScalerContext.h" |
45 | | #include "src/core/SkStrike.h" |
46 | | #include "src/core/SkStrikeCache.h" |
47 | | #include "src/core/SkStrikeSpec.h" |
48 | | #include "src/core/SkWriteBuffer.h" |
49 | | #include "src/gpu/AtlasTypes.h" |
50 | | #include "src/text/GlyphRun.h" |
51 | | #include "src/text/StrikeForGPU.h" |
52 | | #include "src/text/gpu/Glyph.h" |
53 | | #include "src/text/gpu/GlyphVector.h" |
54 | | #include "src/text/gpu/SDFMaskFilter.h" |
55 | | #include "src/text/gpu/SDFTControl.h" |
56 | | #include "src/text/gpu/SubRunAllocator.h" |
57 | | #include "src/text/gpu/VertexFiller.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 | | |
67 | | class GrRecordingContext; |
68 | | |
69 | | #if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) |
70 | | #include "src/gpu/ganesh/GrClip.h" |
71 | | #include "src/gpu/ganesh/GrColorInfo.h" |
72 | | #include "src/gpu/ganesh/GrFragmentProcessor.h" |
73 | | #include "src/gpu/ganesh/GrPaint.h" |
74 | | #include "src/gpu/ganesh/SkGr.h" |
75 | | #include "src/gpu/ganesh/SurfaceDrawContext.h" |
76 | | #include "src/gpu/ganesh/effects/GrDistanceFieldGeoProc.h" |
77 | | #include "src/gpu/ganesh/ops/AtlasTextOp.h" |
78 | | using AtlasTextOp = skgpu::ganesh::AtlasTextOp; |
79 | | #endif // defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) |
80 | | |
81 | | using namespace skia_private; |
82 | | using 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 | | |
95 | | namespace sktext::gpu { |
96 | | // -- SubRunStreamTag ------------------------------------------------------------------------------ |
97 | | enum SubRun::SubRunStreamTag : int { |
98 | | kBad = 0, // Make this 0 to line up with errors from readInt. |
99 | | kDirectMaskStreamTag, |
100 | | #if !defined(SK_DISABLE_SDF_TEXT) |
101 | | kSDFTStreamTag, |
102 | | #endif |
103 | | kTransformMaskStreamTag, |
104 | | kPathStreamTag, |
105 | | kDrawableStreamTag, |
106 | | kSubRunStreamTagCount, |
107 | | }; |
108 | | |
109 | | } // namespace sktext::gpu |
110 | | |
111 | | using MaskFormat = skgpu::MaskFormat; |
112 | | |
113 | | using namespace sktext; |
114 | | using namespace sktext::gpu; |
115 | | |
116 | | namespace { |
117 | | #if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) |
118 | | SkPMColor4f calculate_colors(skgpu::ganesh::SurfaceDrawContext* sdc, |
119 | | const SkPaint& paint, |
120 | | const SkMatrix& matrix, |
121 | | MaskFormat maskFormat, |
122 | 777 | GrPaint* grPaint) { |
123 | 777 | GrRecordingContext* rContext = sdc->recordingContext(); |
124 | 777 | const GrColorInfo& colorInfo = sdc->colorInfo(); |
125 | 777 | const SkSurfaceProps& props = sdc->surfaceProps(); |
126 | 777 | if (maskFormat == MaskFormat::kARGB) { |
127 | 2 | SkPaintToGrPaintReplaceShader(rContext, colorInfo, paint, matrix, nullptr, props, grPaint); |
128 | 2 | float a = grPaint->getColor4f().fA; |
129 | 2 | return {a, a, a, a}; |
130 | 2 | } |
131 | 775 | SkPaintToGrPaint(rContext, colorInfo, paint, matrix, props, grPaint); |
132 | 775 | return grPaint->getColor4f(); |
133 | 777 | } |
134 | | |
135 | 1.73k | SkMatrix position_matrix(const SkMatrix& drawMatrix, SkPoint drawOrigin) { |
136 | 1.73k | SkMatrix position_matrix = drawMatrix; |
137 | 1.73k | return position_matrix.preTranslate(drawOrigin.x(), drawOrigin.y()); |
138 | 1.73k | } |
139 | | #endif // defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) |
140 | | |
141 | 839 | SkSpan<const SkPackedGlyphID> get_packedIDs(SkZip<const SkPackedGlyphID, const SkPoint> accepted) { |
142 | 839 | return accepted.get<0>(); |
143 | 839 | } |
144 | | |
145 | 2.53k | SkSpan<const SkGlyphID> get_glyphIDs(SkZip<const SkGlyphID, const SkPoint> accepted) { |
146 | 2.53k | return accepted.get<0>(); |
147 | 2.53k | } |
148 | | |
149 | | template <typename U> |
150 | 3.37k | SkSpan<const SkPoint> get_positions(SkZip<U, const SkPoint> accepted) { |
151 | 3.37k | return accepted.template get<1>(); |
152 | 3.37k | } SubRunContainer.cpp:SkSpan<SkPoint const> (anonymous namespace)::get_positions<SkPackedGlyphID const>(SkZip<SkPackedGlyphID const, SkPoint const>) Line | Count | Source | 150 | 839 | SkSpan<const SkPoint> get_positions(SkZip<U, const SkPoint> accepted) { | 151 | 839 | return accepted.template get<1>(); | 152 | 839 | } |
SubRunContainer.cpp:SkSpan<SkPoint const> (anonymous namespace)::get_positions<unsigned short const>(SkZip<unsigned short const, SkPoint const>) Line | Count | Source | 150 | 2.53k | SkSpan<const SkPoint> get_positions(SkZip<U, const SkPoint> accepted) { | 151 | 2.53k | return accepted.template get<1>(); | 152 | 2.53k | } |
|
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. |
158 | | class PathOpSubmitter { |
159 | | public: |
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 | 2.53k | , fStrikePromise{std::move(that.fStrikePromise)} {} |
171 | 0 | PathOpSubmitter& operator=(PathOpSubmitter&& that) { |
172 | 0 | this->~PathOpSubmitter(); |
173 | 0 | new (this) PathOpSubmitter{std::move(that)}; |
174 | 0 | return *this; |
175 | 0 | } |
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 | | |
202 | | private: |
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 | | |
215 | 0 | int PathOpSubmitter::unflattenSize() const { |
216 | 0 | return fPositions.size_bytes() + fIDsOrPaths.size_bytes(); |
217 | 0 | } |
218 | | |
219 | 0 | void PathOpSubmitter::flatten(SkWriteBuffer& buffer) const { |
220 | 0 | fStrikePromise.flatten(buffer); |
221 | |
|
222 | 0 | buffer.writeInt(fIsAntiAliased); |
223 | 0 | buffer.writeScalar(fStrikeToSourceScale); |
224 | 0 | buffer.writePointArray(fPositions.data(), SkCount(fPositions)); |
225 | 0 | for (IDOrPath& idOrPath : fIDsOrPaths) { |
226 | 0 | buffer.writeInt(idOrPath.fGlyphID); |
227 | 0 | } |
228 | 0 | } |
229 | | |
230 | | std::optional<PathOpSubmitter> PathOpSubmitter::MakeFromBuffer(SkReadBuffer& buffer, |
231 | | SubRunAllocator* alloc, |
232 | 0 | const SkStrikeClient* client) { |
233 | 0 | std::optional<SkStrikePromise> strikePromise = |
234 | 0 | SkStrikePromise::MakeFromBuffer(buffer, client, SkStrikeCache::GlobalStrikeCache()); |
235 | 0 | if (!buffer.validate(strikePromise.has_value())) { |
236 | 0 | return std::nullopt; |
237 | 0 | } |
238 | | |
239 | 0 | bool isAntiAlias = buffer.readInt(); |
240 | |
|
241 | 0 | SkScalar strikeToSourceScale = buffer.readScalar(); |
242 | 0 | if (!buffer.validate(0 < strikeToSourceScale)) { return std::nullopt; } |
243 | | |
244 | 0 | SkSpan<SkPoint> positions = MakePointsFromBuffer(buffer, alloc); |
245 | 0 | if (positions.empty()) { return std::nullopt; } |
246 | 0 | const int glyphCount = SkCount(positions); |
247 | | |
248 | | // Remember, we stored an int for glyph id. |
249 | 0 | if (!buffer.validateCanReadN<int>(glyphCount)) { return std::nullopt; } |
250 | 0 | auto idsOrPaths = SkSpan(alloc->makeUniqueArray<IDOrPath>(glyphCount).release(), glyphCount); |
251 | 0 | for (auto& idOrPath : idsOrPaths) { |
252 | 0 | idOrPath.fGlyphID = SkTo<SkGlyphID>(buffer.readInt()); |
253 | 0 | } |
254 | |
|
255 | 0 | if (!buffer.isValid()) { return std::nullopt; } |
256 | | |
257 | 0 | return PathOpSubmitter{isAntiAlias, |
258 | 0 | strikeToSourceScale, |
259 | 0 | positions, |
260 | 0 | idsOrPaths, |
261 | 0 | std::move(strikePromise.value())}; |
262 | 0 | } |
263 | | |
264 | | PathOpSubmitter::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 | 2.53k | , fStrikePromise{std::move(strikePromise)} { |
275 | 2.53k | SkASSERT(!fPositions.empty()); |
276 | 2.53k | } |
277 | | |
278 | 5.06k | PathOpSubmitter::~PathOpSubmitter() { |
279 | | // If we have converted glyph IDs to paths, then clean up the SkPaths. |
280 | 5.06k | if (fPathsAreCreated) { |
281 | 48.6k | for (auto& idOrPath : fIDsOrPaths) { |
282 | 48.6k | idOrPath.fPath.~SkPath(); |
283 | 48.6k | } |
284 | 2.53k | } |
285 | 5.06k | } |
286 | | |
287 | | PathOpSubmitter PathOpSubmitter::Make(SkZip<const SkGlyphID, const SkPoint> accepted, |
288 | | bool isAntiAliased, |
289 | | SkScalar strikeToSourceScale, |
290 | | SkStrikePromise&& strikePromise, |
291 | 2.53k | SubRunAllocator* alloc) { |
292 | 48.6k | auto mapToIDOrPath = [](SkGlyphID glyphID) { return IDOrPath{glyphID}; }; |
293 | | |
294 | 2.53k | IDOrPath* const rawIDsOrPaths = |
295 | 2.53k | alloc->makeUniqueArray<IDOrPath>(get_glyphIDs(accepted), mapToIDOrPath).release(); |
296 | | |
297 | 2.53k | return PathOpSubmitter{isAntiAliased, |
298 | 2.53k | strikeToSourceScale, |
299 | 2.53k | alloc->makePODSpan(get_positions(accepted)), |
300 | 2.53k | SkSpan(rawIDsOrPaths, accepted.size()), |
301 | 2.53k | std::move(strikePromise)}; |
302 | 2.53k | } |
303 | | |
304 | | void |
305 | 2.66k | PathOpSubmitter::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 | 2.66k | fConvertIDsToPaths([&]() { |
308 | 2.53k | if (SkStrike* strike = fStrikePromise.strike()) { |
309 | 2.53k | strike->glyphIDsToPaths(fIDsOrPaths); |
310 | | |
311 | | // Drop ref to strike so that it can be purged from the cache if needed. |
312 | 2.53k | fStrikePromise.resetStrike(); |
313 | 2.53k | fPathsAreCreated = true; |
314 | 2.53k | } |
315 | 2.53k | }); |
316 | | |
317 | 2.66k | SkPaint runPaint{paint}; |
318 | 2.66k | runPaint.setAntiAlias(fIsAntiAliased); |
319 | | |
320 | 2.66k | 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 | 2.66k | SkMatrix strikeToSource = SkMatrix::Scale(fStrikeToSourceScale, fStrikeToSourceScale); |
325 | 2.66k | 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 | 2.66k | SkStrokeRec style(runPaint); |
330 | 2.66k | bool needsExactCTM = runPaint.getShader() |
331 | 2.66k | || runPaint.getPathEffect() |
332 | 2.66k | || (!style.isFillStyle() && !style.isHairlineStyle()) |
333 | 2.66k | || (maskFilter != nullptr && !maskFilter->asABlur(nullptr)); |
334 | 2.66k | if (!needsExactCTM) { |
335 | 819 | SkMaskFilterBase::BlurRec blurRec; |
336 | | |
337 | | // If there is a blur mask filter, then sigma needs to be adjusted to account for the |
338 | | // scaling of fStrikeToSourceScale. |
339 | 819 | if (maskFilter != nullptr && maskFilter->asABlur(&blurRec)) { |
340 | 137 | runPaint.setMaskFilter( |
341 | 137 | SkMaskFilter::MakeBlur(blurRec.fStyle, blurRec.fSigma / fStrikeToSourceScale)); |
342 | 137 | } |
343 | 15.3k | for (auto [idOrPath, pos] : SkMakeZip(fIDsOrPaths, fPositions)) { |
344 | | // Transform the glyph to source space. |
345 | 15.3k | SkMatrix pathMatrix = strikeToSource; |
346 | 15.3k | pathMatrix.postTranslate(pos.x(), pos.y()); |
347 | | |
348 | 15.3k | SkAutoCanvasRestore acr(canvas, true); |
349 | 15.3k | canvas->concat(pathMatrix); |
350 | 15.3k | canvas->drawPath(idOrPath.fPath, runPaint); |
351 | 15.3k | } |
352 | 1.84k | } else { |
353 | | // Transform the path to device because the deviceMatrix must be unchanged to |
354 | | // draw effect, filter or shader paths. |
355 | 35.0k | for (auto [idOrPath, pos] : SkMakeZip(fIDsOrPaths, fPositions)) { |
356 | | // Transform the glyph to source space. |
357 | 35.0k | SkMatrix pathMatrix = strikeToSource; |
358 | 35.0k | pathMatrix.postTranslate(pos.x(), pos.y()); |
359 | | |
360 | 35.0k | SkPath deviceOutline; |
361 | 35.0k | idOrPath.fPath.transform(pathMatrix, &deviceOutline); |
362 | 35.0k | deviceOutline.setIsVolatile(true); |
363 | 35.0k | canvas->drawPath(deviceOutline, runPaint); |
364 | 35.0k | } |
365 | 1.84k | } |
366 | 2.66k | } |
367 | | |
368 | | // -- PathSubRun ----------------------------------------------------------------------------------- |
369 | | class PathSubRun final : public SubRun { |
370 | | public: |
371 | 2.53k | PathSubRun(PathOpSubmitter&& pathDrawing) : fPathDrawing(std::move(pathDrawing)) {} |
372 | | |
373 | | static SubRunOwner Make(SkZip<const SkGlyphID, const SkPoint> accepted, |
374 | | bool isAntiAliased, |
375 | | SkScalar strikeToSourceScale, |
376 | | SkStrikePromise&& strikePromise, |
377 | 2.53k | SubRunAllocator* alloc) { |
378 | 2.53k | return alloc->makeUnique<PathSubRun>( |
379 | 2.53k | PathOpSubmitter::Make( |
380 | 2.53k | accepted, isAntiAliased, strikeToSourceScale, std::move(strikePromise), alloc)); |
381 | 2.53k | } |
382 | | |
383 | | void draw(SkCanvas* canvas, |
384 | | SkPoint drawOrigin, |
385 | | const SkPaint& paint, |
386 | | sk_sp<SkRefCnt>, |
387 | 2.66k | const AtlasDrawDelegate&) const override { |
388 | 2.66k | fPathDrawing.submitDraws(canvas, drawOrigin, paint); |
389 | 2.66k | } |
390 | | |
391 | | int unflattenSize() const override; |
392 | | |
393 | 128 | bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const override { |
394 | 128 | return true; |
395 | 128 | } |
396 | 0 | const AtlasSubRun* testingOnly_atlasSubRun() const override { return nullptr; } |
397 | | static SubRunOwner MakeFromBuffer(SkReadBuffer& buffer, |
398 | | SubRunAllocator* alloc, |
399 | | const SkStrikeClient* client); |
400 | | |
401 | | protected: |
402 | 0 | SubRunStreamTag subRunStreamTag() const override { return SubRunStreamTag::kPathStreamTag; } |
403 | | void doFlatten(SkWriteBuffer& buffer) const override; |
404 | | |
405 | | private: |
406 | | PathOpSubmitter fPathDrawing; |
407 | | }; |
408 | | |
409 | 0 | int PathSubRun::unflattenSize() const { |
410 | 0 | return sizeof(PathSubRun) + fPathDrawing.unflattenSize(); |
411 | 0 | } |
412 | | |
413 | 0 | void PathSubRun::doFlatten(SkWriteBuffer& buffer) const { |
414 | 0 | fPathDrawing.flatten(buffer); |
415 | 0 | } |
416 | | |
417 | | SubRunOwner PathSubRun::MakeFromBuffer(SkReadBuffer& buffer, |
418 | | SubRunAllocator* alloc, |
419 | 0 | const SkStrikeClient* client) { |
420 | 0 | auto pathOpSubmitter = PathOpSubmitter::MakeFromBuffer(buffer, alloc, client); |
421 | 0 | if (!buffer.validate(pathOpSubmitter.has_value())) { return nullptr; } |
422 | 0 | return alloc->makeUnique<PathSubRun>(std::move(*pathOpSubmitter)); |
423 | 0 | } |
424 | | |
425 | | // -- DrawableOpSubmitter -------------------------------------------------------------------------- |
426 | | // Shared code for submitting GPU ops for drawing glyphs as drawables. |
427 | | class DrawableOpSubmitter { |
428 | | public: |
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 | 2 | , fStrikePromise{std::move(that.fStrikePromise)} {} |
437 | 0 | DrawableOpSubmitter& operator=(DrawableOpSubmitter&& that) { |
438 | 0 | this->~DrawableOpSubmitter(); |
439 | 0 | new (this) DrawableOpSubmitter{std::move(that)}; |
440 | 0 | return *this; |
441 | 0 | } |
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 | 2 | SubRunAllocator* alloc) { |
451 | 39 | auto mapToIDOrDrawable = [](const SkGlyphID glyphID) { return IDOrDrawable{glyphID}; }; |
452 | | |
453 | 2 | return DrawableOpSubmitter{ |
454 | 2 | strikeToSourceScale, |
455 | 2 | alloc->makePODSpan(get_positions(accepted)), |
456 | 2 | alloc->makePODArray<IDOrDrawable>(get_glyphIDs(accepted), mapToIDOrDrawable), |
457 | 2 | std::move(strikePromise)}; |
458 | 2 | } |
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 | | |
467 | | private: |
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 | | |
477 | 0 | int DrawableOpSubmitter::unflattenSize() const { |
478 | 0 | return fPositions.size_bytes() + fIDsOrDrawables.size_bytes(); |
479 | 0 | } |
480 | | |
481 | 0 | void DrawableOpSubmitter::flatten(SkWriteBuffer& buffer) const { |
482 | 0 | fStrikePromise.flatten(buffer); |
483 | |
|
484 | 0 | buffer.writeScalar(fStrikeToSourceScale); |
485 | 0 | buffer.writePointArray(fPositions.data(), SkCount(fPositions)); |
486 | 0 | for (IDOrDrawable idOrDrawable : fIDsOrDrawables) { |
487 | 0 | buffer.writeInt(idOrDrawable.fGlyphID); |
488 | 0 | } |
489 | 0 | } |
490 | | |
491 | | std::optional<DrawableOpSubmitter> DrawableOpSubmitter::MakeFromBuffer( |
492 | 0 | SkReadBuffer& buffer, SubRunAllocator* alloc, const SkStrikeClient* client) { |
493 | 0 | std::optional<SkStrikePromise> strikePromise = |
494 | 0 | SkStrikePromise::MakeFromBuffer(buffer, client, SkStrikeCache::GlobalStrikeCache()); |
495 | 0 | if (!buffer.validate(strikePromise.has_value())) { |
496 | 0 | return std::nullopt; |
497 | 0 | } |
498 | | |
499 | 0 | SkScalar strikeToSourceScale = buffer.readScalar(); |
500 | 0 | if (!buffer.validate(0 < strikeToSourceScale)) { return std::nullopt; } |
501 | | |
502 | 0 | SkSpan<SkPoint> positions = MakePointsFromBuffer(buffer, alloc); |
503 | 0 | if (positions.empty()) { return std::nullopt; } |
504 | 0 | const int glyphCount = SkCount(positions); |
505 | |
|
506 | 0 | if (!buffer.validateCanReadN<int>(glyphCount)) { return std::nullopt; } |
507 | 0 | auto idsOrDrawables = alloc->makePODArray<IDOrDrawable>(glyphCount); |
508 | 0 | for (int i = 0; i < SkToInt(glyphCount); ++i) { |
509 | | // Remember, we stored an int for glyph id. |
510 | 0 | idsOrDrawables[i].fGlyphID = SkTo<SkGlyphID>(buffer.readInt()); |
511 | 0 | } |
512 | |
|
513 | 0 | SkASSERT(buffer.isValid()); |
514 | 0 | return DrawableOpSubmitter{strikeToSourceScale, |
515 | 0 | positions, |
516 | 0 | SkSpan(idsOrDrawables, glyphCount), |
517 | 0 | std::move(strikePromise.value())}; |
518 | 0 | } Unexecuted instantiation: SubRunContainer.cpp:(anonymous namespace)::DrawableOpSubmitter::MakeFromBuffer(SkReadBuffer&, sktext::gpu::SubRunAllocator*, SkStrikeClient const*) Unexecuted instantiation: SubRunContainer.cpp:(anonymous namespace)::DrawableOpSubmitter::MakeFromBuffer(SkReadBuffer&, sktext::gpu::SubRunAllocator*, SkStrikeClient const*) |
519 | | |
520 | | DrawableOpSubmitter::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 | 2 | , fStrikePromise(std::move(strikePromise)) { |
529 | 2 | SkASSERT(!fPositions.empty()); |
530 | 2 | } |
531 | | |
532 | | void |
533 | 2 | DrawableOpSubmitter::submitDraws(SkCanvas* canvas, SkPoint drawOrigin,const SkPaint& paint) const { |
534 | | // Convert glyph IDs to Drawables if it hasn't been done yet. |
535 | 2 | fConvertIDsToDrawables([&]() { |
536 | 2 | 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 | 2 | }); |
540 | | |
541 | | // Calculate the matrix that maps the path glyphs from their size in the strike to |
542 | | // the graphics source space. |
543 | 2 | SkMatrix strikeToSource = SkMatrix::Scale(fStrikeToSourceScale, fStrikeToSourceScale); |
544 | 2 | 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 | 39 | for (auto [i, position] : SkMakeEnumerate(fPositions)) { |
549 | 39 | SkDrawable* drawable = fIDsOrDrawables[i].fDrawable; |
550 | | |
551 | 39 | if (drawable == nullptr) { |
552 | | // This better be pinned to keep the drawable data alive. |
553 | 0 | fStrikePromise.strike()->verifyPinnedStrike(); |
554 | 0 | SkDEBUGFAIL("Drawable should not be nullptr."); |
555 | 0 | continue; |
556 | 0 | } |
557 | | |
558 | | // Transform the glyph to source space. |
559 | 39 | SkMatrix pathMatrix = strikeToSource; |
560 | 39 | pathMatrix.postTranslate(position.x(), position.y()); |
561 | | |
562 | 39 | SkAutoCanvasRestore acr(canvas, false); |
563 | 39 | SkRect drawableBounds = drawable->getBounds(); |
564 | 39 | pathMatrix.mapRect(&drawableBounds); |
565 | 39 | canvas->saveLayer(&drawableBounds, &paint); |
566 | 39 | drawable->draw(canvas, &pathMatrix); |
567 | 39 | } |
568 | 2 | } |
569 | | |
570 | | // -- DrawableSubRun ------------------------------------------------------------------------------- |
571 | | class DrawableSubRun : public SubRun { |
572 | | public: |
573 | | DrawableSubRun(DrawableOpSubmitter&& drawingDrawing) |
574 | 2 | : fDrawingDrawing(std::move(drawingDrawing)) {} |
575 | | |
576 | | static SubRunOwner Make(SkZip<const SkGlyphID, const SkPoint> drawables, |
577 | | SkScalar strikeToSourceScale, |
578 | | SkStrikePromise&& strikePromise, |
579 | 2 | SubRunAllocator* alloc) { |
580 | 2 | return alloc->makeUnique<DrawableSubRun>( |
581 | 2 | DrawableOpSubmitter::Make(drawables, |
582 | 2 | strikeToSourceScale, |
583 | 2 | std::move(strikePromise), |
584 | 2 | alloc)); |
585 | 2 | } |
586 | | |
587 | | static SubRunOwner MakeFromBuffer(SkReadBuffer& buffer, |
588 | | SubRunAllocator* alloc, |
589 | | const SkStrikeClient* client); |
590 | | |
591 | | void draw(SkCanvas* canvas, |
592 | | SkPoint drawOrigin, |
593 | | const SkPaint& paint, |
594 | | sk_sp<SkRefCnt>, |
595 | 2 | const AtlasDrawDelegate&) const override { |
596 | 2 | fDrawingDrawing.submitDraws(canvas, drawOrigin, paint); |
597 | 2 | } |
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 | | |
605 | | protected: |
606 | 0 | SubRunStreamTag subRunStreamTag() const override { return SubRunStreamTag::kDrawableStreamTag; } |
607 | | void doFlatten(SkWriteBuffer& buffer) const override; |
608 | | |
609 | | private: |
610 | | DrawableOpSubmitter fDrawingDrawing; |
611 | | }; |
612 | | |
613 | 0 | int DrawableSubRun::unflattenSize() const { |
614 | 0 | return sizeof(DrawableSubRun) + fDrawingDrawing.unflattenSize(); |
615 | 0 | } |
616 | | |
617 | 0 | void DrawableSubRun::doFlatten(SkWriteBuffer& buffer) const { |
618 | 0 | fDrawingDrawing.flatten(buffer); |
619 | 0 | } |
620 | | |
621 | | SubRunOwner DrawableSubRun::MakeFromBuffer(SkReadBuffer& buffer, |
622 | | SubRunAllocator* alloc, |
623 | 0 | const SkStrikeClient* client) { |
624 | 0 | auto drawableOpSubmitter = DrawableOpSubmitter::MakeFromBuffer(buffer, alloc, client); |
625 | 0 | if (!buffer.validate(drawableOpSubmitter.has_value())) { return nullptr; } |
626 | 0 | return alloc->makeUnique<DrawableSubRun>(std::move(*drawableOpSubmitter)); |
627 | 0 | } |
628 | | |
629 | 0 | bool DrawableSubRun::canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const { |
630 | 0 | return true; |
631 | 0 | } |
632 | | |
633 | 0 | const AtlasSubRun* DrawableSubRun::testingOnly_atlasSubRun() const { |
634 | 0 | return nullptr; |
635 | 0 | } |
636 | | |
637 | | #if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) |
638 | | enum ClipMethod { |
639 | | kClippedOut, |
640 | | kUnclipped, |
641 | | kGPUClipped, |
642 | | kGeometryClipped |
643 | | }; |
644 | | |
645 | | std::tuple<ClipMethod, SkIRect> |
646 | 796 | calculate_clip(const GrClip* clip, SkRect deviceBounds, SkRect glyphBounds) { |
647 | 796 | if (clip == nullptr && !deviceBounds.intersects(glyphBounds)) { |
648 | 0 | return {kClippedOut, SkIRect::MakeEmpty()}; |
649 | 796 | } else if (clip != nullptr) { |
650 | 796 | switch (auto result = clip->preApply(glyphBounds, GrAA::kNo); result.fEffect) { |
651 | 181 | case GrClip::Effect::kClippedOut: |
652 | 181 | return {kClippedOut, SkIRect::MakeEmpty()}; |
653 | 234 | case GrClip::Effect::kUnclipped: |
654 | 234 | return {kUnclipped, SkIRect::MakeEmpty()}; |
655 | 381 | case GrClip::Effect::kClipped: { |
656 | 381 | if (result.fIsRRect && result.fRRect.isRect()) { |
657 | 325 | SkRect r = result.fRRect.rect(); |
658 | 325 | if (result.fAA == GrAA::kNo || GrClip::IsPixelAligned(r)) { |
659 | 305 | SkIRect clipRect = SkIRect::MakeEmpty(); |
660 | | // Clip geometrically during onPrepare using clipRect. |
661 | 305 | r.round(&clipRect); |
662 | 305 | if (clipRect.contains(glyphBounds)) { |
663 | | // If fully within the clip, signal no clipping using the empty rect. |
664 | 0 | return {kUnclipped, SkIRect::MakeEmpty()}; |
665 | 0 | } |
666 | | // Use the clipRect to clip the geometry. |
667 | 305 | return {kGeometryClipped, clipRect}; |
668 | 305 | } |
669 | | // Partial pixel clipped at this point. Have the GPU handle it. |
670 | 325 | } |
671 | 381 | } |
672 | 76 | break; |
673 | 796 | } |
674 | 796 | } |
675 | 76 | return {kGPUClipped, SkIRect::MakeEmpty()}; |
676 | 796 | } |
677 | | #endif // defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) |
678 | | |
679 | | // -- DirectMaskSubRun ----------------------------------------------------------------------------- |
680 | | class DirectMaskSubRun final : public SubRun, public AtlasSubRun { |
681 | | public: |
682 | | DirectMaskSubRun(VertexFiller&& vertexFiller, |
683 | | GlyphVector&& glyphs) |
684 | | : fVertexFiller{std::move(vertexFiller)} |
685 | 701 | , fGlyphs{std::move(glyphs)} {} |
686 | | |
687 | | static SubRunOwner Make(SkRect creationBounds, |
688 | | SkZip<const SkPackedGlyphID, const SkPoint> accepted, |
689 | | const SkMatrix& creationMatrix, |
690 | | SkStrikePromise&& strikePromise, |
691 | | MaskFormat maskType, |
692 | 701 | SubRunAllocator* alloc) { |
693 | 701 | auto vertexFiller = VertexFiller::Make(maskType, |
694 | 701 | creationMatrix, |
695 | 701 | creationBounds, |
696 | 701 | get_positions(accepted), |
697 | 701 | alloc, |
698 | 701 | kIsDirect); |
699 | | |
700 | 701 | auto glyphVector = |
701 | 701 | GlyphVector::Make(std::move(strikePromise), get_packedIDs(accepted), alloc); |
702 | | |
703 | 701 | return alloc->makeUnique<DirectMaskSubRun>(std::move(vertexFiller), std::move(glyphVector)); |
704 | 701 | } |
705 | | |
706 | | static SubRunOwner MakeFromBuffer(SkReadBuffer& buffer, |
707 | | SubRunAllocator* alloc, |
708 | 0 | const SkStrikeClient* client) { |
709 | 0 | auto vertexFiller = VertexFiller::MakeFromBuffer(buffer, alloc); |
710 | 0 | if (!buffer.validate(vertexFiller.has_value())) { return nullptr; } |
711 | | |
712 | 0 | auto glyphVector = GlyphVector::MakeFromBuffer(buffer, client, alloc); |
713 | 0 | if (!buffer.validate(glyphVector.has_value())) { return nullptr; } |
714 | 0 | if (!buffer.validate(SkCount(glyphVector->glyphs()) == vertexFiller->count())) { |
715 | 0 | return nullptr; |
716 | 0 | } |
717 | | |
718 | 0 | SkASSERT(buffer.isValid()); |
719 | 0 | return alloc->makeUnique<DirectMaskSubRun>( |
720 | 0 | std::move(*vertexFiller), std::move(*glyphVector)); |
721 | 0 | } Unexecuted instantiation: SubRunContainer.cpp:(anonymous namespace)::DirectMaskSubRun::MakeFromBuffer(SkReadBuffer&, sktext::gpu::SubRunAllocator*, SkStrikeClient const*) Unexecuted instantiation: SubRunContainer.cpp:(anonymous namespace)::DirectMaskSubRun::MakeFromBuffer(SkReadBuffer&, sktext::gpu::SubRunAllocator*, SkStrikeClient const*) |
722 | | |
723 | | void draw(SkCanvas*, |
724 | | SkPoint drawOrigin, |
725 | | const SkPaint& paint, |
726 | | sk_sp<SkRefCnt> subRunStorage, |
727 | 868 | const AtlasDrawDelegate& drawAtlas) const override { |
728 | 868 | drawAtlas(this, drawOrigin, paint, std::move(subRunStorage), |
729 | 868 | {/* isSDF = */false, fVertexFiller.isLCD()}); |
730 | 868 | } |
731 | | |
732 | 0 | int unflattenSize() const override { |
733 | 0 | return sizeof(DirectMaskSubRun) + |
734 | 0 | fGlyphs.unflattenSize() + |
735 | 0 | fVertexFiller.unflattenSize(); |
736 | 0 | } |
737 | | |
738 | 1.22k | int glyphCount() const override { |
739 | 1.22k | return SkCount(fGlyphs.glyphs()); |
740 | 1.22k | } |
741 | | |
742 | 0 | SkSpan<const Glyph*> glyphs() const override { |
743 | 0 | return fGlyphs.glyphs(); |
744 | 0 | } |
745 | | |
746 | 0 | MaskFormat maskFormat() const override { return fVertexFiller.grMaskType(); } |
747 | | |
748 | 614 | int glyphSrcPadding() const override { return 0; } |
749 | | |
750 | 0 | unsigned short instanceFlags() const override { |
751 | 0 | return (unsigned short)fVertexFiller.grMaskType(); |
752 | 0 | } |
753 | | |
754 | 0 | void testingOnly_packedGlyphIDToGlyph(StrikeCache* cache) const override { |
755 | 0 | fGlyphs.packedGlyphIDToGlyph(cache); |
756 | 0 | } |
757 | | |
758 | | #if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) |
759 | 0 | size_t vertexStride(const SkMatrix& drawMatrix) const override { |
760 | 0 | return fVertexFiller.vertexStride(drawMatrix); |
761 | 0 | } |
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 | 868 | skgpu::ganesh::SurfaceDrawContext* sdc) const override { |
770 | 868 | SkASSERT(this->glyphCount() != 0); |
771 | 868 | const SkMatrix& positionMatrix = position_matrix(viewMatrix, drawOrigin); |
772 | | |
773 | 868 | auto [integerTranslate, subRunDeviceBounds] = |
774 | 868 | fVertexFiller.deviceRectAndCheckTransform(positionMatrix); |
775 | 868 | if (subRunDeviceBounds.isEmpty()) { |
776 | 72 | return {nullptr, nullptr}; |
777 | 72 | } |
778 | | // Rect for optimized bounds clipping when doing an integer translate. |
779 | 796 | SkIRect geometricClipRect = SkIRect::MakeEmpty(); |
780 | 796 | 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 | 796 | const SkRect deviceBounds = SkRect::MakeWH(sdc->width(), sdc->height()); |
785 | 796 | auto [clipMethod, clipRect] = calculate_clip(clip, deviceBounds, subRunDeviceBounds); |
786 | | |
787 | 796 | switch (clipMethod) { |
788 | 181 | case kClippedOut: |
789 | | // Returning nullptr as op means skip this op. |
790 | 181 | return {nullptr, nullptr}; |
791 | 234 | case kUnclipped: |
792 | 539 | case kGeometryClipped: |
793 | | // GPU clip is not needed. |
794 | 539 | clip = nullptr; |
795 | 539 | break; |
796 | 76 | case kGPUClipped: |
797 | | // Use th GPU clip; clipRect is ignored. |
798 | 76 | break; |
799 | 796 | } |
800 | 615 | geometricClipRect = clipRect; |
801 | | |
802 | 615 | if (!geometricClipRect.isEmpty()) { SkASSERT(clip == nullptr); } |
803 | 615 | } |
804 | | |
805 | 615 | GrPaint grPaint; |
806 | 615 | const SkPMColor4f drawingColor = calculate_colors(sdc, |
807 | 615 | paint, |
808 | 615 | viewMatrix, |
809 | 615 | fVertexFiller.grMaskType(), |
810 | 615 | &grPaint); |
811 | | |
812 | 615 | auto geometry = AtlasTextOp::Geometry::Make(*this, |
813 | 615 | viewMatrix, |
814 | 615 | drawOrigin, |
815 | 615 | geometricClipRect, |
816 | 615 | std::move(subRunStorage), |
817 | 615 | drawingColor, |
818 | 615 | sdc->arenaAlloc()); |
819 | | |
820 | 615 | GrRecordingContext* const rContext = sdc->recordingContext(); |
821 | | |
822 | 615 | GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext, |
823 | 615 | fVertexFiller.opMaskType(), |
824 | 615 | !integerTranslate, |
825 | 615 | this->glyphCount(), |
826 | 615 | subRunDeviceBounds, |
827 | 615 | geometry, |
828 | 615 | sdc->colorInfo(), |
829 | 615 | std::move(grPaint)); |
830 | 615 | return {clip, std::move(op)}; |
831 | 796 | } SubRunContainer.cpp:(anonymous namespace)::DirectMaskSubRun::makeAtlasTextOp(GrClip const*, SkMatrix const&, SkPoint, SkPaint const&, sk_sp<SkRefCnt>&&, skgpu::ganesh::SurfaceDrawContext*) const Line | Count | Source | 769 | 868 | skgpu::ganesh::SurfaceDrawContext* sdc) const override { | 770 | 868 | SkASSERT(this->glyphCount() != 0); | 771 | 868 | const SkMatrix& positionMatrix = position_matrix(viewMatrix, drawOrigin); | 772 | | | 773 | 868 | auto [integerTranslate, subRunDeviceBounds] = | 774 | 868 | fVertexFiller.deviceRectAndCheckTransform(positionMatrix); | 775 | 868 | if (subRunDeviceBounds.isEmpty()) { | 776 | 72 | return {nullptr, nullptr}; | 777 | 72 | } | 778 | | // Rect for optimized bounds clipping when doing an integer translate. | 779 | 796 | SkIRect geometricClipRect = SkIRect::MakeEmpty(); | 780 | 796 | 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 | 796 | const SkRect deviceBounds = SkRect::MakeWH(sdc->width(), sdc->height()); | 785 | 796 | auto [clipMethod, clipRect] = calculate_clip(clip, deviceBounds, subRunDeviceBounds); | 786 | | | 787 | 796 | switch (clipMethod) { | 788 | 181 | case kClippedOut: | 789 | | // Returning nullptr as op means skip this op. | 790 | 181 | return {nullptr, nullptr}; | 791 | 234 | case kUnclipped: | 792 | 539 | case kGeometryClipped: | 793 | | // GPU clip is not needed. | 794 | 539 | clip = nullptr; | 795 | 539 | break; | 796 | 76 | case kGPUClipped: | 797 | | // Use th GPU clip; clipRect is ignored. | 798 | 76 | break; | 799 | 796 | } | 800 | 615 | geometricClipRect = clipRect; | 801 | | | 802 | 615 | if (!geometricClipRect.isEmpty()) { SkASSERT(clip == nullptr); } | 803 | 615 | } | 804 | | | 805 | 615 | GrPaint grPaint; | 806 | 615 | const SkPMColor4f drawingColor = calculate_colors(sdc, | 807 | 615 | paint, | 808 | 615 | viewMatrix, | 809 | 615 | fVertexFiller.grMaskType(), | 810 | 615 | &grPaint); | 811 | | | 812 | 615 | auto geometry = AtlasTextOp::Geometry::Make(*this, | 813 | 615 | viewMatrix, | 814 | 615 | drawOrigin, | 815 | 615 | geometricClipRect, | 816 | 615 | std::move(subRunStorage), | 817 | 615 | drawingColor, | 818 | 615 | sdc->arenaAlloc()); | 819 | | | 820 | 615 | GrRecordingContext* const rContext = sdc->recordingContext(); | 821 | | | 822 | 615 | GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext, | 823 | 615 | fVertexFiller.opMaskType(), | 824 | 615 | !integerTranslate, | 825 | 615 | this->glyphCount(), | 826 | 615 | subRunDeviceBounds, | 827 | 615 | geometry, | 828 | 615 | sdc->colorInfo(), | 829 | 615 | std::move(grPaint)); | 830 | 615 | return {clip, std::move(op)}; | 831 | 796 | } |
Unexecuted instantiation: SubRunContainer.cpp:(anonymous namespace)::DirectMaskSubRun::makeAtlasTextOp(GrClip const*, SkMatrix const&, SkPoint, SkPaint const&, sk_sp<SkRefCnt>&&, skgpu::ganesh::SurfaceDrawContext*) const |
832 | | |
833 | | void fillVertexData(void* vertexDst, int offset, int count, |
834 | | GrColor color, |
835 | | const SkMatrix& drawMatrix, SkPoint drawOrigin, |
836 | 614 | SkIRect clip) const override { |
837 | 614 | const SkMatrix positionMatrix = position_matrix(drawMatrix, drawOrigin); |
838 | 614 | fVertexFiller.fillVertexData(offset, count, |
839 | 614 | fGlyphs.glyphs(), |
840 | 614 | color, |
841 | 614 | positionMatrix, |
842 | 614 | clip, |
843 | 614 | vertexDst); |
844 | 614 | } |
845 | | #endif // defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) |
846 | | |
847 | | std::tuple<bool, int> regenerateAtlas(int begin, int end, |
848 | 614 | RegenerateAtlasDelegate regenerateAtlas) const override { |
849 | 614 | return regenerateAtlas( |
850 | 614 | &fGlyphs, begin, end, fVertexFiller.grMaskType(), this->glyphSrcPadding()); |
851 | 614 | } |
852 | | |
853 | 0 | const VertexFiller& vertexFiller() const override { return fVertexFiller; } |
854 | | |
855 | 167 | bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const override { |
856 | 167 | auto [reuse, _] = fVertexFiller.deviceRectAndCheckTransform(positionMatrix); |
857 | 167 | return reuse; |
858 | 167 | } |
859 | | |
860 | 0 | const AtlasSubRun* testingOnly_atlasSubRun() const override { |
861 | 0 | return this; |
862 | 0 | } |
863 | | |
864 | | protected: |
865 | 0 | SubRunStreamTag subRunStreamTag() const override { |
866 | 0 | return SubRunStreamTag::kDirectMaskStreamTag; |
867 | 0 | } |
868 | | |
869 | 0 | void doFlatten(SkWriteBuffer& buffer) const override { |
870 | 0 | fVertexFiller.flatten(buffer); |
871 | 0 | fGlyphs.flatten(buffer); |
872 | 0 | } |
873 | | |
874 | | private: |
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 ------------------------------------------------------------------------ |
883 | | class TransformedMaskSubRun final : public SubRun, public AtlasSubRun { |
884 | | public: |
885 | | TransformedMaskSubRun(bool isBigEnough, |
886 | | VertexFiller&& vertexFiller, |
887 | | GlyphVector&& glyphs) |
888 | | : fIsBigEnough{isBigEnough} |
889 | | , fVertexFiller{std::move(vertexFiller)} |
890 | 0 | , fGlyphs{std::move(glyphs)} {} |
891 | | |
892 | | static SubRunOwner Make(SkZip<const SkPackedGlyphID, const SkPoint> accepted, |
893 | | const SkMatrix& initialPositionMatrix, |
894 | | SkStrikePromise&& strikePromise, |
895 | | SkMatrix creationMatrix, |
896 | | SkRect creationBounds, |
897 | | MaskFormat maskType, |
898 | 0 | SubRunAllocator* alloc) { |
899 | 0 | auto vertexFiller = VertexFiller::Make(maskType, |
900 | 0 | creationMatrix, |
901 | 0 | creationBounds, |
902 | 0 | get_positions(accepted), |
903 | 0 | alloc, |
904 | 0 | kIsTransformed); |
905 | |
|
906 | 0 | auto glyphVector = GlyphVector::Make( |
907 | 0 | std::move(strikePromise), get_packedIDs(accepted), alloc); |
908 | |
|
909 | 0 | return alloc->makeUnique<TransformedMaskSubRun>( |
910 | 0 | initialPositionMatrix.getMaxScale() >= 1, |
911 | 0 | std::move(vertexFiller), |
912 | 0 | std::move(glyphVector)); |
913 | 0 | } |
914 | | |
915 | | static SubRunOwner MakeFromBuffer(SkReadBuffer& buffer, |
916 | | SubRunAllocator* alloc, |
917 | 0 | const SkStrikeClient* client) { |
918 | 0 | auto vertexFiller = VertexFiller::MakeFromBuffer(buffer, alloc); |
919 | 0 | if (!buffer.validate(vertexFiller.has_value())) { return nullptr; } |
920 | | |
921 | 0 | auto glyphVector = GlyphVector::MakeFromBuffer(buffer, client, alloc); |
922 | 0 | if (!buffer.validate(glyphVector.has_value())) { return nullptr; } |
923 | 0 | if (!buffer.validate(SkCount(glyphVector->glyphs()) == vertexFiller->count())) { |
924 | 0 | return nullptr; |
925 | 0 | } |
926 | 0 | const bool isBigEnough = buffer.readBool(); |
927 | 0 | return alloc->makeUnique<TransformedMaskSubRun>( |
928 | 0 | isBigEnough, std::move(*vertexFiller), std::move(*glyphVector)); |
929 | 0 | } |
930 | | |
931 | 0 | int unflattenSize() const override { |
932 | 0 | return sizeof(TransformedMaskSubRun) + |
933 | 0 | fGlyphs.unflattenSize() + |
934 | 0 | fVertexFiller.unflattenSize(); |
935 | 0 | } |
936 | | |
937 | 0 | 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 | 0 | return fIsBigEnough; |
941 | 0 | } |
942 | | |
943 | 0 | const AtlasSubRun* testingOnly_atlasSubRun() const override { return this; } |
944 | | |
945 | 0 | void testingOnly_packedGlyphIDToGlyph(StrikeCache *cache) const override { |
946 | 0 | fGlyphs.packedGlyphIDToGlyph(cache); |
947 | 0 | } |
948 | | |
949 | 0 | int glyphCount() const override { return SkCount(fGlyphs.glyphs()); } |
950 | | |
951 | 0 | SkSpan<const Glyph*> glyphs() const override { |
952 | 0 | return fGlyphs.glyphs(); |
953 | 0 | } |
954 | | |
955 | 0 | unsigned short instanceFlags() const override { |
956 | 0 | return (unsigned short)fVertexFiller.grMaskType(); |
957 | 0 | } |
958 | | |
959 | 0 | MaskFormat maskFormat() const override { return fVertexFiller.grMaskType(); } |
960 | | |
961 | 0 | int glyphSrcPadding() const override { return 1; } |
962 | | |
963 | | void draw(SkCanvas*, |
964 | | SkPoint drawOrigin, |
965 | | const SkPaint& paint, |
966 | | sk_sp<SkRefCnt> subRunStorage, |
967 | 0 | const AtlasDrawDelegate& drawAtlas) const override { |
968 | 0 | drawAtlas(this, drawOrigin, paint, std::move(subRunStorage), |
969 | 0 | {/* isSDF = */false, fVertexFiller.isLCD()}); |
970 | 0 | } |
971 | | |
972 | | #if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) |
973 | | |
974 | 0 | size_t vertexStride(const SkMatrix& drawMatrix) const override { |
975 | 0 | return fVertexFiller.vertexStride(drawMatrix); |
976 | 0 | } |
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 | 0 | skgpu::ganesh::SurfaceDrawContext* sdc) const override { |
985 | 0 | SkASSERT(this->glyphCount() != 0); |
986 | |
|
987 | 0 | GrPaint grPaint; |
988 | 0 | SkPMColor4f drawingColor = calculate_colors(sdc, |
989 | 0 | paint, |
990 | 0 | viewMatrix, |
991 | 0 | fVertexFiller.grMaskType(), |
992 | 0 | &grPaint); |
993 | |
|
994 | 0 | auto geometry = AtlasTextOp::Geometry::Make(*this, |
995 | 0 | viewMatrix, |
996 | 0 | drawOrigin, |
997 | 0 | SkIRect::MakeEmpty(), |
998 | 0 | std::move(subRunStorage), |
999 | 0 | drawingColor, |
1000 | 0 | sdc->arenaAlloc()); |
1001 | |
|
1002 | 0 | GrRecordingContext* const rContext = sdc->recordingContext(); |
1003 | 0 | SkMatrix positionMatrix = position_matrix(viewMatrix, drawOrigin); |
1004 | 0 | auto [_, deviceRect] = fVertexFiller.deviceRectAndCheckTransform(positionMatrix); |
1005 | 0 | GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext, |
1006 | 0 | fVertexFiller.opMaskType(), |
1007 | 0 | true, |
1008 | 0 | this->glyphCount(), |
1009 | 0 | deviceRect, |
1010 | 0 | geometry, |
1011 | 0 | sdc->colorInfo(), |
1012 | 0 | std::move(grPaint)); |
1013 | 0 | return {clip, std::move(op)}; |
1014 | 0 | } Unexecuted instantiation: SubRunContainer.cpp:(anonymous namespace)::TransformedMaskSubRun::makeAtlasTextOp(GrClip const*, SkMatrix const&, SkPoint, SkPaint const&, sk_sp<SkRefCnt>&&, skgpu::ganesh::SurfaceDrawContext*) const Unexecuted instantiation: SubRunContainer.cpp:(anonymous namespace)::TransformedMaskSubRun::makeAtlasTextOp(GrClip const*, SkMatrix const&, SkPoint, SkPaint const&, sk_sp<SkRefCnt>&&, skgpu::ganesh::SurfaceDrawContext*) const |
1015 | | |
1016 | | void fillVertexData( |
1017 | | void* vertexDst, int offset, int count, |
1018 | | GrColor color, |
1019 | | const SkMatrix& drawMatrix, SkPoint drawOrigin, |
1020 | 0 | SkIRect clip) const override { |
1021 | 0 | const SkMatrix positionMatrix = position_matrix(drawMatrix, drawOrigin); |
1022 | 0 | fVertexFiller.fillVertexData(offset, count, |
1023 | 0 | fGlyphs.glyphs(), |
1024 | 0 | color, |
1025 | 0 | positionMatrix, |
1026 | 0 | clip, |
1027 | 0 | vertexDst); |
1028 | 0 | } |
1029 | | #endif // defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) |
1030 | | |
1031 | | std::tuple<bool, int> regenerateAtlas(int begin, int end, |
1032 | 0 | RegenerateAtlasDelegate regenerateAtlas) const override { |
1033 | 0 | return regenerateAtlas( |
1034 | 0 | &fGlyphs, begin, end, fVertexFiller.grMaskType(), this->glyphSrcPadding()); |
1035 | 0 | } |
1036 | | |
1037 | 0 | const VertexFiller& vertexFiller() const override { return fVertexFiller; } |
1038 | | |
1039 | | protected: |
1040 | 0 | SubRunStreamTag subRunStreamTag() const override { |
1041 | 0 | return SubRunStreamTag::kTransformMaskStreamTag; |
1042 | 0 | } |
1043 | | |
1044 | 0 | void doFlatten(SkWriteBuffer& buffer) const override { |
1045 | 0 | fVertexFiller.flatten(buffer); |
1046 | 0 | fGlyphs.flatten(buffer); |
1047 | 0 | buffer.writeBool(fIsBigEnough); |
1048 | 0 | } |
1049 | | |
1050 | | private: |
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 | | |
1062 | 2.67k | bool has_some_antialiasing(const SkFont& font ) { |
1063 | 2.67k | SkFont::Edging edging = font.getEdging(); |
1064 | 2.67k | return edging == SkFont::Edging::kAntiAlias |
1065 | 2.67k | || edging == SkFont::Edging::kSubpixelAntiAlias; |
1066 | 2.67k | } |
1067 | | |
1068 | | #if !defined(SK_DISABLE_SDF_TEXT) |
1069 | | |
1070 | | #if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) |
1071 | | |
1072 | | static std::tuple<AtlasTextOp::MaskType, uint32_t, bool> calculate_sdf_parameters( |
1073 | | const skgpu::ganesh::SurfaceDrawContext& sdc, |
1074 | | const SkMatrix& drawMatrix, |
1075 | | bool useLCDText, |
1076 | 162 | bool isAntiAliased) { |
1077 | 162 | const GrColorInfo& colorInfo = sdc.colorInfo(); |
1078 | 162 | const SkSurfaceProps& props = sdc.surfaceProps(); |
1079 | 162 | bool isBGR = SkPixelGeometryIsBGR(props.pixelGeometry()); |
1080 | 162 | bool isLCD = useLCDText && SkPixelGeometryIsH(props.pixelGeometry()); |
1081 | 162 | using MT = AtlasTextOp::MaskType; |
1082 | 162 | MT maskType = !isAntiAliased ? MT::kAliasedDistanceField |
1083 | 162 | : isLCD ? (isBGR ? MT::kLCDBGRDistanceField |
1084 | 0 | : MT::kLCDDistanceField) |
1085 | 96 | : MT::kGrayscaleDistanceField; |
1086 | | |
1087 | 162 | bool useGammaCorrectDistanceTable = colorInfo.isLinearlyBlended(); |
1088 | 162 | uint32_t DFGPFlags = drawMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0; |
1089 | 162 | DFGPFlags |= drawMatrix.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0; |
1090 | 162 | DFGPFlags |= useGammaCorrectDistanceTable ? kGammaCorrect_DistanceFieldEffectFlag : 0; |
1091 | 162 | DFGPFlags |= MT::kAliasedDistanceField == maskType ? kAliased_DistanceFieldEffectFlag : 0; |
1092 | 162 | DFGPFlags |= drawMatrix.hasPerspective() ? kPerspective_DistanceFieldEffectFlag : 0; |
1093 | | |
1094 | 162 | if (isLCD) { |
1095 | 0 | DFGPFlags |= kUseLCD_DistanceFieldEffectFlag; |
1096 | 0 | DFGPFlags |= MT::kLCDBGRDistanceField == maskType ? kBGR_DistanceFieldEffectFlag : 0; |
1097 | 0 | } |
1098 | 162 | return {maskType, DFGPFlags, useGammaCorrectDistanceTable}; |
1099 | 162 | } |
1100 | | |
1101 | | #endif // defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) |
1102 | | |
1103 | | class SDFTSubRun final : public SubRun, public AtlasSubRun { |
1104 | | public: |
1105 | | SDFTSubRun(bool useLCDText, |
1106 | | bool antiAliased, |
1107 | | const SDFTMatrixRange& matrixRange, |
1108 | | VertexFiller&& vertexFiller, |
1109 | | GlyphVector&& glyphs) |
1110 | | : fUseLCDText{useLCDText} |
1111 | | , fAntiAliased{antiAliased} |
1112 | | , fMatrixRange{matrixRange} |
1113 | | , fVertexFiller{std::move(vertexFiller)} |
1114 | 138 | , fGlyphs{std::move(glyphs)} { } |
1115 | | |
1116 | | static SubRunOwner Make(SkZip<const SkPackedGlyphID, const SkPoint> accepted, |
1117 | | const SkFont& runFont, |
1118 | | SkStrikePromise&& strikePromise, |
1119 | | const SkMatrix& creationMatrix, |
1120 | | SkRect creationBounds, |
1121 | | const SDFTMatrixRange& matrixRange, |
1122 | 138 | SubRunAllocator* alloc) { |
1123 | 138 | auto vertexFiller = VertexFiller::Make(MaskFormat::kA8, |
1124 | 138 | creationMatrix, |
1125 | 138 | creationBounds, |
1126 | 138 | get_positions(accepted), |
1127 | 138 | alloc, |
1128 | 138 | kIsTransformed); |
1129 | | |
1130 | 138 | auto glyphVector = GlyphVector::Make( |
1131 | 138 | std::move(strikePromise), get_packedIDs(accepted), alloc); |
1132 | | |
1133 | 138 | return alloc->makeUnique<SDFTSubRun>( |
1134 | 138 | runFont.getEdging() == SkFont::Edging::kSubpixelAntiAlias, |
1135 | 138 | has_some_antialiasing(runFont), |
1136 | 138 | matrixRange, |
1137 | 138 | std::move(vertexFiller), |
1138 | 138 | std::move(glyphVector)); |
1139 | 138 | } |
1140 | | |
1141 | | static SubRunOwner MakeFromBuffer(SkReadBuffer& buffer, |
1142 | | SubRunAllocator* alloc, |
1143 | 0 | const SkStrikeClient* client) { |
1144 | 0 | int useLCD = buffer.readInt(); |
1145 | 0 | int isAntiAliased = buffer.readInt(); |
1146 | 0 | SDFTMatrixRange matrixRange = SDFTMatrixRange::MakeFromBuffer(buffer); |
1147 | 0 | auto vertexFiller = VertexFiller::MakeFromBuffer(buffer, alloc); |
1148 | 0 | if (!buffer.validate(vertexFiller.has_value())) { return nullptr; } |
1149 | 0 | if (!buffer.validate(vertexFiller.value().grMaskType() == MaskFormat::kA8)) { |
1150 | 0 | return nullptr; |
1151 | 0 | } |
1152 | 0 | auto glyphVector = GlyphVector::MakeFromBuffer(buffer, client, alloc); |
1153 | 0 | if (!buffer.validate(glyphVector.has_value())) { return nullptr; } |
1154 | 0 | if (!buffer.validate(SkCount(glyphVector->glyphs()) == vertexFiller->count())) { |
1155 | 0 | return nullptr; |
1156 | 0 | } |
1157 | 0 | return alloc->makeUnique<SDFTSubRun>(useLCD, |
1158 | 0 | isAntiAliased, |
1159 | 0 | matrixRange, |
1160 | 0 | std::move(*vertexFiller), |
1161 | 0 | std::move(*glyphVector)); |
1162 | 0 | } |
1163 | | |
1164 | 0 | int unflattenSize() const override { |
1165 | 0 | return sizeof(SDFTSubRun) + fGlyphs.unflattenSize() + fVertexFiller.unflattenSize(); |
1166 | 0 | } |
1167 | | |
1168 | 30 | bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const override { |
1169 | 30 | return fMatrixRange.matrixInRange(positionMatrix); |
1170 | 30 | } |
1171 | | |
1172 | 0 | const AtlasSubRun* testingOnly_atlasSubRun() const override { return this; } |
1173 | | |
1174 | 0 | void testingOnly_packedGlyphIDToGlyph(StrikeCache *cache) const override { |
1175 | 0 | fGlyphs.packedGlyphIDToGlyph(cache); |
1176 | 0 | } |
1177 | | |
1178 | 252 | int glyphCount() const override { return fVertexFiller.count(); } |
1179 | 0 | MaskFormat maskFormat() const override { |
1180 | 0 | SkASSERT(fVertexFiller.grMaskType() == MaskFormat::kA8); |
1181 | 0 | return MaskFormat::kA8; |
1182 | 0 | } Unexecuted instantiation: SubRunContainer.cpp:(anonymous namespace)::SDFTSubRun::maskFormat() const Unexecuted instantiation: SubRunContainer.cpp:(anonymous namespace)::SDFTSubRun::maskFormat() const |
1183 | 90 | int glyphSrcPadding() const override { return SK_DistanceFieldInset; } |
1184 | | |
1185 | 0 | SkSpan<const Glyph*> glyphs() const override { |
1186 | 0 | return fGlyphs.glyphs(); |
1187 | 0 | } |
1188 | | |
1189 | 0 | unsigned short instanceFlags() const override { |
1190 | 0 | return (unsigned short)MaskFormat::kA8; |
1191 | 0 | } |
1192 | | |
1193 | | void draw(SkCanvas*, |
1194 | | SkPoint drawOrigin, |
1195 | | const SkPaint& paint, |
1196 | | sk_sp<SkRefCnt> subRunStorage, |
1197 | 162 | const AtlasDrawDelegate& drawAtlas) const override { |
1198 | 162 | drawAtlas(this, drawOrigin, paint, std::move(subRunStorage), |
1199 | 162 | {/* isSDF = */true, /* isLCD = */fUseLCDText}); |
1200 | 162 | } |
1201 | | |
1202 | | #if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) |
1203 | 0 | size_t vertexStride(const SkMatrix& drawMatrix) const override { |
1204 | 0 | return fVertexFiller.vertexStride(drawMatrix); |
1205 | 0 | } |
1206 | | |
1207 | | std::tuple<const GrClip*, GrOp::Owner> makeAtlasTextOp( |
1208 | | const GrClip* clip, |
1209 | | const SkMatrix& viewMatrix, |
1210 | | SkPoint drawOrigin, |
1211 | | const SkPaint& paint, |
1212 | | sk_sp<SkRefCnt>&& subRunStorage, |
1213 | 162 | skgpu::ganesh::SurfaceDrawContext* sdc) const override { |
1214 | 162 | SkASSERT(this->glyphCount() != 0); |
1215 | | |
1216 | 162 | GrPaint grPaint; |
1217 | 162 | SkPMColor4f drawingColor = calculate_colors(sdc, |
1218 | 162 | paint, |
1219 | 162 | viewMatrix, |
1220 | 162 | MaskFormat::kA8, |
1221 | 162 | &grPaint); |
1222 | | |
1223 | 162 | auto [maskType, DFGPFlags, useGammaCorrectDistanceTable] = |
1224 | 162 | calculate_sdf_parameters(*sdc, viewMatrix, fUseLCDText, fAntiAliased); |
1225 | | |
1226 | 162 | auto geometry = AtlasTextOp::Geometry::Make(*this, |
1227 | 162 | viewMatrix, |
1228 | 162 | drawOrigin, |
1229 | 162 | SkIRect::MakeEmpty(), |
1230 | 162 | std::move(subRunStorage), |
1231 | 162 | drawingColor, |
1232 | 162 | sdc->arenaAlloc()); |
1233 | | |
1234 | 162 | GrRecordingContext* const rContext = sdc->recordingContext(); |
1235 | 162 | SkMatrix positionMatrix = position_matrix(viewMatrix, drawOrigin); |
1236 | 162 | auto [_, deviceRect] = fVertexFiller.deviceRectAndCheckTransform(positionMatrix); |
1237 | 162 | GrOp::Owner op = GrOp::Make<AtlasTextOp>(rContext, |
1238 | 162 | maskType, |
1239 | 162 | true, |
1240 | 162 | this->glyphCount(), |
1241 | 162 | deviceRect, |
1242 | 162 | SkPaintPriv::ComputeLuminanceColor(paint), |
1243 | 162 | useGammaCorrectDistanceTable, |
1244 | 162 | DFGPFlags, |
1245 | 162 | geometry, |
1246 | 162 | std::move(grPaint)); |
1247 | | |
1248 | 162 | return {clip, std::move(op)}; |
1249 | 162 | } |
1250 | | |
1251 | | void fillVertexData( |
1252 | | void *vertexDst, int offset, int count, |
1253 | | GrColor color, |
1254 | | const SkMatrix& drawMatrix, SkPoint drawOrigin, |
1255 | 90 | SkIRect clip) const override { |
1256 | 90 | const SkMatrix positionMatrix = position_matrix(drawMatrix, drawOrigin); |
1257 | | |
1258 | 90 | fVertexFiller.fillVertexData(offset, count, |
1259 | 90 | fGlyphs.glyphs(), |
1260 | 90 | color, |
1261 | 90 | positionMatrix, |
1262 | 90 | clip, |
1263 | 90 | vertexDst); |
1264 | 90 | } |
1265 | | |
1266 | | #endif // defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS) |
1267 | | |
1268 | | std::tuple<bool, int> regenerateAtlas(int begin, int end, |
1269 | 90 | RegenerateAtlasDelegate regenerateAtlas) const override { |
1270 | 90 | return regenerateAtlas(&fGlyphs, begin, end, MaskFormat::kA8, this->glyphSrcPadding()); |
1271 | 90 | } |
1272 | | |
1273 | 0 | const VertexFiller& vertexFiller() const override { return fVertexFiller; } |
1274 | | |
1275 | | protected: |
1276 | 0 | SubRunStreamTag subRunStreamTag() const override { return SubRunStreamTag::kSDFTStreamTag; } |
1277 | 0 | void doFlatten(SkWriteBuffer& buffer) const override { |
1278 | 0 | buffer.writeInt(fUseLCDText); |
1279 | 0 | buffer.writeInt(fAntiAliased); |
1280 | 0 | fMatrixRange.flatten(buffer); |
1281 | 0 | fVertexFiller.flatten(buffer); |
1282 | 0 | fGlyphs.flatten(buffer); |
1283 | 0 | } |
1284 | | |
1285 | | private: |
1286 | | const bool fUseLCDText; |
1287 | | const bool fAntiAliased; |
1288 | | const SDFTMatrixRange fMatrixRange; |
1289 | | |
1290 | | const VertexFiller fVertexFiller; |
1291 | | |
1292 | | // The regenerateAtlas method mutates fGlyphs. It should be called from onPrepare which must |
1293 | | // be single threaded. |
1294 | | mutable GlyphVector fGlyphs; |
1295 | | }; // class SDFTSubRun |
1296 | | |
1297 | | #endif // !defined(SK_DISABLE_SDF_TEXT) |
1298 | | |
1299 | | // -- SubRun --------------------------------------------------------------------------------------- |
1300 | | |
1301 | | template<typename AddSingleMaskFormat> |
1302 | | void add_multi_mask_format( |
1303 | | AddSingleMaskFormat addSingleMaskFormat, |
1304 | 701 | SkZip<const SkPackedGlyphID, const SkPoint, const SkMask::Format> accepted) { |
1305 | 701 | if (accepted.empty()) { return; } |
1306 | | |
1307 | 701 | auto maskSpan = accepted.get<2>(); |
1308 | 701 | MaskFormat format = Glyph::FormatFromSkGlyph(maskSpan[0]); |
1309 | 701 | size_t startIndex = 0; |
1310 | 10.5k | for (size_t i = 1; i < accepted.size(); i++) { |
1311 | 9.88k | MaskFormat nextFormat = Glyph::FormatFromSkGlyph(maskSpan[i]); |
1312 | 9.88k | if (format != nextFormat) { |
1313 | 0 | auto interval = accepted.subspan(startIndex, i - startIndex); |
1314 | | // Only pass the packed glyph ids and positions. |
1315 | 0 | auto glyphsWithSameFormat = SkMakeZip(interval.get<0>(), interval.get<1>()); |
1316 | | // Take a ref on the strike. This should rarely happen. |
1317 | 0 | addSingleMaskFormat(glyphsWithSameFormat, format); |
1318 | 0 | format = nextFormat; |
1319 | 0 | startIndex = i; |
1320 | 0 | } |
1321 | 9.88k | } |
1322 | 701 | auto interval = accepted.last(accepted.size() - startIndex); |
1323 | 701 | auto glyphsWithSameFormat = SkMakeZip(interval.get<0>(), interval.get<1>()); |
1324 | 701 | addSingleMaskFormat(glyphsWithSameFormat, format); |
1325 | 701 | } SubRunContainer.cpp:void (anonymous namespace)::add_multi_mask_format<sktext::gpu::SubRunContainer::MakeInAlloc(sktext::GlyphRunList const&, SkMatrix const&, SkPaint const&, SkStrikeDeviceInfo, sktext::StrikeForGPUCacheInterface*, sktext::gpu::SubRunAllocator*, sktext::gpu::SubRunContainer::SubRunCreationBehavior, char const*)::$_0>(sktext::gpu::SubRunContainer::MakeInAlloc(sktext::GlyphRunList const&, SkMatrix const&, SkPaint const&, SkStrikeDeviceInfo, sktext::StrikeForGPUCacheInterface*, sktext::gpu::SubRunAllocator*, sktext::gpu::SubRunContainer::SubRunCreationBehavior, char const*)::$_0, SkZip<SkPackedGlyphID const, SkPoint const, SkMask::Format const>) Line | Count | Source | 1304 | 701 | SkZip<const SkPackedGlyphID, const SkPoint, const SkMask::Format> accepted) { | 1305 | 701 | if (accepted.empty()) { return; } | 1306 | | | 1307 | 701 | auto maskSpan = accepted.get<2>(); | 1308 | 701 | MaskFormat format = Glyph::FormatFromSkGlyph(maskSpan[0]); | 1309 | 701 | size_t startIndex = 0; | 1310 | 10.5k | for (size_t i = 1; i < accepted.size(); i++) { | 1311 | 9.88k | MaskFormat nextFormat = Glyph::FormatFromSkGlyph(maskSpan[i]); | 1312 | 9.88k | if (format != nextFormat) { | 1313 | 0 | auto interval = accepted.subspan(startIndex, i - startIndex); | 1314 | | // Only pass the packed glyph ids and positions. | 1315 | 0 | auto glyphsWithSameFormat = SkMakeZip(interval.get<0>(), interval.get<1>()); | 1316 | | // Take a ref on the strike. This should rarely happen. | 1317 | 0 | addSingleMaskFormat(glyphsWithSameFormat, format); | 1318 | 0 | format = nextFormat; | 1319 | 0 | startIndex = i; | 1320 | 0 | } | 1321 | 9.88k | } | 1322 | 701 | auto interval = accepted.last(accepted.size() - startIndex); | 1323 | 701 | auto glyphsWithSameFormat = SkMakeZip(interval.get<0>(), interval.get<1>()); | 1324 | 701 | addSingleMaskFormat(glyphsWithSameFormat, format); | 1325 | 701 | } |
Unexecuted instantiation: SubRunContainer.cpp:void (anonymous namespace)::add_multi_mask_format<sktext::gpu::SubRunContainer::MakeInAlloc(sktext::GlyphRunList const&, SkMatrix const&, SkPaint const&, SkStrikeDeviceInfo, sktext::StrikeForGPUCacheInterface*, sktext::gpu::SubRunAllocator*, sktext::gpu::SubRunContainer::SubRunCreationBehavior, char const*)::$_2>(sktext::gpu::SubRunContainer::MakeInAlloc(sktext::GlyphRunList const&, SkMatrix const&, SkPaint const&, SkStrikeDeviceInfo, sktext::StrikeForGPUCacheInterface*, sktext::gpu::SubRunAllocator*, sktext::gpu::SubRunContainer::SubRunCreationBehavior, char const*)::$_2, SkZip<SkPackedGlyphID const, SkPoint const, SkMask::Format const>) Unexecuted instantiation: SubRunContainer.cpp:void (anonymous namespace)::add_multi_mask_format<sktext::gpu::SubRunContainer::MakeInAlloc(sktext::GlyphRunList const&, SkMatrix const&, SkPaint const&, SkStrikeDeviceInfo, sktext::StrikeForGPUCacheInterface*, sktext::gpu::SubRunAllocator*, sktext::gpu::SubRunContainer::SubRunCreationBehavior, char const*)::$_4>(sktext::gpu::SubRunContainer::MakeInAlloc(sktext::GlyphRunList const&, SkMatrix const&, SkPaint const&, SkStrikeDeviceInfo, sktext::StrikeForGPUCacheInterface*, sktext::gpu::SubRunAllocator*, sktext::gpu::SubRunContainer::SubRunCreationBehavior, char const*)::$_4, SkZip<SkPackedGlyphID const, SkPoint const, SkMask::Format const>) |
1326 | | } // namespace |
1327 | | |
1328 | | namespace sktext::gpu { |
1329 | 3.37k | SubRun::~SubRun() = default; |
1330 | 0 | void SubRun::flatten(SkWriteBuffer& buffer) const { |
1331 | 0 | buffer.writeInt(this->subRunStreamTag()); |
1332 | 0 | this->doFlatten(buffer); |
1333 | 0 | } |
1334 | | |
1335 | | SubRunOwner SubRun::MakeFromBuffer(SkReadBuffer& buffer, |
1336 | | SubRunAllocator* alloc, |
1337 | 0 | const SkStrikeClient* client) { |
1338 | 0 | using Maker = SubRunOwner (*)(SkReadBuffer&, |
1339 | 0 | SubRunAllocator*, |
1340 | 0 | const SkStrikeClient*); |
1341 | |
|
1342 | 0 | static Maker makers[kSubRunStreamTagCount] = { |
1343 | 0 | nullptr, // 0 index is bad. |
1344 | 0 | DirectMaskSubRun::MakeFromBuffer, |
1345 | 0 | #if !defined(SK_DISABLE_SDF_TEXT) |
1346 | 0 | SDFTSubRun::MakeFromBuffer, |
1347 | 0 | #endif |
1348 | 0 | TransformedMaskSubRun::MakeFromBuffer, |
1349 | 0 | PathSubRun::MakeFromBuffer, |
1350 | 0 | DrawableSubRun::MakeFromBuffer, |
1351 | 0 | }; |
1352 | 0 | int subRunTypeInt = buffer.readInt(); |
1353 | 0 | SkASSERT(kBad < subRunTypeInt && subRunTypeInt < kSubRunStreamTagCount); |
1354 | 0 | if (!buffer.validate(kBad < subRunTypeInt && subRunTypeInt < kSubRunStreamTagCount)) { |
1355 | 0 | return nullptr; |
1356 | 0 | } |
1357 | 0 | auto maker = makers[subRunTypeInt]; |
1358 | 0 | if (!buffer.validate(maker != nullptr)) { return nullptr; } |
1359 | 0 | return maker(buffer, alloc, client); |
1360 | 0 | } Unexecuted instantiation: sktext::gpu::SubRun::MakeFromBuffer(SkReadBuffer&, sktext::gpu::SubRunAllocator*, SkStrikeClient const*) Unexecuted instantiation: sktext::gpu::SubRun::MakeFromBuffer(SkReadBuffer&, sktext::gpu::SubRunAllocator*, SkStrikeClient const*) |
1361 | | |
1362 | | // -- SubRunContainer ------------------------------------------------------------------------------ |
1363 | | SubRunContainer::SubRunContainer(const SkMatrix& initialPositionMatrix) |
1364 | 901 | : fInitialPositionMatrix{initialPositionMatrix} {} |
1365 | | |
1366 | 0 | void SubRunContainer::flattenAllocSizeHint(SkWriteBuffer& buffer) const { |
1367 | 0 | int unflattenSizeHint = 0; |
1368 | 0 | for (auto& subrun : fSubRuns) { |
1369 | 0 | unflattenSizeHint += subrun.unflattenSize(); |
1370 | 0 | } |
1371 | 0 | buffer.writeInt(unflattenSizeHint); |
1372 | 0 | } |
1373 | | |
1374 | 0 | int SubRunContainer::AllocSizeHintFromBuffer(SkReadBuffer& buffer) { |
1375 | 0 | int subRunsSizeHint = buffer.readInt(); |
1376 | | |
1377 | | // Since the hint doesn't affect correctness, if it looks fishy just pick a reasonable |
1378 | | // value. |
1379 | 0 | if (subRunsSizeHint < 0 || (1 << 16) < subRunsSizeHint) { |
1380 | 0 | subRunsSizeHint = 128; |
1381 | 0 | } |
1382 | 0 | return subRunsSizeHint; |
1383 | 0 | } |
1384 | | |
1385 | 0 | void SubRunContainer::flattenRuns(SkWriteBuffer& buffer) const { |
1386 | 0 | buffer.writeMatrix(fInitialPositionMatrix); |
1387 | 0 | int subRunCount = 0; |
1388 | 0 | for ([[maybe_unused]] auto& subRun : fSubRuns) { |
1389 | 0 | subRunCount += 1; |
1390 | 0 | } |
1391 | 0 | buffer.writeInt(subRunCount); |
1392 | 0 | for (auto& subRun : fSubRuns) { |
1393 | 0 | subRun.flatten(buffer); |
1394 | 0 | } |
1395 | 0 | } |
1396 | | |
1397 | | SubRunContainerOwner SubRunContainer::MakeFromBufferInAlloc(SkReadBuffer& buffer, |
1398 | | const SkStrikeClient* client, |
1399 | 0 | SubRunAllocator* alloc) { |
1400 | 0 | SkMatrix positionMatrix; |
1401 | 0 | buffer.readMatrix(&positionMatrix); |
1402 | 0 | if (!buffer.isValid()) { return nullptr; } |
1403 | 0 | SubRunContainerOwner container = alloc->makeUnique<SubRunContainer>(positionMatrix); |
1404 | |
|
1405 | 0 | int subRunCount = buffer.readInt(); |
1406 | 0 | SkASSERT(subRunCount > 0); |
1407 | 0 | if (!buffer.validate(subRunCount > 0)) { return nullptr; } |
1408 | 0 | for (int i = 0; i < subRunCount; ++i) { |
1409 | 0 | auto subRunOwner = SubRun::MakeFromBuffer(buffer, alloc, client); |
1410 | 0 | if (!buffer.validate(subRunOwner != nullptr)) { return nullptr; } |
1411 | 0 | if (subRunOwner != nullptr) { |
1412 | 0 | container->fSubRuns.append(std::move(subRunOwner)); |
1413 | 0 | } |
1414 | 0 | } |
1415 | 0 | return container; |
1416 | 0 | } Unexecuted instantiation: sktext::gpu::SubRunContainer::MakeFromBufferInAlloc(SkReadBuffer&, SkStrikeClient const*, sktext::gpu::SubRunAllocator*) Unexecuted instantiation: sktext::gpu::SubRunContainer::MakeFromBufferInAlloc(SkReadBuffer&, SkStrikeClient const*, sktext::gpu::SubRunAllocator*) |
1417 | | |
1418 | 901 | size_t SubRunContainer::EstimateAllocSize(const GlyphRunList& glyphRunList) { |
1419 | | // The difference in alignment from the per-glyph data to the SubRun; |
1420 | 901 | constexpr size_t alignDiff = alignof(DirectMaskSubRun) - alignof(SkPoint); |
1421 | 901 | constexpr size_t vertexDataToSubRunPadding = alignDiff > 0 ? alignDiff : 0; |
1422 | 901 | size_t totalGlyphCount = glyphRunList.totalGlyphCount(); |
1423 | | // This is optimized for DirectMaskSubRun which is by far the most common case. |
1424 | 901 | return totalGlyphCount * sizeof(SkPoint) |
1425 | 901 | + GlyphVector::GlyphVectorSize(totalGlyphCount) |
1426 | 901 | + glyphRunList.runCount() * (sizeof(DirectMaskSubRun) + vertexDataToSubRunPadding) |
1427 | 901 | + sizeof(SubRunContainer); |
1428 | 901 | } |
1429 | | |
1430 | 3 | SkScalar find_maximum_glyph_dimension(StrikeForGPU* strike, SkSpan<const SkGlyphID> glyphs) { |
1431 | 3 | StrikeMutationMonitor m{strike}; |
1432 | 3 | SkScalar maxDimension = 0; |
1433 | 33 | for (SkGlyphID glyphID : glyphs) { |
1434 | 33 | SkGlyphDigest digest = strike->digestFor(kMask, SkPackedGlyphID{glyphID}); |
1435 | 33 | maxDimension = std::max(static_cast<SkScalar>(digest.maxDimension()), maxDimension); |
1436 | 33 | } |
1437 | | |
1438 | 3 | return maxDimension; |
1439 | 3 | } |
1440 | | |
1441 | | #if !defined(SK_DISABLE_SDF_TEXT) |
1442 | | std::tuple<SkZip<const SkPackedGlyphID, const SkPoint>, SkZip<SkGlyphID, SkPoint>, SkRect> |
1443 | | prepare_for_SDFT_drawing(StrikeForGPU* strike, |
1444 | | const SkMatrix& creationMatrix, |
1445 | | SkZip<const SkGlyphID, const SkPoint> source, |
1446 | | SkZip<SkPackedGlyphID, SkPoint> acceptedBuffer, |
1447 | 240 | SkZip<SkGlyphID, SkPoint> rejectedBuffer) { |
1448 | 240 | int acceptedSize = 0, |
1449 | 240 | rejectedSize = 0; |
1450 | 240 | SkGlyphRect boundingRect = skglyph::empty_rect(); |
1451 | 240 | StrikeMutationMonitor m{strike}; |
1452 | 3.54k | for (const auto [glyphID, pos] : source) { |
1453 | 3.54k | if (!SkIsFinite(pos.x(), pos.y())) { |
1454 | 172 | continue; |
1455 | 172 | } |
1456 | | |
1457 | 3.37k | const SkPackedGlyphID packedID{glyphID}; |
1458 | 3.37k | switch (const SkGlyphDigest digest = strike->digestFor(skglyph::kSDFT, packedID); |
1459 | 3.37k | digest.actionFor(skglyph::kSDFT)) { |
1460 | 2.01k | case GlyphAction::kAccept: { |
1461 | 2.01k | SkPoint mappedPos = creationMatrix.mapPoint(pos); |
1462 | 2.01k | const SkGlyphRect glyphBounds = |
1463 | 2.01k | digest.bounds() |
1464 | | // The SDFT glyphs have 2-pixel wide padding that should |
1465 | | // not be used in calculating the source rectangle. |
1466 | 2.01k | .inset(SK_DistanceFieldInset, SK_DistanceFieldInset) |
1467 | 2.01k | .offset(mappedPos); |
1468 | 2.01k | boundingRect = skglyph::rect_union(boundingRect, glyphBounds); |
1469 | 2.01k | acceptedBuffer[acceptedSize++] = std::make_tuple(packedID, glyphBounds.leftTop()); |
1470 | 2.01k | break; |
1471 | 0 | } |
1472 | 250 | case GlyphAction::kReject: |
1473 | 250 | rejectedBuffer[rejectedSize++] = std::make_tuple(glyphID, pos); |
1474 | 250 | break; |
1475 | 1.10k | default: |
1476 | 1.10k | break; |
1477 | 3.37k | } |
1478 | 3.37k | } |
1479 | | |
1480 | 240 | return {acceptedBuffer.first(acceptedSize), |
1481 | 240 | rejectedBuffer.first(rejectedSize), |
1482 | 240 | boundingRect.rect()}; |
1483 | 240 | } |
1484 | | #endif |
1485 | | |
1486 | | std::tuple<SkZip<const SkPackedGlyphID, const SkPoint, const SkMask::Format>, |
1487 | | SkZip<SkGlyphID, SkPoint>, |
1488 | | SkRect> |
1489 | | prepare_for_direct_mask_drawing(StrikeForGPU* strike, |
1490 | | const SkMatrix& positionMatrix, |
1491 | | SkZip<const SkGlyphID, const SkPoint> source, |
1492 | | SkZip<SkPackedGlyphID, SkPoint, SkMask::Format> acceptedBuffer, |
1493 | 1.70k | SkZip<SkGlyphID, SkPoint> rejectedBuffer) { |
1494 | 1.70k | const SkIPoint mask = strike->roundingSpec().ignorePositionFieldMask; |
1495 | 1.70k | const SkPoint halfSampleFreq = strike->roundingSpec().halfAxisSampleFreq; |
1496 | | |
1497 | | // Build up the mapping from source space to device space. Add the rounding constant |
1498 | | // halfSampleFreq, so we just need to floor to get the device result. |
1499 | 1.70k | SkMatrix positionMatrixWithRounding = positionMatrix; |
1500 | 1.70k | positionMatrixWithRounding.postTranslate(halfSampleFreq.x(), halfSampleFreq.y()); |
1501 | | |
1502 | 1.70k | int acceptedSize = 0, |
1503 | 1.70k | rejectedSize = 0; |
1504 | 1.70k | SkGlyphRect boundingRect = skglyph::empty_rect(); |
1505 | 1.70k | StrikeMutationMonitor m{strike}; |
1506 | 27.8k | for (auto [glyphID, pos] : source) { |
1507 | 27.8k | if (!SkIsFinite(pos.x(), pos.y())) { |
1508 | 1.75k | continue; |
1509 | 1.75k | } |
1510 | | |
1511 | 26.1k | const SkPoint mappedPos = positionMatrixWithRounding.mapPoint(pos); |
1512 | 26.1k | const SkPackedGlyphID packedID{glyphID, mappedPos, mask}; |
1513 | 26.1k | switch (const SkGlyphDigest digest = strike->digestFor(skglyph::kDirectMask, packedID); |
1514 | 26.1k | digest.actionFor(skglyph::kDirectMask)) { |
1515 | 10.5k | case GlyphAction::kAccept: { |
1516 | 10.5k | const SkPoint roundedPos{SkScalarFloorToScalar(mappedPos.x()), |
1517 | 10.5k | SkScalarFloorToScalar(mappedPos.y())}; |
1518 | 10.5k | const SkGlyphRect glyphBounds = digest.bounds().offset(roundedPos); |
1519 | 10.5k | boundingRect = skglyph::rect_union(boundingRect, glyphBounds); |
1520 | 10.5k | acceptedBuffer[acceptedSize++] = |
1521 | 10.5k | std::make_tuple(packedID, glyphBounds.leftTop(), digest.maskFormat()); |
1522 | 10.5k | break; |
1523 | 0 | } |
1524 | 5.43k | case GlyphAction::kReject: |
1525 | 5.43k | rejectedBuffer[rejectedSize++] = std::make_tuple(glyphID, pos); |
1526 | 5.43k | break; |
1527 | 10.0k | default: |
1528 | 10.0k | break; |
1529 | 26.1k | } |
1530 | 26.1k | } |
1531 | | |
1532 | 1.70k | return {acceptedBuffer.first(acceptedSize), |
1533 | 1.70k | rejectedBuffer.first(rejectedSize), |
1534 | 1.70k | boundingRect.rect()}; |
1535 | 1.70k | } |
1536 | | |
1537 | | std::tuple<SkZip<const SkPackedGlyphID, const SkPoint, const SkMask::Format>, |
1538 | | SkZip<SkGlyphID, SkPoint>, |
1539 | | SkRect> |
1540 | | prepare_for_mask_drawing(StrikeForGPU* strike, |
1541 | | const SkMatrix& creationMatrix, |
1542 | | SkZip<const SkGlyphID, const SkPoint> source, |
1543 | | SkZip<SkPackedGlyphID, SkPoint, SkMask::Format> acceptedBuffer, |
1544 | 1 | SkZip<SkGlyphID, SkPoint> rejectedBuffer) { |
1545 | 1 | int acceptedSize = 0, |
1546 | 1 | rejectedSize = 0; |
1547 | 1 | SkGlyphRect boundingRect = skglyph::empty_rect(); |
1548 | 1 | StrikeMutationMonitor m{strike}; |
1549 | 11 | for (auto [glyphID, pos] : source) { |
1550 | 11 | if (!SkIsFinite(pos.x(), pos.y())) { |
1551 | 0 | continue; |
1552 | 0 | } |
1553 | | |
1554 | 11 | const SkPackedGlyphID packedID{glyphID}; |
1555 | 11 | switch (const SkGlyphDigest digest = strike->digestFor(kMask, packedID); |
1556 | 11 | digest.actionFor(kMask)) { |
1557 | 0 | case GlyphAction::kAccept: { |
1558 | 0 | const SkPoint mappedPos = creationMatrix.mapPoint(pos); |
1559 | 0 | const SkGlyphRect glyphBounds = digest.bounds().offset(mappedPos); |
1560 | 0 | boundingRect = skglyph::rect_union(boundingRect, glyphBounds); |
1561 | 0 | acceptedBuffer[acceptedSize++] = |
1562 | 0 | std::make_tuple(packedID, glyphBounds.leftTop(), digest.maskFormat()); |
1563 | 0 | break; |
1564 | 0 | } |
1565 | 0 | case GlyphAction::kReject: |
1566 | 0 | rejectedBuffer[rejectedSize++] = std::make_tuple(glyphID, pos); |
1567 | 0 | break; |
1568 | 11 | default: |
1569 | 11 | break; |
1570 | 11 | } |
1571 | 11 | } |
1572 | | |
1573 | 1 | return {acceptedBuffer.first(acceptedSize), |
1574 | 1 | rejectedBuffer.first(rejectedSize), |
1575 | 1 | boundingRect.rect()}; |
1576 | 1 | } |
1577 | | |
1578 | | std::tuple<SkZip<const SkGlyphID, const SkPoint>, SkZip<SkGlyphID, SkPoint>> |
1579 | | prepare_for_path_drawing(StrikeForGPU* strike, |
1580 | | SkZip<const SkGlyphID, const SkPoint> source, |
1581 | | SkZip<SkGlyphID, SkPoint> acceptedBuffer, |
1582 | 2.53k | SkZip<SkGlyphID, SkPoint> rejectedBuffer) { |
1583 | 2.53k | int acceptedSize = 0; |
1584 | 2.53k | int rejectedSize = 0; |
1585 | 2.53k | StrikeMutationMonitor m{strike}; |
1586 | 48.6k | for (const auto [glyphID, pos] : source) { |
1587 | 48.6k | if (!SkIsFinite(pos.x(), pos.y())) { |
1588 | 0 | continue; |
1589 | 0 | } |
1590 | | |
1591 | 48.6k | switch (strike->digestFor(skglyph::kPath, SkPackedGlyphID{glyphID}) |
1592 | 48.6k | .actionFor(skglyph::kPath)) { |
1593 | 48.6k | case GlyphAction::kAccept: |
1594 | 48.6k | acceptedBuffer[acceptedSize++] = std::make_tuple(glyphID, pos); |
1595 | 48.6k | break; |
1596 | 0 | case GlyphAction::kReject: |
1597 | 0 | rejectedBuffer[rejectedSize++] = std::make_tuple(glyphID, pos); |
1598 | 0 | break; |
1599 | 0 | default: |
1600 | 0 | break; |
1601 | 48.6k | } |
1602 | 48.6k | } |
1603 | 2.53k | return {acceptedBuffer.first(acceptedSize), rejectedBuffer.first(rejectedSize)}; |
1604 | 2.53k | } |
1605 | | |
1606 | | std::tuple<SkZip<const SkGlyphID, const SkPoint>, SkZip<SkGlyphID, SkPoint>> |
1607 | | prepare_for_drawable_drawing(StrikeForGPU* strike, |
1608 | | SkZip<const SkGlyphID, const SkPoint> source, |
1609 | | SkZip<SkGlyphID, SkPoint> acceptedBuffer, |
1610 | 3.11k | SkZip<SkGlyphID, SkPoint> rejectedBuffer) { |
1611 | 3.11k | int acceptedSize = 0; |
1612 | 3.11k | int rejectedSize = 0; |
1613 | 3.11k | StrikeMutationMonitor m{strike}; |
1614 | 66.1k | for (const auto [glyphID, pos] : source) { |
1615 | 66.1k | if (!SkIsFinite(pos.x(), pos.y())) { |
1616 | 2.87k | continue; |
1617 | 2.87k | } |
1618 | | |
1619 | 63.2k | switch (strike->digestFor(skglyph::kDrawable, SkPackedGlyphID{glyphID}) |
1620 | 63.2k | .actionFor(skglyph::kDrawable)) { |
1621 | 39 | case GlyphAction::kAccept: |
1622 | 39 | acceptedBuffer[acceptedSize++] = std::make_tuple(glyphID, pos); |
1623 | 39 | break; |
1624 | 48.6k | case GlyphAction::kReject: |
1625 | 48.6k | rejectedBuffer[rejectedSize++] = std::make_tuple(glyphID, pos); |
1626 | 48.6k | break; |
1627 | 14.6k | default: |
1628 | 14.6k | break; |
1629 | 63.2k | } |
1630 | 63.2k | } |
1631 | 3.11k | return {acceptedBuffer.first(acceptedSize), rejectedBuffer.first(rejectedSize)}; |
1632 | 3.11k | } |
1633 | | |
1634 | | #if !defined(SK_DISABLE_SDF_TEXT) |
1635 | | static std::tuple<SkStrikeSpec, SkScalar, sktext::gpu::SDFTMatrixRange> |
1636 | | make_sdft_strike_spec(const SkFont& font, const SkPaint& paint, |
1637 | | const SkSurfaceProps& surfaceProps, const SkMatrix& deviceMatrix, |
1638 | 240 | const SkPoint& textLocation, const sktext::gpu::SDFTControl& control) { |
1639 | | // Add filter to the paint which creates the SDFT data for A8 masks. |
1640 | 240 | SkPaint dfPaint{paint}; |
1641 | 240 | dfPaint.setMaskFilter(sktext::gpu::SDFMaskFilter::Make()); |
1642 | | |
1643 | 240 | auto [dfFont, strikeToSourceScale, matrixRange] = control.getSDFFont(font, deviceMatrix, |
1644 | 240 | textLocation); |
1645 | | |
1646 | | // Adjust the stroke width by the scale factor for drawing the SDFT. |
1647 | 240 | dfPaint.setStrokeWidth(paint.getStrokeWidth() / strikeToSourceScale); |
1648 | | |
1649 | | // Check for dashing and adjust the intervals. |
1650 | 240 | if (SkPathEffect* pathEffect = paint.getPathEffect(); pathEffect != nullptr) { |
1651 | 64 | SkPathEffect::DashInfo dashInfo; |
1652 | 64 | if (pathEffect->asADash(&dashInfo) == SkPathEffect::kDash_DashType) { |
1653 | 8 | if (dashInfo.fCount > 0) { |
1654 | | // Allocate the intervals. |
1655 | 8 | std::vector<SkScalar> scaledIntervals(dashInfo.fCount); |
1656 | 8 | dashInfo.fIntervals = scaledIntervals.data(); |
1657 | | // Call again to get the interval data. |
1658 | 8 | (void)pathEffect->asADash(&dashInfo); |
1659 | 160 | for (SkScalar& interval : scaledIntervals) { |
1660 | 160 | interval /= strikeToSourceScale; |
1661 | 160 | } |
1662 | 8 | auto scaledDashes = SkDashPathEffect::Make(scaledIntervals.data(), |
1663 | 8 | scaledIntervals.size(), |
1664 | 8 | dashInfo.fPhase / strikeToSourceScale); |
1665 | 8 | dfPaint.setPathEffect(scaledDashes); |
1666 | 8 | } |
1667 | 8 | } |
1668 | 64 | } |
1669 | | |
1670 | | // Fake-gamma and subpixel antialiasing are applied in the shader, so we ignore the |
1671 | | // passed-in scaler context flags. (It's only used when we fall-back to bitmap text). |
1672 | 240 | SkScalerContextFlags flags = SkScalerContextFlags::kNone; |
1673 | 240 | SkStrikeSpec strikeSpec = SkStrikeSpec::MakeMask(dfFont, dfPaint, surfaceProps, flags, |
1674 | 240 | SkMatrix::I()); |
1675 | | |
1676 | 240 | return std::make_tuple(std::move(strikeSpec), strikeToSourceScale, matrixRange); |
1677 | 240 | } |
1678 | | #endif |
1679 | | |
1680 | | SubRunContainerOwner SubRunContainer::MakeInAlloc( |
1681 | | const GlyphRunList& glyphRunList, |
1682 | | const SkMatrix& positionMatrix, |
1683 | | const SkPaint& runPaint, |
1684 | | SkStrikeDeviceInfo strikeDeviceInfo, |
1685 | | StrikeForGPUCacheInterface* strikeCache, |
1686 | | SubRunAllocator* alloc, |
1687 | | SubRunCreationBehavior creationBehavior, |
1688 | 901 | const char* tag) { |
1689 | 901 | SkASSERT(alloc != nullptr); |
1690 | 901 | SkASSERT(strikeDeviceInfo.fSDFTControl != nullptr); |
1691 | | |
1692 | 901 | SubRunContainerOwner container = alloc->makeUnique<SubRunContainer>(positionMatrix); |
1693 | | // If there is no SDFT description ignore all SubRuns. |
1694 | 901 | if (strikeDeviceInfo.fSDFTControl == nullptr) { |
1695 | 0 | return container; |
1696 | 0 | } |
1697 | | |
1698 | 901 | const SkSurfaceProps deviceProps = strikeDeviceInfo.fSurfaceProps; |
1699 | 901 | const SkScalerContextFlags scalerContextFlags = strikeDeviceInfo.fScalerContextFlags; |
1700 | 901 | #if !defined(SK_DISABLE_SDF_TEXT) |
1701 | 901 | const SDFTControl SDFTControl = *strikeDeviceInfo.fSDFTControl; |
1702 | 901 | const SkScalar maxMaskSize = SDFTControl.maxSize(); |
1703 | | #else |
1704 | | const SkScalar maxMaskSize = 256; |
1705 | | #endif |
1706 | | |
1707 | | // TODO: hoist the buffer structure to the GlyphRunBuilder. The buffer structure here is |
1708 | | // still begin tuned, and this is expected to be slower until tuned. |
1709 | 901 | const int maxGlyphRunSize = glyphRunList.maxGlyphRunSize(); |
1710 | | |
1711 | | // Accepted buffers. |
1712 | 901 | STArray<64, SkPackedGlyphID> acceptedPackedGlyphIDs; |
1713 | 901 | STArray<64, SkGlyphID> acceptedGlyphIDs; |
1714 | 901 | STArray<64, SkPoint> acceptedPositions; |
1715 | 901 | STArray<64, SkMask::Format> acceptedFormats; |
1716 | 901 | acceptedPackedGlyphIDs.resize(maxGlyphRunSize); |
1717 | 901 | acceptedGlyphIDs.resize(maxGlyphRunSize); |
1718 | 901 | acceptedPositions.resize(maxGlyphRunSize); |
1719 | 901 | acceptedFormats.resize(maxGlyphRunSize); |
1720 | | |
1721 | | // Rejected buffers. |
1722 | 901 | STArray<64, SkGlyphID> rejectedGlyphIDs; |
1723 | 901 | STArray<64, SkPoint> rejectedPositions; |
1724 | 901 | rejectedGlyphIDs.resize(maxGlyphRunSize); |
1725 | 901 | rejectedPositions.resize(maxGlyphRunSize); |
1726 | 901 | const auto rejectedBuffer = SkMakeZip(rejectedGlyphIDs, rejectedPositions); |
1727 | | |
1728 | 901 | const SkPoint glyphRunListLocation = glyphRunList.sourceBounds().center(); |
1729 | | |
1730 | | // Handle all the runs in the glyphRunList |
1731 | 4.71k | for (auto& glyphRun : glyphRunList) { |
1732 | 4.71k | SkZip<const SkGlyphID, const SkPoint> source = glyphRun.source(); |
1733 | 4.71k | const SkFont& runFont = glyphRun.font(); |
1734 | | |
1735 | 4.71k | const SkScalar approximateDeviceTextSize = |
1736 | | // Since the positionMatrix has the origin prepended, use the plain |
1737 | | // sourceBounds from above. |
1738 | 4.71k | SkFontPriv::ApproximateTransformedTextSize(runFont, positionMatrix, |
1739 | 4.71k | glyphRunListLocation); |
1740 | | |
1741 | | // Atlas mask cases - SDFT and direct mask |
1742 | | // Only consider using direct or SDFT drawing if not drawing hairlines and not too big. |
1743 | 4.71k | if ((runPaint.getStyle() != SkPaint::kStroke_Style || runPaint.getStrokeWidth() != 0) && |
1744 | 4.71k | approximateDeviceTextSize < maxMaskSize) { |
1745 | | |
1746 | 3.15k | #if !defined(SK_DISABLE_SDF_TEXT) |
1747 | | // SDFT case |
1748 | 3.15k | if (SDFTControl.isSDFT(approximateDeviceTextSize, runPaint, positionMatrix)) { |
1749 | | // Process SDFT - This should be the .009% case. |
1750 | 240 | const auto& [strikeSpec, strikeToSourceScale, matrixRange] = |
1751 | 240 | make_sdft_strike_spec( |
1752 | 240 | runFont, runPaint, deviceProps, positionMatrix, |
1753 | 240 | glyphRunListLocation, SDFTControl); |
1754 | | |
1755 | 240 | if (!SkScalarNearlyZero(strikeToSourceScale)) { |
1756 | 240 | sk_sp<StrikeForGPU> strike = strikeSpec.findOrCreateScopedStrike(strikeCache); |
1757 | | |
1758 | | // The creationMatrix needs to scale the strike data when inverted and |
1759 | | // multiplied by the positionMatrix. The final CTM should be: |
1760 | | // [positionMatrix][scale by strikeToSourceScale], |
1761 | | // which should equal the following because of the transform during the vertex |
1762 | | // calculation, |
1763 | | // [positionMatrix][creationMatrix]^-1. |
1764 | | // So, the creation matrix needs to be |
1765 | | // [scale by 1/strikeToSourceScale]. |
1766 | 240 | SkMatrix creationMatrix = |
1767 | 240 | SkMatrix::Scale(1.f/strikeToSourceScale, 1.f/strikeToSourceScale); |
1768 | | |
1769 | 240 | auto acceptedBuffer = SkMakeZip(acceptedPackedGlyphIDs, acceptedPositions); |
1770 | 240 | auto [accepted, rejected, creationBounds] = prepare_for_SDFT_drawing( |
1771 | 240 | strike.get(), creationMatrix, source, acceptedBuffer, rejectedBuffer); |
1772 | 240 | source = rejected; |
1773 | | |
1774 | 240 | if (creationBehavior == kAddSubRuns && !accepted.empty()) { |
1775 | 138 | container->fSubRuns.append(SDFTSubRun::Make( |
1776 | 138 | accepted, |
1777 | 138 | runFont, |
1778 | 138 | strike->strikePromise(), |
1779 | 138 | creationMatrix, |
1780 | 138 | creationBounds, |
1781 | 138 | matrixRange, |
1782 | 138 | alloc)); |
1783 | 138 | } |
1784 | 240 | } |
1785 | 240 | } |
1786 | 3.15k | #endif // !defined(SK_DISABLE_SDF_TEXT) |
1787 | | |
1788 | | // Direct Mask case |
1789 | | // Handle all the directly mapped mask subruns. |
1790 | 3.15k | if (!source.empty() && !positionMatrix.hasPerspective()) { |
1791 | | // Process masks including ARGB - this should be the 99.99% case. |
1792 | | // This will handle medium size emoji that are sharing the run with SDFT drawn text. |
1793 | | // If things are too big they will be passed along to the drawing of last resort |
1794 | | // below. |
1795 | 1.70k | SkStrikeSpec strikeSpec = SkStrikeSpec::MakeMask( |
1796 | 1.70k | runFont, runPaint, deviceProps, scalerContextFlags, positionMatrix); |
1797 | | |
1798 | 1.70k | sk_sp<StrikeForGPU> strike = strikeSpec.findOrCreateScopedStrike(strikeCache); |
1799 | | |
1800 | 1.70k | auto acceptedBuffer = SkMakeZip(acceptedPackedGlyphIDs, |
1801 | 1.70k | acceptedPositions, |
1802 | 1.70k | acceptedFormats); |
1803 | 1.70k | auto [accepted, rejected, creationBounds] = prepare_for_direct_mask_drawing( |
1804 | 1.70k | strike.get(), positionMatrix, source, acceptedBuffer, rejectedBuffer); |
1805 | 1.70k | source = rejected; |
1806 | | |
1807 | 1.70k | if (creationBehavior == kAddSubRuns && !accepted.empty()) { |
1808 | 701 | auto addGlyphsWithSameFormat = |
1809 | 701 | [&, bounds = creationBounds]( |
1810 | 701 | SkZip<const SkPackedGlyphID, const SkPoint> subrun, |
1811 | 701 | MaskFormat format) { |
1812 | 701 | container->fSubRuns.append( |
1813 | 701 | DirectMaskSubRun::Make(bounds, |
1814 | 701 | subrun, |
1815 | 701 | container->initialPosition(), |
1816 | 701 | strike->strikePromise(), |
1817 | 701 | format, |
1818 | 701 | alloc)); |
1819 | 701 | }; |
1820 | 701 | add_multi_mask_format(addGlyphsWithSameFormat, accepted); |
1821 | 701 | } |
1822 | 1.70k | } |
1823 | 3.15k | } |
1824 | | |
1825 | | // Drawable case |
1826 | | // Handle all the drawable glyphs - usually large or perspective color glyphs. |
1827 | 4.71k | if (!source.empty()) { |
1828 | 3.12k | auto [strikeSpec, strikeToSourceScale] = |
1829 | 3.12k | SkStrikeSpec::MakePath(runFont, runPaint, deviceProps, scalerContextFlags); |
1830 | | |
1831 | 3.12k | if (!SkScalarNearlyZero(strikeToSourceScale)) { |
1832 | 3.11k | sk_sp<StrikeForGPU> strike = strikeSpec.findOrCreateScopedStrike(strikeCache); |
1833 | | |
1834 | 3.11k | auto acceptedBuffer = SkMakeZip(acceptedGlyphIDs, acceptedPositions); |
1835 | 3.11k | auto [accepted, rejected] = |
1836 | 3.11k | prepare_for_drawable_drawing(strike.get(), source, acceptedBuffer, rejectedBuffer); |
1837 | 3.11k | source = rejected; |
1838 | | |
1839 | 3.11k | if (creationBehavior == kAddSubRuns && !accepted.empty()) { |
1840 | 2 | container->fSubRuns.append( |
1841 | 2 | DrawableSubRun::Make( |
1842 | 2 | accepted, |
1843 | 2 | strikeToSourceScale, |
1844 | 2 | strike->strikePromise(), |
1845 | 2 | alloc)); |
1846 | 2 | } |
1847 | 3.11k | } |
1848 | 3.12k | } |
1849 | | |
1850 | | // Path case |
1851 | | // Handle path subruns. Mainly, large or large perspective glyphs with no color. |
1852 | 4.71k | if (!source.empty()) { |
1853 | 2.54k | auto [strikeSpec, strikeToSourceScale] = |
1854 | 2.54k | SkStrikeSpec::MakePath(runFont, runPaint, deviceProps, scalerContextFlags); |
1855 | | |
1856 | 2.54k | if (!SkScalarNearlyZero(strikeToSourceScale)) { |
1857 | 2.53k | sk_sp<StrikeForGPU> strike = strikeSpec.findOrCreateScopedStrike(strikeCache); |
1858 | | |
1859 | 2.53k | auto acceptedBuffer = SkMakeZip(acceptedGlyphIDs, acceptedPositions); |
1860 | 2.53k | auto [accepted, rejected] = |
1861 | 2.53k | prepare_for_path_drawing(strike.get(), source, acceptedBuffer, rejectedBuffer); |
1862 | 2.53k | source = rejected; |
1863 | | |
1864 | 2.53k | if (creationBehavior == kAddSubRuns && !accepted.empty()) { |
1865 | 2.53k | container->fSubRuns.append( |
1866 | 2.53k | PathSubRun::Make(accepted, |
1867 | 2.53k | has_some_antialiasing(runFont), |
1868 | 2.53k | strikeToSourceScale, |
1869 | 2.53k | strike->strikePromise(), |
1870 | 2.53k | alloc)); |
1871 | 2.53k | } |
1872 | 2.53k | } |
1873 | 2.54k | } |
1874 | | |
1875 | | // Drawing of last resort case |
1876 | | // Draw all the rest of the rejected glyphs from above. This scales out of the atlas to |
1877 | | // the screen, so quality will suffer. This mainly handles large color or perspective |
1878 | | // color not handled by Drawables. |
1879 | 4.71k | if (!source.empty() && !SkScalarNearlyZero(approximateDeviceTextSize)) { |
1880 | | // Creation matrix will be changed below to meet the following criteria: |
1881 | | // * No perspective - the font scaler and the strikes can't handle perspective masks. |
1882 | | // * Fits atlas - creationMatrix will be conditioned so that the maximum glyph |
1883 | | // dimension for this run will be < kMaxBilerpAtlasDimension. |
1884 | 1 | SkMatrix creationMatrix = positionMatrix; |
1885 | | |
1886 | | // Condition creationMatrix for perspective. |
1887 | 1 | if (creationMatrix.hasPerspective()) { |
1888 | | // Find a scale factor that reduces pixelation caused by keystoning. |
1889 | 0 | SkPoint center = glyphRunList.sourceBounds().center(); |
1890 | 0 | SkScalar maxAreaScale = SkMatrixPriv::DifferentialAreaScale(creationMatrix, center); |
1891 | 0 | SkScalar perspectiveFactor = 1; |
1892 | 0 | if (SkIsFinite(maxAreaScale) && !SkScalarNearlyZero(maxAreaScale)) { |
1893 | 0 | perspectiveFactor = SkScalarSqrt(maxAreaScale); |
1894 | 0 | } |
1895 | | |
1896 | | // Masks can not be created in perspective. Create a non-perspective font with a |
1897 | | // scale that will support the perspective keystoning. |
1898 | 0 | creationMatrix = SkMatrix::Scale(perspectiveFactor, perspectiveFactor); |
1899 | 0 | } |
1900 | | |
1901 | | // Reduce to make a one pixel border for the bilerp padding. |
1902 | 1 | static const constexpr SkScalar kMaxBilerpAtlasDimension = |
1903 | 1 | SkGlyphDigest::kSkSideTooBigForAtlas - 2; |
1904 | | |
1905 | | // Get the raw glyph IDs to simulate device drawing to figure the maximum device |
1906 | | // dimension. |
1907 | 1 | const SkSpan<const SkGlyphID> glyphs = get_glyphIDs(source); |
1908 | | |
1909 | | // maxGlyphDimension always returns an integer even though the return type is SkScalar. |
1910 | 3 | auto maxGlyphDimension = [&](const SkMatrix& m) { |
1911 | 3 | const SkStrikeSpec strikeSpec = SkStrikeSpec::MakeTransformMask( |
1912 | 3 | runFont, runPaint, deviceProps, scalerContextFlags, m); |
1913 | 3 | const sk_sp<StrikeForGPU> gaugingStrike = |
1914 | 3 | strikeSpec.findOrCreateScopedStrike(strikeCache); |
1915 | 3 | const SkScalar maxDimension = |
1916 | 3 | find_maximum_glyph_dimension(gaugingStrike.get(), glyphs); |
1917 | | // TODO: There is a problem where a small character (say .) and a large |
1918 | | // character (say M) are in the same run. If the run is scaled to be very |
1919 | | // large, then the M may return 0 because its dimensions are > 65535, but |
1920 | | // the small character produces regular result because its largest dimension |
1921 | | // is < 65535. This will create an improper scale factor causing the M to be |
1922 | | // too large to fit in the atlas. Tracked by skia:13714. |
1923 | 3 | return maxDimension; |
1924 | 3 | }; SubRunContainer.cpp:sktext::gpu::SubRunContainer::MakeInAlloc(sktext::GlyphRunList const&, SkMatrix const&, SkPaint const&, SkStrikeDeviceInfo, sktext::StrikeForGPUCacheInterface*, sktext::gpu::SubRunAllocator*, sktext::gpu::SubRunContainer::SubRunCreationBehavior, char const*)::$_1::operator()(SkMatrix const&) const Line | Count | Source | 1910 | 3 | auto maxGlyphDimension = [&](const SkMatrix& m) { | 1911 | 3 | const SkStrikeSpec strikeSpec = SkStrikeSpec::MakeTransformMask( | 1912 | 3 | runFont, runPaint, deviceProps, scalerContextFlags, m); | 1913 | 3 | const sk_sp<StrikeForGPU> gaugingStrike = | 1914 | 3 | strikeSpec.findOrCreateScopedStrike(strikeCache); | 1915 | 3 | const SkScalar maxDimension = | 1916 | 3 | find_maximum_glyph_dimension(gaugingStrike.get(), glyphs); | 1917 | | // TODO: There is a problem where a small character (say .) and a large | 1918 | | // character (say M) are in the same run. If the run is scaled to be very | 1919 | | // large, then the M may return 0 because its dimensions are > 65535, but | 1920 | | // the small character produces regular result because its largest dimension | 1921 | | // is < 65535. This will create an improper scale factor causing the M to be | 1922 | | // too large to fit in the atlas. Tracked by skia:13714. | 1923 | 3 | return maxDimension; | 1924 | 3 | }; |
Unexecuted instantiation: SubRunContainer.cpp:sktext::gpu::SubRunContainer::MakeInAlloc(sktext::GlyphRunList const&, SkMatrix const&, SkPaint const&, SkStrikeDeviceInfo, sktext::StrikeForGPUCacheInterface*, sktext::gpu::SubRunAllocator*, sktext::gpu::SubRunContainer::SubRunCreationBehavior, char const*)::$_3::operator()(SkMatrix const&) const |
1925 | | |
1926 | | // Condition the creationMatrix so that glyphs fit in the atlas. |
1927 | 1 | for (SkScalar maxDimension = maxGlyphDimension(creationMatrix); |
1928 | 3 | kMaxBilerpAtlasDimension < maxDimension; |
1929 | 2 | maxDimension = maxGlyphDimension(creationMatrix)) |
1930 | 2 | { |
1931 | | // The SkScalerContext has a limit of 65536 maximum dimension. |
1932 | | // reductionFactor will always be < 1 because |
1933 | | // maxDimension > kMaxBilerpAtlasDimension, and because maxDimension will always |
1934 | | // be an integer the reduction factor will always be at most 254 / 255. |
1935 | 2 | SkScalar reductionFactor = kMaxBilerpAtlasDimension / maxDimension; |
1936 | 2 | creationMatrix.postScale(reductionFactor, reductionFactor); |
1937 | 2 | } |
1938 | | |
1939 | | // Draw using the creationMatrix. |
1940 | 1 | SkStrikeSpec strikeSpec = SkStrikeSpec::MakeTransformMask( |
1941 | 1 | runFont, runPaint, deviceProps, scalerContextFlags, creationMatrix); |
1942 | | |
1943 | 1 | sk_sp<StrikeForGPU> strike = strikeSpec.findOrCreateScopedStrike(strikeCache); |
1944 | | |
1945 | 1 | auto acceptedBuffer = |
1946 | 1 | SkMakeZip(acceptedPackedGlyphIDs, acceptedPositions, acceptedFormats); |
1947 | 1 | auto [accepted, rejected, creationBounds] = |
1948 | 1 | prepare_for_mask_drawing( |
1949 | 1 | strike.get(), creationMatrix, source, acceptedBuffer, rejectedBuffer); |
1950 | 1 | source = rejected; |
1951 | | |
1952 | 1 | if (creationBehavior == kAddSubRuns && !accepted.empty()) { |
1953 | |
|
1954 | 0 | auto addGlyphsWithSameFormat = |
1955 | 0 | [&, bounds = creationBounds]( |
1956 | 0 | SkZip<const SkPackedGlyphID, const SkPoint> subrun, |
1957 | 0 | MaskFormat format) { |
1958 | 0 | container->fSubRuns.append( |
1959 | 0 | TransformedMaskSubRun::Make(subrun, |
1960 | 0 | container->initialPosition(), |
1961 | 0 | strike->strikePromise(), |
1962 | 0 | creationMatrix, |
1963 | 0 | bounds, |
1964 | 0 | format, |
1965 | 0 | alloc)); |
1966 | 0 | }; Unexecuted instantiation: SubRunContainer.cpp:sktext::gpu::SubRunContainer::MakeInAlloc(sktext::GlyphRunList const&, SkMatrix const&, SkPaint const&, SkStrikeDeviceInfo, sktext::StrikeForGPUCacheInterface*, sktext::gpu::SubRunAllocator*, sktext::gpu::SubRunContainer::SubRunCreationBehavior, char const*)::$_2::operator()(SkZip<SkPackedGlyphID const, SkPoint const>, skgpu::MaskFormat) const Unexecuted instantiation: SubRunContainer.cpp:sktext::gpu::SubRunContainer::MakeInAlloc(sktext::GlyphRunList const&, SkMatrix const&, SkPaint const&, SkStrikeDeviceInfo, sktext::StrikeForGPUCacheInterface*, sktext::gpu::SubRunAllocator*, sktext::gpu::SubRunContainer::SubRunCreationBehavior, char const*)::$_4::operator()(SkZip<SkPackedGlyphID const, SkPoint const>, skgpu::MaskFormat) const |
1967 | 0 | add_multi_mask_format(addGlyphsWithSameFormat, accepted); |
1968 | 0 | } |
1969 | 1 | } |
1970 | 4.71k | } |
1971 | | |
1972 | 901 | return container; |
1973 | 901 | } |
1974 | | |
1975 | | void SubRunContainer::draw(SkCanvas* canvas, |
1976 | | SkPoint drawOrigin, |
1977 | | const SkPaint& paint, |
1978 | | const SkRefCnt* subRunStorage, |
1979 | 901 | const AtlasDrawDelegate& atlasDelegate) const { |
1980 | 3.69k | for (auto& subRun : fSubRuns) { |
1981 | 3.69k | subRun.draw(canvas, drawOrigin, paint, sk_ref_sp(subRunStorage), atlasDelegate); |
1982 | 3.69k | } |
1983 | 901 | } |
1984 | | |
1985 | 107 | bool SubRunContainer::canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const { |
1986 | 325 | for (const SubRun& subRun : fSubRuns) { |
1987 | 325 | if (!subRun.canReuse(paint, positionMatrix)) { |
1988 | 6 | return false; |
1989 | 6 | } |
1990 | 325 | } |
1991 | 101 | return true; |
1992 | 107 | } |
1993 | | |
1994 | | // Returns the empty span if there is a problem reading the positions. |
1995 | 0 | SkSpan<SkPoint> MakePointsFromBuffer(SkReadBuffer& buffer, SubRunAllocator* alloc) { |
1996 | 0 | uint32_t glyphCount = buffer.getArrayCount(); |
1997 | | |
1998 | | // Zero indicates a problem with serialization. |
1999 | 0 | if (!buffer.validate(glyphCount != 0)) { return {}; } |
2000 | | |
2001 | | // Check that the count will not overflow the arena. |
2002 | 0 | if (!buffer.validate(glyphCount <= INT_MAX && |
2003 | 0 | BagOfBytes::WillCountFit<SkPoint>(glyphCount))) { return {}; } |
2004 | | |
2005 | 0 | SkPoint* positionsData = alloc->makePODArray<SkPoint>(glyphCount); |
2006 | 0 | if (!buffer.readPointArray(positionsData, glyphCount)) { return {}; } |
2007 | 0 | return {positionsData, glyphCount}; |
2008 | 0 | } |
2009 | | |
2010 | | } // namespace sktext::gpu |