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( @uppressLintR) @onNull @izemax = 8) Attribute[] attributes, @IntRange(from = 1, to = 1024) int vertexStride, @SuppressLint(R) @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( @uppressLintR) @onNull @izemax = 8) Attribute[] attributes, @IntRange(from = 1, to = 1024) int vertexStride, @SuppressLint(R) @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( @uppressLintR) @onNull @izemax = 8) Attribute[] attributes, @IntRange(from = 1, to = 1024) int vertexStride, @SuppressLint(R) @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