Flutter Engine
The Flutter Engine
VertexFiller.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2023 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
9
13#include "include/core/SkRect.h"
17#include "src/base/SkZip.h"
20#include "src/gpu/AtlasTypes.h"
21#include "src/text/gpu/Glyph.h"
24
25#if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
27#endif
28
29#include <cstdint>
30#include <initializer_list>
31#include <optional>
32
34
35namespace sktext::gpu {
36
38 const SkMatrix &creationMatrix,
39 SkRect creationBounds,
41 bool canDrawDirect)
42 : fMaskType{maskFormat}, fCanDrawDirect{canDrawDirect},
43 fCreationMatrix{creationMatrix}, fCreationBounds{creationBounds},
44 fLeftTop{leftTop} {}
45
47 const SkMatrix &creationMatrix,
48 SkRect creationBounds,
49 SkSpan<const SkPoint> positions,
50 SubRunAllocator *alloc,
51 FillerType fillerType) {
52 SkSpan<SkPoint> leftTop = alloc->makePODSpan<SkPoint>(positions);
53 return VertexFiller{
54 maskType, creationMatrix, creationBounds, leftTop, fillerType == kIsDirect};
55}
56
58 SubRunAllocator *alloc) {
59 int checkingMaskType = buffer.readInt();
60 if (!buffer.validate(
61 0 <= checkingMaskType && checkingMaskType < skgpu::kMaskFormatCount)) {
62 return std::nullopt;
63 }
64 MaskFormat maskType = (MaskFormat) checkingMaskType;
65
66 const bool canDrawDirect = buffer.readBool();
67
68 SkMatrix creationMatrix;
69 buffer.readMatrix(&creationMatrix);
70
71 SkRect creationBounds = buffer.readRect();
72
74 if (leftTop.empty()) { return std::nullopt; }
75
76 SkASSERT(buffer.isValid());
77 return VertexFiller{maskType, creationMatrix, creationBounds, leftTop, canDrawDirect};
78}
79
81 buffer.writeInt(static_cast<int>(fMaskType));
82 buffer.writeBool(fCanDrawDirect);
83 buffer.writeMatrix(fCreationMatrix);
84 buffer.writeRect(fCreationBounds);
85 buffer.writePointArray(fLeftTop.data(), SkCount(fLeftTop));
86}
87
88SkMatrix VertexFiller::viewDifference(const SkMatrix &positionMatrix) const {
89 if (SkMatrix inverse; fCreationMatrix.invert(&inverse)) {
90 return SkMatrix::Concat(positionMatrix, inverse);
91 }
92 return SkMatrix::I();
93}
94
95// Check for integer translate with the same 2x2 matrix.
96// Returns the translation, and true if the change from creation matrix to the position matrix
97// supports using direct glyph masks.
98static std::tuple<bool, SkVector> can_use_direct(
99 const SkMatrix& creationMatrix, const SkMatrix& positionMatrix) {
100 // The existing direct glyph info can be used if the creationMatrix, and the
101 // positionMatrix have the same 2x2, the translation between them is integer, and no
102 // perspective is involved. Calculate the translation in source space to a translation in
103 // device space by mapping (0, 0) through both the creationMatrix and the positionMatrix;
104 // take the difference.
105 SkVector translation = positionMatrix.mapOrigin() - creationMatrix.mapOrigin();
106 return {creationMatrix.getScaleX() == positionMatrix.getScaleX() &&
107 creationMatrix.getScaleY() == positionMatrix.getScaleY() &&
108 creationMatrix.getSkewX() == positionMatrix.getSkewX() &&
109 creationMatrix.getSkewY() == positionMatrix.getSkewY() &&
110 !positionMatrix.hasPerspective() && !creationMatrix.hasPerspective() &&
111 SkScalarIsInt(translation.x()) && SkScalarIsInt(translation.y()),
112 translation};
113}
114
115struct AtlasPt {
116 uint16_t u;
117 uint16_t v;
118};
119
120#if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
121
122// Normal text mask, SDFT, or color.
123struct Mask2DVertex {
124 SkPoint devicePos;
126 AtlasPt atlasPos;
127};
128
129struct ARGB2DVertex {
130 ARGB2DVertex(SkPoint d, GrColor, AtlasPt a) : devicePos{d}, atlasPos{a} {}
131
132 SkPoint devicePos;
133 AtlasPt atlasPos;
134};
135
136// Perspective SDFT or SDFT forced to 3D or perspective color.
137struct Mask3DVertex {
138 SkPoint3 devicePos;
140 AtlasPt atlasPos;
141};
142
143struct ARGB3DVertex {
144 ARGB3DVertex(SkPoint3 d, GrColor, AtlasPt a) : devicePos{d}, atlasPos{a} {}
145
146 SkPoint3 devicePos;
147 AtlasPt atlasPos;
148};
149
150size_t VertexFiller::vertexStride(const SkMatrix &matrix) const {
151 if (fMaskType != MaskFormat::kARGB) {
152 // For formats MaskFormat::kA565 and MaskFormat::kA8 where A8 include SDF.
153 return matrix.hasPerspective() ? sizeof(Mask3DVertex) : sizeof(Mask2DVertex);
154 } else {
155 // For format MaskFormat::kARGB
156 return matrix.hasPerspective() ? sizeof(ARGB3DVertex) : sizeof(ARGB2DVertex);
157 }
158}
159
160// The 99% case. Direct Mask, No clip, No RGB.
161void fillDirectNoClipping(SkZip<Mask2DVertex[4], const Glyph*, const SkPoint> quadData,
163 SkPoint originOffset) {
164 for (auto[quad, glyph, leftTop] : quadData) {
165 auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
166 SkScalar dl = leftTop.x() + originOffset.x(),
167 dt = leftTop.y() + originOffset.y(),
168 dr = dl + (ar - al),
169 db = dt + (ab - at);
170
171 quad[0] = {{dl, dt}, color, {al, at}}; // L,T
172 quad[1] = {{dl, db}, color, {al, ab}}; // L,B
173 quad[2] = {{dr, dt}, color, {ar, at}}; // R,T
174 quad[3] = {{dr, db}, color, {ar, ab}}; // R,B
175 }
176}
177
178template <typename Rect>
179static auto LTBR(const Rect& r) {
180 return std::make_tuple(r.left(), r.top(), r.right(), r.bottom());
181}
182
183// Handle any combination of BW or color and clip or no clip.
184template<typename Quad, typename VertexData>
185static void fillDirectClipped(SkZip<Quad, const Glyph*, const VertexData> quadData,
187 SkPoint originOffset,
188 SkIRect* clip = nullptr) {
189 for (auto[quad, glyph, leftTop] : quadData) {
190 auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
191 uint16_t w = ar - al,
192 h = ab - at;
193 SkScalar l = leftTop.x() + originOffset.x(),
194 t = leftTop.y() + originOffset.y();
195 if (clip == nullptr) {
196 auto[dl, dt, dr, db] = SkRect::MakeLTRB(l, t, l + w, t + h);
197 quad[0] = {{dl, dt}, color, {al, at}}; // L,T
198 quad[1] = {{dl, db}, color, {al, ab}}; // L,B
199 quad[2] = {{dr, dt}, color, {ar, at}}; // R,T
200 quad[3] = {{dr, db}, color, {ar, ab}}; // R,B
201 } else {
202 SkIRect devIRect = SkIRect::MakeLTRB(l, t, l + w, t + h);
203 SkScalar dl, dt, dr, db;
204 if (!clip->containsNoEmptyCheck(devIRect)) {
205 if (SkIRect clipped; clipped.intersect(devIRect, *clip)) {
206 al += clipped.left() - devIRect.left();
207 at += clipped.top() - devIRect.top();
208 ar += clipped.right() - devIRect.right();
209 ab += clipped.bottom() - devIRect.bottom();
210 std::tie(dl, dt, dr, db) = LTBR(clipped);
211 } else {
212 // TODO: omit generating any vertex data for fully clipped glyphs ?
213 std::tie(dl, dt, dr, db) = std::make_tuple(0, 0, 0, 0);
214 std::tie(al, at, ar, ab) = std::make_tuple(0, 0, 0, 0);
215 }
216 } else {
217 std::tie(dl, dt, dr, db) = LTBR(devIRect);
218 }
219 quad[0] = {{dl, dt}, color, {al, at}}; // L,T
220 quad[1] = {{dl, db}, color, {al, ab}}; // L,B
221 quad[2] = {{dr, dt}, color, {ar, at}}; // R,T
222 quad[3] = {{dr, db}, color, {ar, ab}}; // R,B
223 }
224 }
225}
226
227template<typename Quad, typename VertexData>
228static void fill2D(SkZip<Quad, const Glyph*, const VertexData> quadData,
230 const SkMatrix& viewDifference) {
231 for (auto [quad, glyph, leftTop] : quadData) {
232 auto [l, t] = leftTop;
233 auto [r, b] = leftTop + glyph->fAtlasLocator.widthHeight();
234 SkPoint lt = viewDifference.mapXY(l, t),
235 lb = viewDifference.mapXY(l, b),
236 rt = viewDifference.mapXY(r, t),
237 rb = viewDifference.mapXY(r, b);
238 auto [al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
239 quad[0] = {lt, color, {al, at}}; // L,T
240 quad[1] = {lb, color, {al, ab}}; // L,B
241 quad[2] = {rt, color, {ar, at}}; // R,T
242 quad[3] = {rb, color, {ar, ab}}; // R,B
243 }
244}
245
246template<typename Quad, typename VertexData>
247static void fill3D(SkZip<Quad, const Glyph*, const VertexData> quadData,
249 const SkMatrix& viewDifference) {
250 auto mapXYZ = [&](SkScalar x, SkScalar y) {
251 SkPoint pt{x, y};
253 viewDifference.mapHomogeneousPoints(&result, &pt, 1);
254 return result;
255 };
256 for (auto [quad, glyph, leftTop] : quadData) {
257 auto [l, t] = leftTop;
258 auto [r, b] = leftTop + glyph->fAtlasLocator.widthHeight();
259 SkPoint3 lt = mapXYZ(l, t),
260 lb = mapXYZ(l, b),
261 rt = mapXYZ(r, t),
262 rb = mapXYZ(r, b);
263 auto [al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
264 quad[0] = {lt, color, {al, at}}; // L,T
265 quad[1] = {lb, color, {al, ab}}; // L,B
266 quad[2] = {rt, color, {ar, at}}; // R,T
267 quad[3] = {rb, color, {ar, ab}}; // R,B
268 }
269}
270
271void VertexFiller::fillVertexData(int offset, int count,
274 const SkMatrix& positionMatrix,
276 void* vertexBuffer) const {
277 auto quadData = [&](auto dst) {
278 return SkMakeZip(dst,
279 glyphs.subspan(offset, count),
280 fLeftTop.subspan(offset, count));
281 };
282
283 // Handle direct mask drawing specifically.
284 if (fCanDrawDirect) {
285 auto [noTransformNeeded, originOffset] =
286 can_use_direct(fCreationMatrix, positionMatrix);
287
288 if (noTransformNeeded) {
289 if (clip.isEmpty()) {
290 if (fMaskType != MaskFormat::kARGB) {
291 using Quad = Mask2DVertex[4];
292 SkASSERT(sizeof(Mask2DVertex) == this->vertexStride(SkMatrix::I()));
293 fillDirectNoClipping(quadData((Quad*)vertexBuffer), color, originOffset);
294 } else {
295 using Quad = ARGB2DVertex[4];
296 SkASSERT(sizeof(ARGB2DVertex) == this->vertexStride(SkMatrix::I()));
297 fillDirectClipped(quadData((Quad*)vertexBuffer), color, originOffset);
298 }
299 } else {
300 if (fMaskType != MaskFormat::kARGB) {
301 using Quad = Mask2DVertex[4];
302 SkASSERT(sizeof(Mask2DVertex) == this->vertexStride(SkMatrix::I()));
303 fillDirectClipped(quadData((Quad*)vertexBuffer), color, originOffset, &clip);
304 } else {
305 using Quad = ARGB2DVertex[4];
306 SkASSERT(sizeof(ARGB2DVertex) == this->vertexStride(SkMatrix::I()));
307 fillDirectClipped(quadData((Quad*)vertexBuffer), color, originOffset, &clip);
308 }
309 }
310 return;
311 }
312 }
313
314 // Handle the general transformed case.
315 SkMatrix viewDifference = this->viewDifference(positionMatrix);
316 if (!positionMatrix.hasPerspective()) {
317 if (fMaskType == MaskFormat::kARGB) {
318 using Quad = ARGB2DVertex[4];
319 SkASSERT(sizeof(ARGB2DVertex) == this->vertexStride(positionMatrix));
320 fill2D(quadData((Quad*)vertexBuffer), color, viewDifference);
321 } else {
322 using Quad = Mask2DVertex[4];
323 SkASSERT(sizeof(Mask2DVertex) == this->vertexStride(positionMatrix));
324 fill2D(quadData((Quad*)vertexBuffer), color, viewDifference);
325 }
326 } else {
327 if (fMaskType == MaskFormat::kARGB) {
328 using Quad = ARGB3DVertex[4];
329 SkASSERT(sizeof(ARGB3DVertex) == this->vertexStride(positionMatrix));
330 fill3D(quadData((Quad*)vertexBuffer), color, viewDifference);
331 } else {
332 using Quad = Mask3DVertex[4];
333 SkASSERT(sizeof(Mask3DVertex) == this->vertexStride(positionMatrix));
334 fill3D(quadData((Quad*)vertexBuffer), color, viewDifference);
335 }
336 }
337}
338
339using AtlasTextOp = skgpu::ganesh::AtlasTextOp;
340AtlasTextOp::MaskType VertexFiller::opMaskType() const {
341 switch (fMaskType) {
342 case MaskFormat::kA8: return AtlasTextOp::MaskType::kGrayscaleCoverage;
343 case MaskFormat::kA565: return AtlasTextOp::MaskType::kLCDCoverage;
344 case MaskFormat::kARGB: return AtlasTextOp::MaskType::kColorBitmap;
345 }
347}
348#endif // defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
349
350bool VertexFiller::isLCD() const { return fMaskType == MaskFormat::kA565; }
351
352// Return true if the positionMatrix represents an integer translation. Return the device
353// bounding box of all the glyphs. If the bounding box is empty, then something went singular
354// and this operation should be dropped.
356 const SkMatrix &positionMatrix) const {
357 if (fCanDrawDirect) {
358 const auto [directDrawCompatible, offset] =
359 can_use_direct(fCreationMatrix, positionMatrix);
360
361 if (directDrawCompatible) {
362 return {true, fCreationBounds.makeOffset(offset)};
363 }
364 }
365
366 if (SkMatrix inverse; fCreationMatrix.invert(&inverse)) {
367 SkMatrix viewDifference = SkMatrix::Concat(positionMatrix, inverse);
368 return {false, viewDifference.mapRect(fCreationBounds)};
369 }
370
371 // initialPositionMatrix is singular. Do nothing.
372 return {false, SkRect::MakeEmpty()};
373}
374
375} // namespace sktext::gpu
uint16_t glyphs[5]
Definition: FontMgrTest.cpp:46
int count
Definition: FontMgrTest.cpp:50
uint32_t GrColor
Definition: GrColor.h:25
#define SkUNREACHABLE
Definition: SkAssert.h:135
#define SkASSERT(cond)
Definition: SkAssert.h:116
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition: SkPath.cpp:3892
static bool SkScalarIsInt(SkScalar x)
Definition: SkScalar.h:80
constexpr int SkCount(const Container &c)
Definition: SkTLogic.h:54
constexpr auto SkMakeZip(Ts &&... ts)
Definition: SkZip.h:212
skgpu::MaskFormat MaskFormat
SkScalar getSkewY() const
Definition: SkMatrix.h:430
void mapHomogeneousPoints(SkPoint3 dst[], const SkPoint3 src[], int count) const
Definition: SkMatrix.cpp:1066
static SkMatrix Concat(const SkMatrix &a, const SkMatrix &b)
Definition: SkMatrix.h:1775
SkScalar getSkewX() const
Definition: SkMatrix.h:438
bool invert(SkMatrix *inverse) const
Definition: SkMatrix.h:1206
void mapXY(SkScalar x, SkScalar y, SkPoint *result) const
Definition: SkMatrix.cpp:777
SkScalar getScaleX() const
Definition: SkMatrix.h:415
static const SkMatrix & I()
Definition: SkMatrix.cpp:1544
SkScalar getScaleY() const
Definition: SkMatrix.h:422
SkPoint mapOrigin() const
Definition: SkMatrix.h:1437
bool hasPerspective() const
Definition: SkMatrix.h:312
bool mapRect(SkRect *dst, const SkRect &src, SkApplyPerspectiveClip pc=SkApplyPerspectiveClip::kYes) const
Definition: SkMatrix.cpp:1141
bool isEmpty() const
Definition: SkPath.cpp:416
constexpr bool empty() const
Definition: SkSpan_impl.h:96
Definition: SkZip.h:25
SkSpan< T > makePODSpan(SkSpan< const T > s)
static std::optional< VertexFiller > MakeFromBuffer(SkReadBuffer &buffer, SubRunAllocator *alloc)
static VertexFiller Make(skgpu::MaskFormat maskType, const SkMatrix &creationMatrix, SkRect creationBounds, SkSpan< const SkPoint > positions, SubRunAllocator *alloc, FillerType fillerType)
std::tuple< bool, SkRect > deviceRectAndCheckTransform(const SkMatrix &positionMatrix) const
void flatten(SkWriteBuffer &buffer) const
VertexFiller(skgpu::MaskFormat maskFormat, const SkMatrix &creationMatrix, SkRect creationBounds, SkSpan< const SkPoint > leftTop, bool canDrawDirect)
DlColor color
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition: main.cc:19
float SkScalar
Definition: extension.cpp:12
static bool b
struct MyStruct a[10]
GAsyncResult * result
double y
double x
unsigned useCenter Optional< SkMatrix > matrix
Definition: SkRecords.h:258
Definition: ab.py:1
const CatchEntryMove ab[]
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
dst
Definition: cp.py:12
std::array< Point, 4 > Quad
Definition: point.h:327
static const int kMaskFormatCount
Definition: AtlasTypes.h:105
MaskFormat
Definition: AtlasTypes.h:98
skgpu::graphite::Rect Rect
static std::tuple< bool, SkVector > can_use_direct(const SkMatrix &creationMatrix, const SkMatrix &positionMatrix)
SkSpan< SkPoint > MakePointsFromBuffer(SkReadBuffer &buffer, SubRunAllocator *alloc)
SkScalar w
SkScalar h
SeparatedVector2 offset
Definition: SkRect.h:32
bool intersect(const SkIRect &r)
Definition: SkRect.h:513
constexpr int32_t top() const
Definition: SkRect.h:120
static constexpr SkIRect MakeLTRB(int32_t l, int32_t t, int32_t r, int32_t b)
Definition: SkRect.h:91
constexpr int32_t bottom() const
Definition: SkRect.h:134
constexpr int32_t right() const
Definition: SkRect.h:127
constexpr int32_t left() const
Definition: SkRect.h:113
constexpr float y() const
Definition: SkPoint_impl.h:187
constexpr float x() const
Definition: SkPoint_impl.h:181
static constexpr SkRect MakeEmpty()
Definition: SkRect.h:595
constexpr SkRect makeOffset(float dx, float dy) const
Definition: SkRect.h:965
static constexpr SkRect MakeLTRB(float l, float t, float r, float b)
Definition: SkRect.h:646