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