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.ColorInt;
20 import android.annotation.ColorLong;
21 import android.annotation.IntDef;
22 import android.annotation.NonNull;
23 
24 import libcore.util.NativeAllocationRegistry;
25 
26 import java.nio.Buffer;
27 import java.nio.ShortBuffer;
28 
29 /**
30  * Class representing a mesh object.
31  *
32  * This class represents a Mesh object that can optionally be indexed.
33  * A {@link MeshSpecification} is required along with various attributes for
34  * detailing the mesh object, including a mode, vertex buffer, optional index buffer, and bounds
35  * for the mesh. Once generated, a mesh object can be drawn through
36  * {@link Canvas#drawMesh(Mesh, BlendMode, Paint)}
37  */
38 public class Mesh {
39     private long mNativeMeshWrapper;
40     private boolean mIsIndexed;
41 
42     /**
43      * Determines how the mesh is represented and will be drawn.
44      */
45     @IntDef({TRIANGLES, TRIANGLE_STRIP})
46     private @interface Mode {}
47 
48     /**
49      * The mesh will be drawn with triangles without utilizing shared vertices.
50      */
51     public static final int TRIANGLES = 0;
52 
53     /**
54      * The mesh will be drawn with triangles utilizing shared vertices.
55      */
56     public static final int TRIANGLE_STRIP = 1;
57 
58     private static class MeshHolder {
59         public static final NativeAllocationRegistry MESH_SPECIFICATION_REGISTRY =
60                 NativeAllocationRegistry.createMalloced(
61                         MeshSpecification.class.getClassLoader(), nativeGetFinalizer());
62     }
63 
64     /**
65      * Constructor for a non-indexed Mesh.
66      *
67      * @param meshSpec     {@link MeshSpecification} used when generating the mesh.
68      * @param mode         Determines what mode to draw the mesh in. Must be one of
69      *                     {@link Mesh#TRIANGLES} or {@link Mesh#TRIANGLE_STRIP}
70      * @param vertexBuffer vertex buffer representing through {@link Buffer}. This provides the data
71      *                     for all attributes provided within the meshSpec for every vertex. That
72      *                     is, a vertex buffer should be (attributes size * number of vertices) in
73      *                     length to be valid. Note that currently implementation will have a CPU
74      *                     backed buffer generated.
75      * @param vertexCount  the number of vertices represented in the vertexBuffer and mesh.
76      * @param bounds       bounds of the mesh object.
77      */
Mesh(@onNull MeshSpecification meshSpec, @Mode int mode, @NonNull Buffer vertexBuffer, int vertexCount, @NonNull RectF bounds)78     public Mesh(@NonNull MeshSpecification meshSpec, @Mode int mode,
79             @NonNull Buffer vertexBuffer, int vertexCount, @NonNull RectF bounds) {
80         if (mode != TRIANGLES && mode != TRIANGLE_STRIP) {
81             throw new IllegalArgumentException("Invalid value passed in for mode parameter");
82         }
83         long nativeMesh = nativeMake(meshSpec.mNativeMeshSpec, mode, vertexBuffer,
84                 vertexBuffer.isDirect(), vertexCount, vertexBuffer.position(), bounds.left,
85                 bounds.top, bounds.right, bounds.bottom);
86         if (nativeMesh == 0) {
87             throw new IllegalArgumentException("Mesh construction failed.");
88         }
89 
90         meshSetup(nativeMesh, false);
91     }
92 
93     /**
94      * Constructor for an indexed Mesh.
95      *
96      * @param meshSpec     {@link MeshSpecification} used when generating the mesh.
97      * @param mode         Determines what mode to draw the mesh in. Must be one of
98      *                     {@link Mesh#TRIANGLES} or {@link Mesh#TRIANGLE_STRIP}
99      * @param vertexBuffer vertex buffer representing through {@link Buffer}. This provides the data
100      *                     for all attributes provided within the meshSpec for every vertex. That
101      *                     is, a vertex buffer should be (attributes size * number of vertices) in
102      *                     length to be valid. Note that currently implementation will have a CPU
103      *                     backed buffer generated.
104      * @param vertexCount  the number of vertices represented in the vertexBuffer and mesh.
105      * @param indexBuffer  index buffer representing through {@link ShortBuffer}. Indices are
106      *                     required to be 16 bits, so ShortBuffer is necessary. Note that
107      *                     currently implementation will have a CPU
108      *                     backed buffer generated.
109      * @param bounds       bounds of the mesh object.
110      */
Mesh(@onNull MeshSpecification meshSpec, @Mode int mode, @NonNull Buffer vertexBuffer, int vertexCount, @NonNull ShortBuffer indexBuffer, @NonNull RectF bounds)111     public Mesh(@NonNull MeshSpecification meshSpec, @Mode int mode,
112             @NonNull Buffer vertexBuffer, int vertexCount, @NonNull ShortBuffer indexBuffer,
113             @NonNull RectF bounds) {
114         if (mode != TRIANGLES && mode != TRIANGLE_STRIP) {
115             throw new IllegalArgumentException("Invalid value passed in for mode parameter");
116         }
117         long nativeMesh = nativeMakeIndexed(meshSpec.mNativeMeshSpec, mode, vertexBuffer,
118                 vertexBuffer.isDirect(), vertexCount, vertexBuffer.position(), indexBuffer,
119                 indexBuffer.isDirect(), indexBuffer.capacity(), indexBuffer.position(), bounds.left,
120                 bounds.top, bounds.right, bounds.bottom);
121         if (nativeMesh == 0) {
122             throw new IllegalArgumentException("Mesh construction failed.");
123         }
124 
125         meshSetup(nativeMesh, true);
126     }
127 
128     /**
129      * Sets the uniform color value corresponding to the shader assigned to the mesh. If the shader
130      * does not have a uniform with that name or if the uniform is declared with a type other than
131      * vec3 or vec4 and corresponding layout(color) annotation then an IllegalArgumentExcepton is
132      * thrown.
133      *
134      * @param uniformName name matching the color uniform declared in the shader program.
135      * @param color       the provided sRGB color will be converted into the shader program's output
136      *                    colorspace and be available as a vec4 uniform in the program.
137      */
setColorUniform(@onNull String uniformName, @ColorInt int color)138     public void setColorUniform(@NonNull String uniformName, @ColorInt int color) {
139         setUniform(uniformName, Color.valueOf(color).getComponents(), true);
140     }
141 
142     /**
143      * Sets the uniform color value corresponding to the shader assigned to the mesh. If the shader
144      * does not have a uniform with that name or if the uniform is declared with a type other than
145      * vec3 or vec4 and corresponding layout(color) annotation then an IllegalArgumentExcepton is
146      * thrown.
147      *
148      * @param uniformName name matching the color uniform declared in the shader program.
149      * @param color       the provided sRGB color will be converted into the shader program's output
150      *                    colorspace and be available as a vec4 uniform in the program.
151      */
setColorUniform(@onNull String uniformName, @ColorLong long color)152     public void setColorUniform(@NonNull String uniformName, @ColorLong long color) {
153         Color exSRGB = Color.valueOf(color).convert(ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB));
154         setUniform(uniformName, exSRGB.getComponents(), true);
155     }
156 
157     /**
158      * Sets the uniform color value corresponding to the shader assigned to the mesh. If the shader
159      * does not have a uniform with that name or if the uniform is declared with a type other than
160      * vec3 or vec4 and corresponding layout(color) annotation then an IllegalArgumentExcepton is
161      * thrown.
162      *
163      * @param uniformName name matching the color uniform declared in the shader program.
164      * @param color       the provided sRGB color will be converted into the shader program's output
165      *                    colorspace and will be made available as a vec4 uniform in the program.
166      */
setColorUniform(@onNull String uniformName, @NonNull Color color)167     public void setColorUniform(@NonNull String uniformName, @NonNull Color color) {
168         if (color == null) {
169             throw new NullPointerException("The color parameter must not be null");
170         }
171 
172         Color exSRGB = color.convert(ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB));
173         setUniform(uniformName, exSRGB.getComponents(), true);
174     }
175 
176     /**
177      * Sets the uniform value corresponding to the shader assigned to the mesh. If the shader does
178      * not have a uniform with that name or if the uniform is declared with a type other than a
179      * float or float[1] then an IllegalArgumentException is thrown.
180      *
181      * @param uniformName name matching the float uniform declared in the shader program.
182      * @param value       float value corresponding to the float uniform with the given name.
183      */
setFloatUniform(@onNull String uniformName, float value)184     public void setFloatUniform(@NonNull String uniformName, float value) {
185         setFloatUniform(uniformName, value, 0.0f, 0.0f, 0.0f, 1);
186     }
187 
188     /**
189      * Sets the uniform value corresponding to the shader assigned to the mesh. If the shader does
190      * not have a uniform with that name or if the uniform is declared with a type other than a
191      * vec2 or float[2] then an IllegalArgumentException is thrown.
192      *
193      * @param uniformName name matching the float uniform declared in the shader program.
194      * @param value1      first float value corresponding to the float uniform with the given name.
195      * @param value2      second float value corresponding to the float uniform with the given name.
196      */
setFloatUniform(@onNull String uniformName, float value1, float value2)197     public void setFloatUniform(@NonNull String uniformName, float value1, float value2) {
198         setFloatUniform(uniformName, value1, value2, 0.0f, 0.0f, 2);
199     }
200 
201     /**
202      * Sets the uniform value corresponding to the shader assigned to the mesh. If the shader does
203      * not have a uniform with that name or if the uniform is declared with a type other than a
204      * vec3 or float[3] then an IllegalArgumentException is thrown.
205      *
206      * @param uniformName name matching the float uniform declared in the shader program.
207      * @param value1      first float value corresponding to the float uniform with the given name.
208      * @param value2      second float value corresponding to the float uniform with the given name.
209      * @param value3      third float value corresponding to the float unifiform with the given
210      *                    name.
211      */
setFloatUniform( @onNull String uniformName, float value1, float value2, float value3)212     public void setFloatUniform(
213             @NonNull String uniformName, float value1, float value2, float value3) {
214         setFloatUniform(uniformName, value1, value2, value3, 0.0f, 3);
215     }
216 
217     /**
218      * Sets the uniform value corresponding to the shader assigned to the mesh. If the shader does
219      * not have a uniform with that name or if the uniform is declared with a type other than a
220      * vec4 or float[4] then an IllegalArgumentException is thrown.
221      *
222      * @param uniformName name matching the float uniform declared in the shader program.
223      * @param value1      first float value corresponding to the float uniform with the given name.
224      * @param value2      second float value corresponding to the float uniform with the given name.
225      * @param value3      third float value corresponding to the float uniform with the given name.
226      * @param value4      fourth float value corresponding to the float uniform with the given name.
227      */
setFloatUniform( @onNull String uniformName, float value1, float value2, float value3, float value4)228     public void setFloatUniform(
229             @NonNull String uniformName, float value1, float value2, float value3, float value4) {
230         setFloatUniform(uniformName, value1, value2, value3, value4, 4);
231     }
232 
233     /**
234      * Sets the uniform value corresponding to the shader assigned to the mesh. If the shader does
235      * not have a uniform with that name or if the uniform is declared with a type other than a
236      * float (for N=1), vecN, or float[N], where N is the length of the values param, then an
237      * IllegalArgumentException is thrown.
238      *
239      * @param uniformName name matching the float uniform declared in the shader program.
240      * @param values      float value corresponding to the vec4 float uniform with the given name.
241      */
setFloatUniform(@onNull String uniformName, @NonNull float[] values)242     public void setFloatUniform(@NonNull String uniformName, @NonNull float[] values) {
243         setUniform(uniformName, values, false);
244     }
245 
setFloatUniform( String uniformName, float value1, float value2, float value3, float value4, int count)246     private void setFloatUniform(
247             String uniformName, float value1, float value2, float value3, float value4, int count) {
248         if (uniformName == null) {
249             throw new NullPointerException("The uniformName parameter must not be null");
250         }
251         nativeUpdateUniforms(
252                 mNativeMeshWrapper, uniformName, value1, value2, value3, value4, count);
253     }
254 
setUniform(String uniformName, float[] values, boolean isColor)255     private void setUniform(String uniformName, float[] values, boolean isColor) {
256         if (uniformName == null) {
257             throw new NullPointerException("The uniformName parameter must not be null");
258         }
259         if (values == null) {
260             throw new NullPointerException("The uniform values parameter must not be null");
261         }
262 
263         nativeUpdateUniforms(mNativeMeshWrapper, uniformName, values, isColor);
264     }
265 
266     /**
267      * Sets the uniform value corresponding to the shader assigned to the mesh. If the shader does
268      * not have a uniform with that name or if the uniform is declared with a type other than int
269      * or int[1] then an IllegalArgumentException is thrown.
270      *
271      * @param uniformName name matching the int uniform delcared in the shader program.
272      * @param value       value corresponding to the int uniform with the given name.
273      */
setIntUniform(@onNull String uniformName, int value)274     public void setIntUniform(@NonNull String uniformName, int value) {
275         setIntUniform(uniformName, value, 0, 0, 0, 1);
276     }
277 
278     /**
279      * Sets the uniform value corresponding to the shader assigned to the mesh. If the shader does
280      * not have a uniform with that name or if the uniform is declared with a type other than ivec2
281      * or int[2] then an IllegalArgumentException is thrown.
282      *
283      * @param uniformName name matching the int uniform delcared in the shader program.
284      * @param value1      first value corresponding to the int uniform with the given name.
285      * @param value2      second value corresponding to the int uniform with the given name.
286      */
setIntUniform(@onNull String uniformName, int value1, int value2)287     public void setIntUniform(@NonNull String uniformName, int value1, int value2) {
288         setIntUniform(uniformName, value1, value2, 0, 0, 2);
289     }
290 
291     /**
292      * Sets the uniform value corresponding to the shader assigned to the mesh. If the shader does
293      * not have a uniform with that name or if the uniform is declared with a type other than ivec3
294      * or int[3] then an IllegalArgumentException is thrown.
295      *
296      * @param uniformName name matching the int uniform delcared in the shader program.
297      * @param value1      first value corresponding to the int uniform with the given name.
298      * @param value2      second value corresponding to the int uniform with the given name.
299      * @param value3      third value corresponding to the int uniform with the given name.
300      */
setIntUniform(@onNull String uniformName, int value1, int value2, int value3)301     public void setIntUniform(@NonNull String uniformName, int value1, int value2, int value3) {
302         setIntUniform(uniformName, value1, value2, value3, 0, 3);
303     }
304 
305     /**
306      * Sets the uniform value corresponding to the shader assigned to the mesh. If the shader does
307      * not have a uniform with that name or if the uniform is declared with a type other than ivec4
308      * or int[4] then an IllegalArgumentException is thrown.
309      *
310      * @param uniformName name matching the int uniform delcared in the shader program.
311      * @param value1      first value corresponding to the int uniform with the given name.
312      * @param value2      second value corresponding to the int uniform with the given name.
313      * @param value3      third value corresponding to the int uniform with the given name.
314      * @param value4      fourth value corresponding to the int uniform with the given name.
315      */
setIntUniform( @onNull String uniformName, int value1, int value2, int value3, int value4)316     public void setIntUniform(
317             @NonNull String uniformName, int value1, int value2, int value3, int value4) {
318         setIntUniform(uniformName, value1, value2, value3, value4, 4);
319     }
320 
321     /**
322      * Sets the uniform value corresponding to the shader assigned to the mesh. If the shader does
323      * not have a uniform with that name or if the uniform is declared with a type other than an
324      * int (for N=1), ivecN, or int[N], where N is the length of the values param, then an
325      * IllegalArgumentException is thrown.
326      *
327      * @param uniformName name matching the int uniform delcared in the shader program.
328      * @param values      int values corresponding to the vec4 int uniform with the given name.
329      */
setIntUniform(@onNull String uniformName, @NonNull int[] values)330     public void setIntUniform(@NonNull String uniformName, @NonNull int[] values) {
331         if (uniformName == null) {
332             throw new NullPointerException("The uniformName parameter must not be null");
333         }
334         if (values == null) {
335             throw new NullPointerException("The uniform values parameter must not be null");
336         }
337         nativeUpdateUniforms(mNativeMeshWrapper, uniformName, values);
338     }
339 
340     /**
341      * @hide so only calls from module can utilize it
342      */
getNativeWrapperInstance()343     long getNativeWrapperInstance() {
344         return mNativeMeshWrapper;
345     }
346 
setIntUniform( String uniformName, int value1, int value2, int value3, int value4, int count)347     private void setIntUniform(
348             String uniformName, int value1, int value2, int value3, int value4, int count) {
349         if (uniformName == null) {
350             throw new NullPointerException("The uniformName parameter must not be null");
351         }
352 
353         nativeUpdateUniforms(
354                 mNativeMeshWrapper, uniformName, value1, value2, value3, value4, count);
355     }
356 
meshSetup(long nativeMeshWrapper, boolean isIndexed)357     private void meshSetup(long nativeMeshWrapper, boolean isIndexed) {
358         mNativeMeshWrapper = nativeMeshWrapper;
359         this.mIsIndexed = isIndexed;
360         MeshHolder.MESH_SPECIFICATION_REGISTRY.registerNativeAllocation(this, mNativeMeshWrapper);
361     }
362 
nativeGetFinalizer()363     private static native long nativeGetFinalizer();
364 
nativeMake(long meshSpec, int mode, Buffer vertexBuffer, boolean isDirect, int vertexCount, int vertexOffset, float left, float top, float right, float bottom)365     private static native long nativeMake(long meshSpec, int mode, Buffer vertexBuffer,
366             boolean isDirect, int vertexCount, int vertexOffset, float left, float top, float right,
367             float bottom);
368 
nativeMakeIndexed(long meshSpec, int mode, Buffer vertexBuffer, boolean isVertexDirect, int vertexCount, int vertexOffset, ShortBuffer indexBuffer, boolean isIndexDirect, int indexCount, int indexOffset, float left, float top, float right, float bottom)369     private static native long nativeMakeIndexed(long meshSpec, int mode, Buffer vertexBuffer,
370             boolean isVertexDirect, int vertexCount, int vertexOffset, ShortBuffer indexBuffer,
371             boolean isIndexDirect, int indexCount, int indexOffset, float left, float top,
372             float right, float bottom);
373 
nativeUpdateUniforms(long builder, String uniformName, float value1, float value2, float value3, float value4, int count)374     private static native void nativeUpdateUniforms(long builder, String uniformName, float value1,
375             float value2, float value3, float value4, int count);
376 
nativeUpdateUniforms( long builder, String uniformName, float[] values, boolean isColor)377     private static native void nativeUpdateUniforms(
378             long builder, String uniformName, float[] values, boolean isColor);
379 
nativeUpdateUniforms(long builder, String uniformName, int value1, int value2, int value3, int value4, int count)380     private static native void nativeUpdateUniforms(long builder, String uniformName, int value1,
381             int value2, int value3, int value4, int count);
382 
nativeUpdateUniforms(long builder, String uniformName, int[] values)383     private static native void nativeUpdateUniforms(long builder, String uniformName, int[] values);
384 
385 }
386