1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.graphics;
18 
19 import android.annotation.IntDef;
20 import android.annotation.IntRange;
21 import android.annotation.NonNull;
22 import android.annotation.Size;
23 import android.annotation.SuppressLint;
24 
25 import libcore.util.NativeAllocationRegistry;
26 
27 import java.lang.annotation.Retention;
28 import java.lang.annotation.RetentionPolicy;
29 
30 /**
31  * Class responsible for holding specifications for {@link Mesh} creations. This class generates a
32  * {@link MeshSpecification} via the
33  * {@link MeshSpecification#make(Attribute[], int, Varying[], String, String)} method,
34  * where multiple parameters to set up the mesh are supplied, including attributes, vertex stride,
35  * {@link Varying}, and vertex/fragment shaders. There are also additional methods to provide an
36  * optional {@link ColorSpace} as well as an alpha type.
37  *
38  * For example a vertex shader that leverages a {@link Varying} may look like the following:
39  *
40  * <pre>
41  *        Varyings main(const Attributes attributes) {
42  *             Varyings varyings;
43  *             varyings.position = attributes.position;
44  *             return varyings;
45  *        }
46  * </pre>
47  *
48  * The corresponding fragment shader that may consume the varying look like the following:
49  *
50  * <pre>
51  *      float2 main(const Varyings varyings, out float4 color) {
52  *             color = vec4(1.0, 0.0, 0.0, 1.0);
53  *             return varyings.position;
54  *      }
55  * </pre>
56  *
57  * The color returned from this fragment shader is blended with the other parameters that are
58  * configured on the Paint object (ex. {@link Paint#setBlendMode(BlendMode)} used to draw the mesh.
59  *
60  * The position returned in the fragment shader can be consumed by any following fragment shaders in
61  * the shader chain.
62  *
63  * See https://developer.android.com/develop/ui/views/graphics/agsl for more information
64  * regarding Android Graphics Shader Language.
65  *
66  * Note that there are several limitations on various mesh specifications:
67  * 1. The max amount of attributes allowed is 8.
68  * 2. The offset alignment length is 4 bytes.
69  * 2. The max stride length is 1024.
70  * 3. The max amount of varyings is 6.
71  *
72  * These should be kept in mind when generating a mesh specification, as exceeding them will
73  * lead to errors.
74  */
75 public class MeshSpecification {
76     long mNativeMeshSpec;
77 
78     /**
79      * Constants for {@link #make(Attribute[], int, Varying[], String, String)}
80      * to determine alpha type. Describes how to interpret the alpha component of a pixel.
81      *
82      * @hide
83      */
84     @IntDef(
85         prefix = {"ALPHA_TYPE_"},
86         value = {ALPHA_TYPE_UNKNOWN, ALPHA_TYPE_OPAQUE, ALPHA_TYPE_PREMULTIPLIED,
87                 ALPHA_TYPE_UNPREMULTIPLIED}
88     )
89     @Retention(RetentionPolicy.SOURCE)
90     private @interface AlphaType {}
91 
92     /**
93      * uninitialized.
94      */
95     public static final int ALPHA_TYPE_UNKNOWN = 0;
96 
97     /**
98      * Pixel is opaque.
99      */
100     public static final int ALPHA_TYPE_OPAQUE = 1;
101 
102     /**
103      * Pixel components are premultiplied by alpha.
104      */
105     public static final int ALPHA_TYPE_PREMULTIPLIED = 2;
106 
107     /**
108      * Pixel components are independent of alpha.
109      */
110     public static final int ALPHA_TYPE_UNPREMULTIPLIED = 3;
111 
112     /**
113      * Constants for {@link Attribute} and {@link Varying} for determining the data type.
114      *
115      * @hide
116      */
117     @IntDef(
118         prefix = {"TYPE_"},
119         value = {TYPE_FLOAT, TYPE_FLOAT2, TYPE_FLOAT3, TYPE_FLOAT4, TYPE_UBYTE4}
120     )
121     @Retention(RetentionPolicy.SOURCE)
122     private @interface Type {}
123 
124     /**
125      * Represents one float. Its equivalent shader type is float.
126      */
127     public static final int TYPE_FLOAT = 0;
128 
129     /**
130      * Represents two floats. Its equivalent shader type is float2.
131      */
132     public static final int TYPE_FLOAT2 = 1;
133 
134     /**
135      * Represents three floats. Its equivalent shader type is float3.
136      */
137     public static final int TYPE_FLOAT3 = 2;
138 
139     /**
140      * Represents four floats. Its equivalent shader type is float4.
141      */
142     public static final int TYPE_FLOAT4 = 3;
143 
144     /**
145      * Represents four bytes. Its equivalent shader type is half4.
146      */
147     public static final int TYPE_UBYTE4 = 4;
148 
149     /**
150      * Data class to represent a single attribute in a shader. An attribute is a variable that
151      * accompanies a vertex, this can be a color or texture coordinates.
152      *
153      * See https://developer.android.com/develop/ui/views/graphics/agsl for more information
154      * regarding Android Graphics Shader Language.
155      *
156      * Note that offset is the offset in number of bytes. For example, if we had two attributes
157      *
158      * <pre>
159      * Float3 att1
160      * Float att2
161      * </pre>
162      *
163      * att1 would have an offset of 0, while att2 would have an offset of 12 bytes.
164      *
165      * This is consumed as part of
166      * {@link MeshSpecification#make(Attribute[], int, Varying[], String, String, ColorSpace, int)}
167      * to create a {@link MeshSpecification} instance.
168      */
169     public static class Attribute {
170         @Type
171         private final int mType;
172         private final int mOffset;
173         private final String mName;
174 
Attribute(@ype int type, int offset, @NonNull String name)175         public Attribute(@Type int type, int offset, @NonNull String name) {
176             mType = type;
177             mOffset = offset;
178             mName = name;
179         }
180 
181         /**
182          * Return the corresponding data type for this {@link Attribute}.
183          */
184         @Type
getType()185         public int getType() {
186             return mType;
187         }
188 
189         /**
190          * Return the offset of the attribute in bytes
191          */
getOffset()192         public int getOffset() {
193             return mOffset;
194         }
195 
196         /**
197          * Return the name of this {@link Attribute}
198          */
199         @NonNull
getName()200         public String getName() {
201             return mName;
202         }
203 
204         @Override
toString()205         public String toString() {
206             return "Attribute{"
207                     + "mType=" + mType
208                     + ", mOffset=" + mOffset
209                     + ", mName='" + mName + '\''
210                     + '}';
211         }
212     }
213 
214     /**
215      * Data class to represent a single varying variable. A Varying variable can be altered by the
216      * vertex shader defined on the mesh but not by the fragment shader defined by AGSL.
217      *
218      * See https://developer.android.com/develop/ui/views/graphics/agsl for more information
219      * regarding Android Graphics Shader Language.
220      *
221      * This is consumed as part of
222      * {@link MeshSpecification#make(Attribute[], int, Varying[], String, String, ColorSpace, int)}
223      * to create a {@link MeshSpecification} instance.
224      */
225     public static class Varying {
226         @Type
227         private final int mType;
228         private final String mName;
229 
Varying(@ype int type, @NonNull String name)230         public Varying(@Type int type, @NonNull String name) {
231             mType = type;
232             mName = name;
233         }
234 
235         /**
236          * Return the corresponding data type for this {@link Varying}.
237          */
238         @Type
getType()239         public int getType() {
240             return mType;
241         }
242 
243         /**
244          * Return the name of this {@link Varying}
245          */
246         @NonNull
getName()247         public String getName() {
248             return mName;
249         }
250 
251         @Override
toString()252         public String toString() {
253             return "Varying{"
254                     + "mType=" + mType
255                     + ", mName='" + mName + '\''
256                     + '}';
257         }
258     }
259 
260     private static class MeshSpecificationHolder {
261         public static final NativeAllocationRegistry MESH_SPECIFICATION_REGISTRY =
262                 NativeAllocationRegistry.createMalloced(
263                         MeshSpecification.class.getClassLoader(), nativeGetFinalizer());
264     }
265 
266     /**
267      * Creates a {@link MeshSpecification} object for use within {@link Mesh}. This uses a default
268      * color space of {@link ColorSpace.Named#SRGB} and alphaType of
269      * {@link #ALPHA_TYPE_PREMULTIPLIED}.
270      *
271      * @param attributes     list of attributes represented by {@link Attribute}. Can hold a max of
272      *                       8.
273      * @param vertexStride   length of vertex stride in bytes. This should be the size of a single
274      *                       vertex' attributes. Max of 1024 is accepted.
275      * @param varyings       List of varyings represented by {@link Varying}. Can hold a max of 6.
276      *                       Note that `position` is provided by default, does not need to be
277      *                       provided in the list, and does not count towards
278      *                       the 6 varyings allowed.
279      * @param vertexShader   vertex shader to be supplied to the mesh. Ensure that the position
280      *                       varying is set within the shader to get proper results.
281      *                       See {@link MeshSpecification} for an example vertex shader
282      *                       implementation
283      * @param fragmentShader fragment shader to be supplied to the mesh.
284      *                       See {@link MeshSpecification} for an example fragment shader
285      *                       implementation
286      * @return {@link MeshSpecification} object for use when creating {@link Mesh}
287      */
288     @NonNull
make( @uppressLint"ArrayReturn") @onNull @izemax = 8) Attribute[] attributes, @IntRange(from = 1, to = 1024) int vertexStride, @SuppressLint("ArrayReturn") @NonNull @Size(max = 6) Varying[] varyings, @NonNull String vertexShader, @NonNull String fragmentShader)289     public static MeshSpecification make(
290             @SuppressLint("ArrayReturn") @NonNull @Size(max = 8) Attribute[] attributes,
291             @IntRange(from = 1, to = 1024) int vertexStride,
292             @SuppressLint("ArrayReturn") @NonNull @Size(max = 6) Varying[] varyings,
293             @NonNull String vertexShader,
294             @NonNull String fragmentShader) {
295         long nativeMeshSpec = nativeMake(attributes,
296                 vertexStride, varyings, vertexShader,
297                 fragmentShader);
298         if (nativeMeshSpec == 0) {
299             throw new IllegalArgumentException("MeshSpecification construction failed");
300         }
301         return new MeshSpecification(nativeMeshSpec);
302     }
303 
304     /**
305      * Creates a {@link MeshSpecification} object.  This uses a default alphaType of
306      * {@link #ALPHA_TYPE_PREMULTIPLIED}.
307      *
308      * @param attributes     list of attributes represented by {@link Attribute}. Can hold a max of
309      *                       8.
310      * @param vertexStride   length of vertex stride in bytes. This should be the size of a single
311      *                       vertex' attributes. Max of 1024 is accepted.
312      * @param varyings       List of varyings represented by {@link Varying}. Can hold a max of 6.
313      *                       Note that `position` is provided by default, does not need to be
314      *                       provided in the list, and does not count towards
315      *                       the 6 varyings allowed.
316      * @param vertexShader   vertex shader to be supplied to the mesh. Ensure that the position
317      *                       varying is set within the shader to get proper results.
318      *                       See {@link MeshSpecification} for an example vertex shader
319      *                       implementation
320      * @param fragmentShader fragment shader to be supplied to the mesh.
321      *                       See {@link MeshSpecification} for an example fragment shader
322      *                       implementation
323      * @param colorSpace     {@link ColorSpace} to tell what color space to work in.
324      * @return {@link MeshSpecification} object for use when creating {@link Mesh}
325      */
326     @NonNull
make( @uppressLint"ArrayReturn") @onNull @izemax = 8) Attribute[] attributes, @IntRange(from = 1, to = 1024) int vertexStride, @SuppressLint("ArrayReturn") @NonNull @Size(max = 6) Varying[] varyings, @NonNull String vertexShader, @NonNull String fragmentShader, @NonNull ColorSpace colorSpace )327     public static MeshSpecification make(
328             @SuppressLint("ArrayReturn") @NonNull @Size(max = 8) Attribute[] attributes,
329             @IntRange(from = 1, to = 1024) int vertexStride,
330             @SuppressLint("ArrayReturn") @NonNull @Size(max = 6) Varying[] varyings,
331             @NonNull String vertexShader,
332             @NonNull String fragmentShader,
333             @NonNull ColorSpace colorSpace
334     ) {
335         long nativeMeshSpec = nativeMakeWithCS(attributes,
336                 vertexStride, varyings, vertexShader,
337                 fragmentShader, colorSpace.getNativeInstance());
338         if (nativeMeshSpec == 0) {
339             throw new IllegalArgumentException("MeshSpecification construction failed");
340         }
341         return new MeshSpecification(nativeMeshSpec);
342     }
343 
344     /**
345      * Creates a {@link MeshSpecification} object.
346      *
347      * @param attributes     list of attributes represented by {@link Attribute}. Can hold a max of
348      *                       8.
349      * @param vertexStride   length of vertex stride in bytes. This should be the size of a single
350      *                       vertex' attributes. Max of 1024 is accepted.
351      * @param varyings       List of varyings represented by {@link Varying}. Can hold a max of 6.
352      *                       Note that `position` is provided by default, does not need to be
353      *                       provided in the list, and does not count towards
354      *                       the 6 varyings allowed.
355      * @param vertexShader   vertex shader to be supplied to the mesh. Ensure that the position
356      *                       varying is set within the shader to get proper results.
357      *                       See {@link MeshSpecification} for an example vertex shader
358      *                       implementation
359      * @param fragmentShader fragment shader to be supplied to the mesh.
360      *                       See {@link MeshSpecification} for an example fragment shader
361      *                       implementation
362      * @param colorSpace     {@link ColorSpace} to tell what color space to work in.
363      * @param alphaType      Describes how to interpret the alpha component for a pixel. Must be
364      *                       one of
365      *                       {@link MeshSpecification#ALPHA_TYPE_UNKNOWN},
366      *                       {@link MeshSpecification#ALPHA_TYPE_OPAQUE},
367      *                       {@link MeshSpecification#ALPHA_TYPE_PREMULTIPLIED}, or
368      *                       {@link MeshSpecification#ALPHA_TYPE_UNPREMULTIPLIED}
369      * @return {@link MeshSpecification} object for use when creating {@link Mesh}
370      */
371     @NonNull
make( @uppressLint"ArrayReturn") @onNull @izemax = 8) Attribute[] attributes, @IntRange(from = 1, to = 1024) int vertexStride, @SuppressLint("ArrayReturn") @NonNull @Size(max = 6) Varying[] varyings, @NonNull String vertexShader, @NonNull String fragmentShader, @NonNull ColorSpace colorSpace, @AlphaType int alphaType)372     public static MeshSpecification make(
373             @SuppressLint("ArrayReturn") @NonNull @Size(max = 8) Attribute[] attributes,
374             @IntRange(from = 1, to = 1024) int vertexStride,
375             @SuppressLint("ArrayReturn") @NonNull @Size(max = 6) Varying[] varyings,
376             @NonNull String vertexShader,
377             @NonNull String fragmentShader,
378             @NonNull ColorSpace colorSpace,
379             @AlphaType int alphaType) {
380         long nativeMeshSpec =
381                 nativeMakeWithAlpha(attributes, vertexStride, varyings, vertexShader,
382                         fragmentShader, colorSpace.getNativeInstance(), alphaType);
383         if (nativeMeshSpec == 0) {
384             throw new IllegalArgumentException("MeshSpecification construction failed");
385         }
386         return new MeshSpecification(nativeMeshSpec);
387     }
388 
MeshSpecification(long meshSpec)389     private MeshSpecification(long meshSpec) {
390         mNativeMeshSpec = meshSpec;
391         MeshSpecificationHolder.MESH_SPECIFICATION_REGISTRY.registerNativeAllocation(
392                 this, meshSpec);
393     }
394 
nativeGetFinalizer()395     private static native long nativeGetFinalizer();
396 
nativeMake(Attribute[] attributes, int vertexStride, Varying[] varyings, String vertexShader, String fragmentShader)397     private static native long nativeMake(Attribute[] attributes, int vertexStride,
398             Varying[] varyings, String vertexShader, String fragmentShader);
399 
nativeMakeWithCS(Attribute[] attributes, int vertexStride, Varying[] varyings, String vertexShader, String fragmentShader, long colorSpace)400     private static native long nativeMakeWithCS(Attribute[] attributes, int vertexStride,
401             Varying[] varyings, String vertexShader, String fragmentShader, long colorSpace);
402 
nativeMakeWithAlpha(Attribute[] attributes, int vertexStride, Varying[] varyings, String vertexShader, String fragmentShader, long colorSpace, int alphaType)403     private static native long nativeMakeWithAlpha(Attribute[] attributes, int vertexStride,
404             Varying[] varyings, String vertexShader, String fragmentShader, long colorSpace,
405             int alphaType);
406 }
407