xref: /aosp_15_r20/external/skia/src/text/gpu/VertexFiller.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2023 Google LLC
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "src/text/gpu/VertexFiller.h"
9*c8dee2aaSAndroid Build Coastguard Worker 
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkMatrix.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPoint.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRect.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkScalar.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTLogic.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkReadBuffer.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkWriteBuffer.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/AtlasTypes.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/text/gpu/SubRunAllocator.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "src/text/gpu/SubRunContainer.h"
21*c8dee2aaSAndroid Build Coastguard Worker 
22*c8dee2aaSAndroid Build Coastguard Worker #include <cstdint>
23*c8dee2aaSAndroid Build Coastguard Worker #include <optional>
24*c8dee2aaSAndroid Build Coastguard Worker 
25*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
26*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPoint3.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkZip.h"
28*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/ops/AtlasTextOp.h"
29*c8dee2aaSAndroid Build Coastguard Worker #include "src/text/gpu/Glyph.h"
30*c8dee2aaSAndroid Build Coastguard Worker #endif
31*c8dee2aaSAndroid Build Coastguard Worker 
32*c8dee2aaSAndroid Build Coastguard Worker using MaskFormat = skgpu::MaskFormat;
33*c8dee2aaSAndroid Build Coastguard Worker 
34*c8dee2aaSAndroid Build Coastguard Worker namespace sktext::gpu {
35*c8dee2aaSAndroid Build Coastguard Worker 
VertexFiller(MaskFormat maskFormat,const SkMatrix & creationMatrix,SkRect creationBounds,SkSpan<const SkPoint> leftTop,bool canDrawDirect)36*c8dee2aaSAndroid Build Coastguard Worker VertexFiller::VertexFiller(MaskFormat maskFormat,
37*c8dee2aaSAndroid Build Coastguard Worker                            const SkMatrix &creationMatrix,
38*c8dee2aaSAndroid Build Coastguard Worker                            SkRect creationBounds,
39*c8dee2aaSAndroid Build Coastguard Worker                            SkSpan<const SkPoint> leftTop,
40*c8dee2aaSAndroid Build Coastguard Worker                            bool canDrawDirect)
41*c8dee2aaSAndroid Build Coastguard Worker             : fMaskType{maskFormat}, fCanDrawDirect{canDrawDirect},
42*c8dee2aaSAndroid Build Coastguard Worker               fCreationMatrix{creationMatrix}, fCreationBounds{creationBounds},
43*c8dee2aaSAndroid Build Coastguard Worker               fLeftTop{leftTop} {}
44*c8dee2aaSAndroid Build Coastguard Worker 
Make(MaskFormat maskType,const SkMatrix & creationMatrix,SkRect creationBounds,SkSpan<const SkPoint> positions,SubRunAllocator * alloc,FillerType fillerType)45*c8dee2aaSAndroid Build Coastguard Worker VertexFiller VertexFiller::Make(MaskFormat maskType,
46*c8dee2aaSAndroid Build Coastguard Worker                                 const SkMatrix &creationMatrix,
47*c8dee2aaSAndroid Build Coastguard Worker                                 SkRect creationBounds,
48*c8dee2aaSAndroid Build Coastguard Worker                                 SkSpan<const SkPoint> positions,
49*c8dee2aaSAndroid Build Coastguard Worker                                 SubRunAllocator *alloc,
50*c8dee2aaSAndroid Build Coastguard Worker                                 FillerType fillerType) {
51*c8dee2aaSAndroid Build Coastguard Worker     SkSpan<SkPoint> leftTop = alloc->makePODSpan<SkPoint>(positions);
52*c8dee2aaSAndroid Build Coastguard Worker     return VertexFiller{
53*c8dee2aaSAndroid Build Coastguard Worker             maskType, creationMatrix, creationBounds, leftTop, fillerType == kIsDirect};
54*c8dee2aaSAndroid Build Coastguard Worker }
55*c8dee2aaSAndroid Build Coastguard Worker 
MakeFromBuffer(SkReadBuffer & buffer,SubRunAllocator * alloc)56*c8dee2aaSAndroid Build Coastguard Worker std::optional<VertexFiller> VertexFiller::MakeFromBuffer(SkReadBuffer &buffer,
57*c8dee2aaSAndroid Build Coastguard Worker                                                          SubRunAllocator *alloc) {
58*c8dee2aaSAndroid Build Coastguard Worker     int checkingMaskType = buffer.readInt();
59*c8dee2aaSAndroid Build Coastguard Worker     if (!buffer.validate(
60*c8dee2aaSAndroid Build Coastguard Worker             0 <= checkingMaskType && checkingMaskType < skgpu::kMaskFormatCount)) {
61*c8dee2aaSAndroid Build Coastguard Worker         return std::nullopt;
62*c8dee2aaSAndroid Build Coastguard Worker     }
63*c8dee2aaSAndroid Build Coastguard Worker     MaskFormat maskType = (MaskFormat) checkingMaskType;
64*c8dee2aaSAndroid Build Coastguard Worker 
65*c8dee2aaSAndroid Build Coastguard Worker     const bool canDrawDirect = buffer.readBool();
66*c8dee2aaSAndroid Build Coastguard Worker 
67*c8dee2aaSAndroid Build Coastguard Worker     SkMatrix creationMatrix;
68*c8dee2aaSAndroid Build Coastguard Worker     buffer.readMatrix(&creationMatrix);
69*c8dee2aaSAndroid Build Coastguard Worker 
70*c8dee2aaSAndroid Build Coastguard Worker     SkRect creationBounds = buffer.readRect();
71*c8dee2aaSAndroid Build Coastguard Worker 
72*c8dee2aaSAndroid Build Coastguard Worker     SkSpan<SkPoint> leftTop = MakePointsFromBuffer(buffer, alloc);
73*c8dee2aaSAndroid Build Coastguard Worker     if (leftTop.empty()) { return std::nullopt; }
74*c8dee2aaSAndroid Build Coastguard Worker 
75*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(buffer.isValid());
76*c8dee2aaSAndroid Build Coastguard Worker     return VertexFiller{maskType, creationMatrix, creationBounds, leftTop, canDrawDirect};
77*c8dee2aaSAndroid Build Coastguard Worker }
78*c8dee2aaSAndroid Build Coastguard Worker 
flatten(SkWriteBuffer & buffer) const79*c8dee2aaSAndroid Build Coastguard Worker void VertexFiller::flatten(SkWriteBuffer &buffer) const {
80*c8dee2aaSAndroid Build Coastguard Worker     buffer.writeInt(static_cast<int>(fMaskType));
81*c8dee2aaSAndroid Build Coastguard Worker     buffer.writeBool(fCanDrawDirect);
82*c8dee2aaSAndroid Build Coastguard Worker     buffer.writeMatrix(fCreationMatrix);
83*c8dee2aaSAndroid Build Coastguard Worker     buffer.writeRect(fCreationBounds);
84*c8dee2aaSAndroid Build Coastguard Worker     buffer.writePointArray(fLeftTop.data(), SkCount(fLeftTop));
85*c8dee2aaSAndroid Build Coastguard Worker }
86*c8dee2aaSAndroid Build Coastguard Worker 
viewDifference(const SkMatrix & positionMatrix) const87*c8dee2aaSAndroid Build Coastguard Worker SkMatrix VertexFiller::viewDifference(const SkMatrix &positionMatrix) const {
88*c8dee2aaSAndroid Build Coastguard Worker     if (SkMatrix inverse; fCreationMatrix.invert(&inverse)) {
89*c8dee2aaSAndroid Build Coastguard Worker         return SkMatrix::Concat(positionMatrix, inverse);
90*c8dee2aaSAndroid Build Coastguard Worker     }
91*c8dee2aaSAndroid Build Coastguard Worker     return SkMatrix::I();
92*c8dee2aaSAndroid Build Coastguard Worker }
93*c8dee2aaSAndroid Build Coastguard Worker 
94*c8dee2aaSAndroid Build Coastguard Worker // Check for integer translate with the same 2x2 matrix.
95*c8dee2aaSAndroid Build Coastguard Worker // Returns the translation, and true if the change from creation matrix to the position matrix
96*c8dee2aaSAndroid Build Coastguard Worker // supports using direct glyph masks.
can_use_direct(const SkMatrix & creationMatrix,const SkMatrix & positionMatrix)97*c8dee2aaSAndroid Build Coastguard Worker static std::tuple<bool, SkVector> can_use_direct(
98*c8dee2aaSAndroid Build Coastguard Worker         const SkMatrix& creationMatrix, const SkMatrix& positionMatrix) {
99*c8dee2aaSAndroid Build Coastguard Worker     // The existing direct glyph info can be used if the creationMatrix, and the
100*c8dee2aaSAndroid Build Coastguard Worker     // positionMatrix have the same 2x2, the translation between them is integer, and no
101*c8dee2aaSAndroid Build Coastguard Worker     // perspective is involved. Calculate the translation in source space to a translation in
102*c8dee2aaSAndroid Build Coastguard Worker     // device space by mapping (0, 0) through both the creationMatrix and the positionMatrix;
103*c8dee2aaSAndroid Build Coastguard Worker     // take the difference.
104*c8dee2aaSAndroid Build Coastguard Worker     SkVector translation = positionMatrix.mapOrigin() - creationMatrix.mapOrigin();
105*c8dee2aaSAndroid Build Coastguard Worker     return {creationMatrix.getScaleX() == positionMatrix.getScaleX() &&
106*c8dee2aaSAndroid Build Coastguard Worker             creationMatrix.getScaleY() == positionMatrix.getScaleY() &&
107*c8dee2aaSAndroid Build Coastguard Worker             creationMatrix.getSkewX()  == positionMatrix.getSkewX()  &&
108*c8dee2aaSAndroid Build Coastguard Worker             creationMatrix.getSkewY()  == positionMatrix.getSkewY()  &&
109*c8dee2aaSAndroid Build Coastguard Worker             !positionMatrix.hasPerspective() && !creationMatrix.hasPerspective() &&
110*c8dee2aaSAndroid Build Coastguard Worker             SkScalarIsInt(translation.x()) && SkScalarIsInt(translation.y()),
111*c8dee2aaSAndroid Build Coastguard Worker             translation};
112*c8dee2aaSAndroid Build Coastguard Worker }
113*c8dee2aaSAndroid Build Coastguard Worker 
114*c8dee2aaSAndroid Build Coastguard Worker struct AtlasPt {
115*c8dee2aaSAndroid Build Coastguard Worker     uint16_t u;
116*c8dee2aaSAndroid Build Coastguard Worker     uint16_t v;
117*c8dee2aaSAndroid Build Coastguard Worker };
118*c8dee2aaSAndroid Build Coastguard Worker 
119*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
120*c8dee2aaSAndroid Build Coastguard Worker 
121*c8dee2aaSAndroid Build Coastguard Worker // Normal text mask, SDFT, or color.
122*c8dee2aaSAndroid Build Coastguard Worker struct Mask2DVertex {
123*c8dee2aaSAndroid Build Coastguard Worker     SkPoint devicePos;
124*c8dee2aaSAndroid Build Coastguard Worker     GrColor color;
125*c8dee2aaSAndroid Build Coastguard Worker     AtlasPt atlasPos;
126*c8dee2aaSAndroid Build Coastguard Worker };
127*c8dee2aaSAndroid Build Coastguard Worker 
128*c8dee2aaSAndroid Build Coastguard Worker struct ARGB2DVertex {
ARGB2DVertexsktext::gpu::ARGB2DVertex129*c8dee2aaSAndroid Build Coastguard Worker     ARGB2DVertex(SkPoint d, GrColor, AtlasPt a) : devicePos{d}, atlasPos{a} {}
130*c8dee2aaSAndroid Build Coastguard Worker 
131*c8dee2aaSAndroid Build Coastguard Worker     SkPoint devicePos;
132*c8dee2aaSAndroid Build Coastguard Worker     AtlasPt atlasPos;
133*c8dee2aaSAndroid Build Coastguard Worker };
134*c8dee2aaSAndroid Build Coastguard Worker 
135*c8dee2aaSAndroid Build Coastguard Worker // Perspective SDFT or SDFT forced to 3D or perspective color.
136*c8dee2aaSAndroid Build Coastguard Worker struct Mask3DVertex {
137*c8dee2aaSAndroid Build Coastguard Worker     SkPoint3 devicePos;
138*c8dee2aaSAndroid Build Coastguard Worker     GrColor color;
139*c8dee2aaSAndroid Build Coastguard Worker     AtlasPt atlasPos;
140*c8dee2aaSAndroid Build Coastguard Worker };
141*c8dee2aaSAndroid Build Coastguard Worker 
142*c8dee2aaSAndroid Build Coastguard Worker struct ARGB3DVertex {
ARGB3DVertexsktext::gpu::ARGB3DVertex143*c8dee2aaSAndroid Build Coastguard Worker     ARGB3DVertex(SkPoint3 d, GrColor, AtlasPt a) : devicePos{d}, atlasPos{a} {}
144*c8dee2aaSAndroid Build Coastguard Worker 
145*c8dee2aaSAndroid Build Coastguard Worker     SkPoint3 devicePos;
146*c8dee2aaSAndroid Build Coastguard Worker     AtlasPt atlasPos;
147*c8dee2aaSAndroid Build Coastguard Worker };
148*c8dee2aaSAndroid Build Coastguard Worker 
vertexStride(const SkMatrix & matrix) const149*c8dee2aaSAndroid Build Coastguard Worker size_t VertexFiller::vertexStride(const SkMatrix &matrix) const {
150*c8dee2aaSAndroid Build Coastguard Worker     if (fMaskType != MaskFormat::kARGB) {
151*c8dee2aaSAndroid Build Coastguard Worker         // For formats MaskFormat::kA565 and MaskFormat::kA8 where A8 include SDF.
152*c8dee2aaSAndroid Build Coastguard Worker         return matrix.hasPerspective() ? sizeof(Mask3DVertex) : sizeof(Mask2DVertex);
153*c8dee2aaSAndroid Build Coastguard Worker     } else {
154*c8dee2aaSAndroid Build Coastguard Worker         // For format MaskFormat::kARGB
155*c8dee2aaSAndroid Build Coastguard Worker         return matrix.hasPerspective() ? sizeof(ARGB3DVertex) : sizeof(ARGB2DVertex);
156*c8dee2aaSAndroid Build Coastguard Worker     }
157*c8dee2aaSAndroid Build Coastguard Worker }
158*c8dee2aaSAndroid Build Coastguard Worker 
159*c8dee2aaSAndroid Build Coastguard Worker // The 99% case. Direct Mask, No clip, No RGB.
fillDirectNoClipping(SkZip<Mask2DVertex[4],const Glyph *,const SkPoint> quadData,GrColor color,SkPoint originOffset)160*c8dee2aaSAndroid Build Coastguard Worker void fillDirectNoClipping(SkZip<Mask2DVertex[4], const Glyph*, const SkPoint> quadData,
161*c8dee2aaSAndroid Build Coastguard Worker                           GrColor color,
162*c8dee2aaSAndroid Build Coastguard Worker                           SkPoint originOffset) {
163*c8dee2aaSAndroid Build Coastguard Worker     for (auto[quad, glyph, leftTop] : quadData) {
164*c8dee2aaSAndroid Build Coastguard Worker         auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
165*c8dee2aaSAndroid Build Coastguard Worker         SkScalar dl = leftTop.x() + originOffset.x(),
166*c8dee2aaSAndroid Build Coastguard Worker                  dt = leftTop.y() + originOffset.y(),
167*c8dee2aaSAndroid Build Coastguard Worker                  dr = dl + (ar - al),
168*c8dee2aaSAndroid Build Coastguard Worker                  db = dt + (ab - at);
169*c8dee2aaSAndroid Build Coastguard Worker 
170*c8dee2aaSAndroid Build Coastguard Worker         quad[0] = {{dl, dt}, color, {al, at}};  // L,T
171*c8dee2aaSAndroid Build Coastguard Worker         quad[1] = {{dl, db}, color, {al, ab}};  // L,B
172*c8dee2aaSAndroid Build Coastguard Worker         quad[2] = {{dr, dt}, color, {ar, at}};  // R,T
173*c8dee2aaSAndroid Build Coastguard Worker         quad[3] = {{dr, db}, color, {ar, ab}};  // R,B
174*c8dee2aaSAndroid Build Coastguard Worker     }
175*c8dee2aaSAndroid Build Coastguard Worker }
176*c8dee2aaSAndroid Build Coastguard Worker 
177*c8dee2aaSAndroid Build Coastguard Worker template <typename Rect>
LTBR(const Rect & r)178*c8dee2aaSAndroid Build Coastguard Worker static auto LTBR(const Rect& r) {
179*c8dee2aaSAndroid Build Coastguard Worker     return std::make_tuple(r.left(), r.top(), r.right(), r.bottom());
180*c8dee2aaSAndroid Build Coastguard Worker }
181*c8dee2aaSAndroid Build Coastguard Worker 
182*c8dee2aaSAndroid Build Coastguard Worker // Handle any combination of BW or color and clip or no clip.
183*c8dee2aaSAndroid Build Coastguard Worker template<typename Quad, typename VertexData>
fillDirectClipped(SkZip<Quad,const Glyph *,const VertexData> quadData,GrColor color,SkPoint originOffset,SkIRect * clip=nullptr)184*c8dee2aaSAndroid Build Coastguard Worker static void fillDirectClipped(SkZip<Quad, const Glyph*, const VertexData> quadData,
185*c8dee2aaSAndroid Build Coastguard Worker                               GrColor color,
186*c8dee2aaSAndroid Build Coastguard Worker                               SkPoint originOffset,
187*c8dee2aaSAndroid Build Coastguard Worker                               SkIRect* clip = nullptr) {
188*c8dee2aaSAndroid Build Coastguard Worker     for (auto[quad, glyph, leftTop] : quadData) {
189*c8dee2aaSAndroid Build Coastguard Worker         auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
190*c8dee2aaSAndroid Build Coastguard Worker         uint16_t w = ar - al,
191*c8dee2aaSAndroid Build Coastguard Worker                  h = ab - at;
192*c8dee2aaSAndroid Build Coastguard Worker         SkScalar l = leftTop.x() + originOffset.x(),
193*c8dee2aaSAndroid Build Coastguard Worker                  t = leftTop.y() + originOffset.y();
194*c8dee2aaSAndroid Build Coastguard Worker         if (clip == nullptr) {
195*c8dee2aaSAndroid Build Coastguard Worker             auto[dl, dt, dr, db] = SkRect::MakeLTRB(l, t, l + w, t + h);
196*c8dee2aaSAndroid Build Coastguard Worker             quad[0] = {{dl, dt}, color, {al, at}};  // L,T
197*c8dee2aaSAndroid Build Coastguard Worker             quad[1] = {{dl, db}, color, {al, ab}};  // L,B
198*c8dee2aaSAndroid Build Coastguard Worker             quad[2] = {{dr, dt}, color, {ar, at}};  // R,T
199*c8dee2aaSAndroid Build Coastguard Worker             quad[3] = {{dr, db}, color, {ar, ab}};  // R,B
200*c8dee2aaSAndroid Build Coastguard Worker         } else {
201*c8dee2aaSAndroid Build Coastguard Worker             SkIRect devIRect = SkIRect::MakeLTRB(l, t, l + w, t + h);
202*c8dee2aaSAndroid Build Coastguard Worker             SkScalar dl, dt, dr, db;
203*c8dee2aaSAndroid Build Coastguard Worker             if (!clip->containsNoEmptyCheck(devIRect)) {
204*c8dee2aaSAndroid Build Coastguard Worker                 if (SkIRect clipped; clipped.intersect(devIRect, *clip)) {
205*c8dee2aaSAndroid Build Coastguard Worker                     al += clipped.left()   - devIRect.left();
206*c8dee2aaSAndroid Build Coastguard Worker                     at += clipped.top()    - devIRect.top();
207*c8dee2aaSAndroid Build Coastguard Worker                     ar += clipped.right()  - devIRect.right();
208*c8dee2aaSAndroid Build Coastguard Worker                     ab += clipped.bottom() - devIRect.bottom();
209*c8dee2aaSAndroid Build Coastguard Worker                     std::tie(dl, dt, dr, db) = LTBR(clipped);
210*c8dee2aaSAndroid Build Coastguard Worker                 } else {
211*c8dee2aaSAndroid Build Coastguard Worker                     // TODO: omit generating any vertex data for fully clipped glyphs ?
212*c8dee2aaSAndroid Build Coastguard Worker                     std::tie(dl, dt, dr, db) = std::make_tuple(0, 0, 0, 0);
213*c8dee2aaSAndroid Build Coastguard Worker                     std::tie(al, at, ar, ab) = std::make_tuple(0, 0, 0, 0);
214*c8dee2aaSAndroid Build Coastguard Worker                 }
215*c8dee2aaSAndroid Build Coastguard Worker             } else {
216*c8dee2aaSAndroid Build Coastguard Worker                 std::tie(dl, dt, dr, db) = LTBR(devIRect);
217*c8dee2aaSAndroid Build Coastguard Worker             }
218*c8dee2aaSAndroid Build Coastguard Worker             quad[0] = {{dl, dt}, color, {al, at}};  // L,T
219*c8dee2aaSAndroid Build Coastguard Worker             quad[1] = {{dl, db}, color, {al, ab}};  // L,B
220*c8dee2aaSAndroid Build Coastguard Worker             quad[2] = {{dr, dt}, color, {ar, at}};  // R,T
221*c8dee2aaSAndroid Build Coastguard Worker             quad[3] = {{dr, db}, color, {ar, ab}};  // R,B
222*c8dee2aaSAndroid Build Coastguard Worker         }
223*c8dee2aaSAndroid Build Coastguard Worker     }
224*c8dee2aaSAndroid Build Coastguard Worker }
225*c8dee2aaSAndroid Build Coastguard Worker 
226*c8dee2aaSAndroid Build Coastguard Worker template<typename Quad, typename VertexData>
fill2D(SkZip<Quad,const Glyph *,const VertexData> quadData,GrColor color,const SkMatrix & viewDifference)227*c8dee2aaSAndroid Build Coastguard Worker static void fill2D(SkZip<Quad, const Glyph*, const VertexData> quadData,
228*c8dee2aaSAndroid Build Coastguard Worker                    GrColor color,
229*c8dee2aaSAndroid Build Coastguard Worker                    const SkMatrix& viewDifference) {
230*c8dee2aaSAndroid Build Coastguard Worker     for (auto [quad, glyph, leftTop] : quadData) {
231*c8dee2aaSAndroid Build Coastguard Worker         auto [l, t] = leftTop;
232*c8dee2aaSAndroid Build Coastguard Worker         auto [r, b] = leftTop + glyph->fAtlasLocator.widthHeight();
233*c8dee2aaSAndroid Build Coastguard Worker         SkPoint lt = viewDifference.mapXY(l, t),
234*c8dee2aaSAndroid Build Coastguard Worker                 lb = viewDifference.mapXY(l, b),
235*c8dee2aaSAndroid Build Coastguard Worker                 rt = viewDifference.mapXY(r, t),
236*c8dee2aaSAndroid Build Coastguard Worker                 rb = viewDifference.mapXY(r, b);
237*c8dee2aaSAndroid Build Coastguard Worker         auto [al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
238*c8dee2aaSAndroid Build Coastguard Worker         quad[0] = {lt, color, {al, at}};  // L,T
239*c8dee2aaSAndroid Build Coastguard Worker         quad[1] = {lb, color, {al, ab}};  // L,B
240*c8dee2aaSAndroid Build Coastguard Worker         quad[2] = {rt, color, {ar, at}};  // R,T
241*c8dee2aaSAndroid Build Coastguard Worker         quad[3] = {rb, color, {ar, ab}};  // R,B
242*c8dee2aaSAndroid Build Coastguard Worker     }
243*c8dee2aaSAndroid Build Coastguard Worker }
244*c8dee2aaSAndroid Build Coastguard Worker 
245*c8dee2aaSAndroid Build Coastguard Worker template<typename Quad, typename VertexData>
fill3D(SkZip<Quad,const Glyph *,const VertexData> quadData,GrColor color,const SkMatrix & viewDifference)246*c8dee2aaSAndroid Build Coastguard Worker static void fill3D(SkZip<Quad, const Glyph*, const VertexData> quadData,
247*c8dee2aaSAndroid Build Coastguard Worker                    GrColor color,
248*c8dee2aaSAndroid Build Coastguard Worker                    const SkMatrix& viewDifference) {
249*c8dee2aaSAndroid Build Coastguard Worker     auto mapXYZ = [&](SkScalar x, SkScalar y) {
250*c8dee2aaSAndroid Build Coastguard Worker         SkPoint pt{x, y};
251*c8dee2aaSAndroid Build Coastguard Worker         SkPoint3 result;
252*c8dee2aaSAndroid Build Coastguard Worker         viewDifference.mapHomogeneousPoints(&result, &pt, 1);
253*c8dee2aaSAndroid Build Coastguard Worker         return result;
254*c8dee2aaSAndroid Build Coastguard Worker     };
255*c8dee2aaSAndroid Build Coastguard Worker     for (auto [quad, glyph, leftTop] : quadData) {
256*c8dee2aaSAndroid Build Coastguard Worker         auto [l, t] = leftTop;
257*c8dee2aaSAndroid Build Coastguard Worker         auto [r, b] = leftTop + glyph->fAtlasLocator.widthHeight();
258*c8dee2aaSAndroid Build Coastguard Worker         SkPoint3 lt = mapXYZ(l, t),
259*c8dee2aaSAndroid Build Coastguard Worker                 lb = mapXYZ(l, b),
260*c8dee2aaSAndroid Build Coastguard Worker                 rt = mapXYZ(r, t),
261*c8dee2aaSAndroid Build Coastguard Worker                 rb = mapXYZ(r, b);
262*c8dee2aaSAndroid Build Coastguard Worker         auto [al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
263*c8dee2aaSAndroid Build Coastguard Worker         quad[0] = {lt, color, {al, at}};  // L,T
264*c8dee2aaSAndroid Build Coastguard Worker         quad[1] = {lb, color, {al, ab}};  // L,B
265*c8dee2aaSAndroid Build Coastguard Worker         quad[2] = {rt, color, {ar, at}};  // R,T
266*c8dee2aaSAndroid Build Coastguard Worker         quad[3] = {rb, color, {ar, ab}};  // R,B
267*c8dee2aaSAndroid Build Coastguard Worker     }
268*c8dee2aaSAndroid Build Coastguard Worker }
269*c8dee2aaSAndroid Build Coastguard Worker 
fillVertexData(int offset,int count,SkSpan<const Glyph * > glyphs,GrColor color,const SkMatrix & positionMatrix,SkIRect clip,void * vertexBuffer) const270*c8dee2aaSAndroid Build Coastguard Worker void VertexFiller::fillVertexData(int offset, int count,
271*c8dee2aaSAndroid Build Coastguard Worker                                   SkSpan<const Glyph*> glyphs,
272*c8dee2aaSAndroid Build Coastguard Worker                                   GrColor color,
273*c8dee2aaSAndroid Build Coastguard Worker                                   const SkMatrix& positionMatrix,
274*c8dee2aaSAndroid Build Coastguard Worker                                   SkIRect clip,
275*c8dee2aaSAndroid Build Coastguard Worker                                   void* vertexBuffer) const {
276*c8dee2aaSAndroid Build Coastguard Worker     auto quadData = [&](auto dst) {
277*c8dee2aaSAndroid Build Coastguard Worker         return SkMakeZip(dst,
278*c8dee2aaSAndroid Build Coastguard Worker                          glyphs.subspan(offset, count),
279*c8dee2aaSAndroid Build Coastguard Worker                          fLeftTop.subspan(offset, count));
280*c8dee2aaSAndroid Build Coastguard Worker     };
281*c8dee2aaSAndroid Build Coastguard Worker 
282*c8dee2aaSAndroid Build Coastguard Worker     // Handle direct mask drawing specifically.
283*c8dee2aaSAndroid Build Coastguard Worker     if (fCanDrawDirect) {
284*c8dee2aaSAndroid Build Coastguard Worker         auto [noTransformNeeded, originOffset] =
285*c8dee2aaSAndroid Build Coastguard Worker                 can_use_direct(fCreationMatrix, positionMatrix);
286*c8dee2aaSAndroid Build Coastguard Worker 
287*c8dee2aaSAndroid Build Coastguard Worker         if (noTransformNeeded) {
288*c8dee2aaSAndroid Build Coastguard Worker             if (clip.isEmpty()) {
289*c8dee2aaSAndroid Build Coastguard Worker                 if (fMaskType != MaskFormat::kARGB) {
290*c8dee2aaSAndroid Build Coastguard Worker                     using Quad = Mask2DVertex[4];
291*c8dee2aaSAndroid Build Coastguard Worker                     SkASSERT(sizeof(Mask2DVertex) == this->vertexStride(SkMatrix::I()));
292*c8dee2aaSAndroid Build Coastguard Worker                     fillDirectNoClipping(quadData((Quad*)vertexBuffer), color, originOffset);
293*c8dee2aaSAndroid Build Coastguard Worker                 } else {
294*c8dee2aaSAndroid Build Coastguard Worker                     using Quad = ARGB2DVertex[4];
295*c8dee2aaSAndroid Build Coastguard Worker                     SkASSERT(sizeof(ARGB2DVertex) == this->vertexStride(SkMatrix::I()));
296*c8dee2aaSAndroid Build Coastguard Worker                     fillDirectClipped(quadData((Quad*)vertexBuffer), color, originOffset);
297*c8dee2aaSAndroid Build Coastguard Worker                 }
298*c8dee2aaSAndroid Build Coastguard Worker             } else {
299*c8dee2aaSAndroid Build Coastguard Worker                 if (fMaskType != MaskFormat::kARGB) {
300*c8dee2aaSAndroid Build Coastguard Worker                     using Quad = Mask2DVertex[4];
301*c8dee2aaSAndroid Build Coastguard Worker                     SkASSERT(sizeof(Mask2DVertex) == this->vertexStride(SkMatrix::I()));
302*c8dee2aaSAndroid Build Coastguard Worker                     fillDirectClipped(quadData((Quad*)vertexBuffer), color, originOffset, &clip);
303*c8dee2aaSAndroid Build Coastguard Worker                 } else {
304*c8dee2aaSAndroid Build Coastguard Worker                     using Quad = ARGB2DVertex[4];
305*c8dee2aaSAndroid Build Coastguard Worker                     SkASSERT(sizeof(ARGB2DVertex) == this->vertexStride(SkMatrix::I()));
306*c8dee2aaSAndroid Build Coastguard Worker                     fillDirectClipped(quadData((Quad*)vertexBuffer), color, originOffset, &clip);
307*c8dee2aaSAndroid Build Coastguard Worker                 }
308*c8dee2aaSAndroid Build Coastguard Worker             }
309*c8dee2aaSAndroid Build Coastguard Worker             return;
310*c8dee2aaSAndroid Build Coastguard Worker         }
311*c8dee2aaSAndroid Build Coastguard Worker     }
312*c8dee2aaSAndroid Build Coastguard Worker 
313*c8dee2aaSAndroid Build Coastguard Worker     // Handle the general transformed case.
314*c8dee2aaSAndroid Build Coastguard Worker     SkMatrix viewDifference = this->viewDifference(positionMatrix);
315*c8dee2aaSAndroid Build Coastguard Worker     if (!positionMatrix.hasPerspective()) {
316*c8dee2aaSAndroid Build Coastguard Worker         if (fMaskType == MaskFormat::kARGB) {
317*c8dee2aaSAndroid Build Coastguard Worker             using Quad = ARGB2DVertex[4];
318*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(sizeof(ARGB2DVertex) == this->vertexStride(positionMatrix));
319*c8dee2aaSAndroid Build Coastguard Worker             fill2D(quadData((Quad*)vertexBuffer), color, viewDifference);
320*c8dee2aaSAndroid Build Coastguard Worker         } else {
321*c8dee2aaSAndroid Build Coastguard Worker             using Quad = Mask2DVertex[4];
322*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(sizeof(Mask2DVertex) == this->vertexStride(positionMatrix));
323*c8dee2aaSAndroid Build Coastguard Worker             fill2D(quadData((Quad*)vertexBuffer), color, viewDifference);
324*c8dee2aaSAndroid Build Coastguard Worker         }
325*c8dee2aaSAndroid Build Coastguard Worker     } else {
326*c8dee2aaSAndroid Build Coastguard Worker         if (fMaskType == MaskFormat::kARGB) {
327*c8dee2aaSAndroid Build Coastguard Worker             using Quad = ARGB3DVertex[4];
328*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(sizeof(ARGB3DVertex) == this->vertexStride(positionMatrix));
329*c8dee2aaSAndroid Build Coastguard Worker             fill3D(quadData((Quad*)vertexBuffer), color, viewDifference);
330*c8dee2aaSAndroid Build Coastguard Worker         } else {
331*c8dee2aaSAndroid Build Coastguard Worker             using Quad = Mask3DVertex[4];
332*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(sizeof(Mask3DVertex) == this->vertexStride(positionMatrix));
333*c8dee2aaSAndroid Build Coastguard Worker             fill3D(quadData((Quad*)vertexBuffer), color, viewDifference);
334*c8dee2aaSAndroid Build Coastguard Worker         }
335*c8dee2aaSAndroid Build Coastguard Worker     }
336*c8dee2aaSAndroid Build Coastguard Worker }
337*c8dee2aaSAndroid Build Coastguard Worker 
338*c8dee2aaSAndroid Build Coastguard Worker using AtlasTextOp = skgpu::ganesh::AtlasTextOp;
opMaskType() const339*c8dee2aaSAndroid Build Coastguard Worker AtlasTextOp::MaskType VertexFiller::opMaskType() const {
340*c8dee2aaSAndroid Build Coastguard Worker     switch (fMaskType) {
341*c8dee2aaSAndroid Build Coastguard Worker         case MaskFormat::kA8:   return AtlasTextOp::MaskType::kGrayscaleCoverage;
342*c8dee2aaSAndroid Build Coastguard Worker         case MaskFormat::kA565: return AtlasTextOp::MaskType::kLCDCoverage;
343*c8dee2aaSAndroid Build Coastguard Worker         case MaskFormat::kARGB: return AtlasTextOp::MaskType::kColorBitmap;
344*c8dee2aaSAndroid Build Coastguard Worker     }
345*c8dee2aaSAndroid Build Coastguard Worker     SkUNREACHABLE;
346*c8dee2aaSAndroid Build Coastguard Worker }
347*c8dee2aaSAndroid Build Coastguard Worker #endif  // defined(SK_GANESH) || defined(SK_USE_LEGACY_GANESH_TEXT_APIS)
348*c8dee2aaSAndroid Build Coastguard Worker 
isLCD() const349*c8dee2aaSAndroid Build Coastguard Worker bool VertexFiller::isLCD() const { return fMaskType == MaskFormat::kA565; }
350*c8dee2aaSAndroid Build Coastguard Worker 
351*c8dee2aaSAndroid Build Coastguard Worker // Return true if the positionMatrix represents an integer translation. Return the device
352*c8dee2aaSAndroid Build Coastguard Worker // bounding box of all the glyphs. If the bounding box is empty, then something went singular
353*c8dee2aaSAndroid Build Coastguard Worker // and this operation should be dropped.
deviceRectAndCheckTransform(const SkMatrix & positionMatrix) const354*c8dee2aaSAndroid Build Coastguard Worker std::tuple<bool, SkRect> VertexFiller::deviceRectAndCheckTransform(
355*c8dee2aaSAndroid Build Coastguard Worker             const SkMatrix &positionMatrix) const {
356*c8dee2aaSAndroid Build Coastguard Worker     if (fCanDrawDirect) {
357*c8dee2aaSAndroid Build Coastguard Worker         const auto [directDrawCompatible, offset] =
358*c8dee2aaSAndroid Build Coastguard Worker                 can_use_direct(fCreationMatrix, positionMatrix);
359*c8dee2aaSAndroid Build Coastguard Worker 
360*c8dee2aaSAndroid Build Coastguard Worker         if (directDrawCompatible) {
361*c8dee2aaSAndroid Build Coastguard Worker             return {true, fCreationBounds.makeOffset(offset)};
362*c8dee2aaSAndroid Build Coastguard Worker         }
363*c8dee2aaSAndroid Build Coastguard Worker     }
364*c8dee2aaSAndroid Build Coastguard Worker 
365*c8dee2aaSAndroid Build Coastguard Worker     if (SkMatrix inverse; fCreationMatrix.invert(&inverse)) {
366*c8dee2aaSAndroid Build Coastguard Worker         SkMatrix viewDifference = SkMatrix::Concat(positionMatrix, inverse);
367*c8dee2aaSAndroid Build Coastguard Worker         return {false, viewDifference.mapRect(fCreationBounds)};
368*c8dee2aaSAndroid Build Coastguard Worker     }
369*c8dee2aaSAndroid Build Coastguard Worker 
370*c8dee2aaSAndroid Build Coastguard Worker     // initialPositionMatrix is singular. Do nothing.
371*c8dee2aaSAndroid Build Coastguard Worker     return {false, SkRect::MakeEmpty()};
372*c8dee2aaSAndroid Build Coastguard Worker }
373*c8dee2aaSAndroid Build Coastguard Worker 
374*c8dee2aaSAndroid Build Coastguard Worker }  // namespace sktext::gpu
375