• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
8 #include "src/text/gpu/VertexFiller.h"
9 
10 #include "include/core/SkMatrix.h"
11 #include "include/core/SkPoint.h"
12 #include "include/core/SkPoint3.h"
13 #include "include/core/SkRect.h"
14 #include "include/core/SkScalar.h"
15 #include "include/core/SkTypes.h"
16 #include "include/private/base/SkTLogic.h"
17 #include "src/base/SkZip.h"
18 #include "src/core/SkReadBuffer.h"
19 #include "src/core/SkWriteBuffer.h"
20 #include "src/gpu/AtlasTypes.h"
21 #include "src/text/gpu/Glyph.h"
22 #include "src/text/gpu/SubRunAllocator.h"
23 #include "src/text/gpu/SubRunContainer.h"
24 
25 #if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
26 #include "src/gpu/ganesh/ops/AtlasTextOp.h"
27 #endif
28 
29 #include <cstdint>
30 #include <initializer_list>
31 #include <optional>
32 
33 using MaskFormat = skgpu::MaskFormat;
34 
35 namespace sktext::gpu {
36 
VertexFiller(MaskFormat maskFormat,const SkMatrix & creationMatrix,SkRect creationBounds,SkSpan<const SkPoint> leftTop,bool canDrawDirect)37 VertexFiller::VertexFiller(MaskFormat maskFormat,
38                            const SkMatrix &creationMatrix,
39                            SkRect creationBounds,
40                            SkSpan<const SkPoint> leftTop,
41                            bool canDrawDirect)
42             : fMaskType{maskFormat}, fCanDrawDirect{canDrawDirect},
43               fCreationMatrix{creationMatrix}, fCreationBounds{creationBounds},
44               fLeftTop{leftTop} {}
45 
Make(MaskFormat maskType,const SkMatrix & creationMatrix,SkRect creationBounds,SkSpan<const SkPoint> positions,SubRunAllocator * alloc,FillerType fillerType)46 VertexFiller VertexFiller::Make(MaskFormat maskType,
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 
MakeFromBuffer(SkReadBuffer & buffer,SubRunAllocator * alloc)57 std::optional<VertexFiller> VertexFiller::MakeFromBuffer(SkReadBuffer &buffer,
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 
flatten(SkWriteBuffer & buffer) const80 void VertexFiller::flatten(SkWriteBuffer &buffer) const {
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 
viewDifference(const SkMatrix & positionMatrix) const88 SkMatrix 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.
can_use_direct(const SkMatrix & creationMatrix,const SkMatrix & positionMatrix)98 static 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 
115 struct 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.
123 struct Mask2DVertex {
124     SkPoint devicePos;
125     GrColor color;
126     AtlasPt atlasPos;
127 };
128 
129 struct ARGB2DVertex {
ARGB2DVertexsktext::gpu::ARGB2DVertex130     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.
137 struct Mask3DVertex {
138     SkPoint3 devicePos;
139     GrColor color;
140     AtlasPt atlasPos;
141 };
142 
143 struct ARGB3DVertex {
ARGB3DVertexsktext::gpu::ARGB3DVertex144     ARGB3DVertex(SkPoint3 d, GrColor, AtlasPt a) : devicePos{d}, atlasPos{a} {}
145 
146     SkPoint3 devicePos;
147     AtlasPt atlasPos;
148 };
149 
vertexStride(const SkMatrix & matrix) const150 size_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.
fillDirectNoClipping(SkZip<Mask2DVertex[4],const Glyph *,const SkPoint> quadData,GrColor color,SkPoint originOffset)161 void fillDirectNoClipping(SkZip<Mask2DVertex[4], const Glyph*, const SkPoint> quadData,
162                           GrColor color,
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 
178 template <typename Rect>
LTBR(const Rect & r)179 static 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.
184 template<typename Quad, typename VertexData>
fillDirectClipped(SkZip<Quad,const Glyph *,const VertexData> quadData,GrColor color,SkPoint originOffset,SkIRect * clip=nullptr)185 static void fillDirectClipped(SkZip<Quad, const Glyph*, const VertexData> quadData,
186                               GrColor color,
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 
227 template<typename Quad, typename VertexData>
fill2D(SkZip<Quad,const Glyph *,const VertexData> quadData,GrColor color,const SkMatrix & viewDifference)228 static void fill2D(SkZip<Quad, const Glyph*, const VertexData> quadData,
229                    GrColor color,
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 
246 template<typename Quad, typename VertexData>
fill3D(SkZip<Quad,const Glyph *,const VertexData> quadData,GrColor color,const SkMatrix & viewDifference)247 static void fill3D(SkZip<Quad, const Glyph*, const VertexData> quadData,
248                    GrColor color,
249                    const SkMatrix& viewDifference) {
250     auto mapXYZ = [&](SkScalar x, SkScalar y) {
251         SkPoint pt{x, y};
252         SkPoint3 result;
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 
fillVertexData(int offset,int count,SkSpan<const Glyph * > glyphs,GrColor color,const SkMatrix & positionMatrix,SkIRect clip,void * vertexBuffer) const271 void VertexFiller::fillVertexData(int offset, int count,
272                                   SkSpan<const Glyph*> glyphs,
273                                   GrColor color,
274                                   const SkMatrix& positionMatrix,
275                                   SkIRect clip,
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 
339 using AtlasTextOp = skgpu::ganesh::AtlasTextOp;
opMaskType() const340 AtlasTextOp::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     }
346     SkUNREACHABLE;
347 }
348 #endif  // defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
349 
isLCD() const350 bool 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.
deviceRectAndCheckTransform(const SkMatrix & positionMatrix) const355 std::tuple<bool, SkRect> VertexFiller::deviceRectAndCheckTransform(
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
376