Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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
73 SkSpan<SkPoint> leftTop = MakePointsFromBuffer(buffer, alloc);
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]
int count
uint32_t GrColor
Definition GrColor.h:25
SkColor4f color
#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:3824
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
SkScalar getSkewY() const
Definition SkMatrix.h:430
void mapHomogeneousPoints(SkPoint3 dst[], const SkPoint3 src[], int count) const
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()
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
bool isEmpty() const
Definition SkPath.cpp:406
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)
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]
static const uint8_t buffer[]
GAsyncResult * result
skgpu::MaskFormat MaskFormat
Definition fontcache.cpp:30
double y
double x
unsigned useCenter Optional< SkMatrix > matrix
Definition SkRecords.h:258
Definition ab.py:1
dst
Definition cp.py:12
std::array< Point, 4 > Quad
Definition point.h:321
static const int kMaskFormatCount
Definition AtlasTypes.h:105
skgpu::graphite::Rect Rect
static std::tuple< bool, SkVector > can_use_direct(const SkMatrix &creationMatrix, const SkMatrix &positionMatrix)
SkScalar w
SkScalar h
Point offset
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
constexpr float x() const
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