| /* |
| * Copyright (C) 2022 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.graphics; |
| |
| import android.annotation.ColorInt; |
| import android.annotation.ColorLong; |
| import android.annotation.IntDef; |
| import android.annotation.NonNull; |
| |
| import libcore.util.NativeAllocationRegistry; |
| |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.RetentionPolicy; |
| import java.nio.Buffer; |
| import java.nio.ShortBuffer; |
| |
| /** |
| * Class representing a mesh object. |
| * |
| * This class represents a Mesh object that can optionally be indexed. |
| * A {@link MeshSpecification} is required along with various attributes for |
| * detailing the mesh object, including a mode, vertex buffer, optional index buffer, and bounds |
| * for the mesh. Once generated, a mesh object can be drawn through |
| * {@link Canvas#drawMesh(Mesh, BlendMode, Paint)} |
| */ |
| public class Mesh { |
| private long mNativeMeshWrapper; |
| private boolean mIsIndexed; |
| |
| /** |
| * Determines how the mesh is represented and will be drawn. |
| */ |
| @IntDef({TRIANGLES, TRIANGLE_STRIP}) |
| @Retention(RetentionPolicy.SOURCE) |
| private @interface Mode {} |
| |
| /** |
| * The mesh will be drawn with triangles without utilizing shared vertices. |
| */ |
| public static final int TRIANGLES = 0; |
| |
| /** |
| * The mesh will be drawn with triangles utilizing shared vertices. |
| */ |
| public static final int TRIANGLE_STRIP = 1; |
| |
| private static class MeshHolder { |
| public static final NativeAllocationRegistry MESH_SPECIFICATION_REGISTRY = |
| NativeAllocationRegistry.createMalloced( |
| MeshSpecification.class.getClassLoader(), nativeGetFinalizer()); |
| } |
| |
| /** |
| * Constructor for a non-indexed Mesh. |
| * |
| * @param meshSpec {@link MeshSpecification} used when generating the mesh. |
| * @param mode Determines what mode to draw the mesh in. Must be one of |
| * {@link Mesh#TRIANGLES} or {@link Mesh#TRIANGLE_STRIP} |
| * @param vertexBuffer vertex buffer representing through {@link Buffer}. This provides the data |
| * for all attributes provided within the meshSpec for every vertex. That |
| * is, a vertex buffer should be (attributes size * number of vertices) in |
| * length to be valid. Note that currently implementation will have a CPU |
| * backed buffer generated. |
| * @param vertexCount the number of vertices represented in the vertexBuffer and mesh. |
| * @param bounds bounds of the mesh object. |
| */ |
| public Mesh(@NonNull MeshSpecification meshSpec, @Mode int mode, |
| @NonNull Buffer vertexBuffer, int vertexCount, @NonNull RectF bounds) { |
| if (mode != TRIANGLES && mode != TRIANGLE_STRIP) { |
| throw new IllegalArgumentException("Invalid value passed in for mode parameter"); |
| } |
| long nativeMesh = nativeMake(meshSpec.mNativeMeshSpec, mode, vertexBuffer, |
| vertexBuffer.isDirect(), vertexCount, vertexBuffer.position(), bounds.left, |
| bounds.top, bounds.right, bounds.bottom); |
| if (nativeMesh == 0) { |
| throw new IllegalArgumentException("Mesh construction failed."); |
| } |
| |
| meshSetup(nativeMesh, false); |
| } |
| |
| /** |
| * Constructor for an indexed Mesh. |
| * |
| * @param meshSpec {@link MeshSpecification} used when generating the mesh. |
| * @param mode Determines what mode to draw the mesh in. Must be one of |
| * {@link Mesh#TRIANGLES} or {@link Mesh#TRIANGLE_STRIP} |
| * @param vertexBuffer vertex buffer representing through {@link Buffer}. This provides the data |
| * for all attributes provided within the meshSpec for every vertex. That |
| * is, a vertex buffer should be (attributes size * number of vertices) in |
| * length to be valid. Note that currently implementation will have a CPU |
| * backed buffer generated. |
| * @param vertexCount the number of vertices represented in the vertexBuffer and mesh. |
| * @param indexBuffer index buffer representing through {@link ShortBuffer}. Indices are |
| * required to be 16 bits, so ShortBuffer is necessary. Note that |
| * currently implementation will have a CPU |
| * backed buffer generated. |
| * @param bounds bounds of the mesh object. |
| */ |
| public Mesh(@NonNull MeshSpecification meshSpec, @Mode int mode, |
| @NonNull Buffer vertexBuffer, int vertexCount, @NonNull ShortBuffer indexBuffer, |
| @NonNull RectF bounds) { |
| if (mode != TRIANGLES && mode != TRIANGLE_STRIP) { |
| throw new IllegalArgumentException("Invalid value passed in for mode parameter"); |
| } |
| long nativeMesh = nativeMakeIndexed(meshSpec.mNativeMeshSpec, mode, vertexBuffer, |
| vertexBuffer.isDirect(), vertexCount, vertexBuffer.position(), indexBuffer, |
| indexBuffer.isDirect(), indexBuffer.capacity(), indexBuffer.position(), bounds.left, |
| bounds.top, bounds.right, bounds.bottom); |
| if (nativeMesh == 0) { |
| throw new IllegalArgumentException("Mesh construction failed."); |
| } |
| |
| meshSetup(nativeMesh, true); |
| } |
| |
| /** |
| * Sets the uniform color value corresponding to the shader assigned to the mesh. If the shader |
| * does not have a uniform with that name or if the uniform is declared with a type other than |
| * vec3 or vec4 and corresponding layout(color) annotation then an IllegalArgumentExcepton is |
| * thrown. |
| * |
| * @param uniformName name matching the color uniform declared in the shader program. |
| * @param color the provided sRGB color will be converted into the shader program's output |
| * colorspace and be available as a vec4 uniform in the program. |
| */ |
| public void setColorUniform(@NonNull String uniformName, @ColorInt int color) { |
| setUniform(uniformName, Color.valueOf(color).getComponents(), true); |
| } |
| |
| /** |
| * Sets the uniform color value corresponding to the shader assigned to the mesh. If the shader |
| * does not have a uniform with that name or if the uniform is declared with a type other than |
| * vec3 or vec4 and corresponding layout(color) annotation then an IllegalArgumentExcepton is |
| * thrown. |
| * |
| * @param uniformName name matching the color uniform declared in the shader program. |
| * @param color the provided sRGB color will be converted into the shader program's output |
| * colorspace and be available as a vec4 uniform in the program. |
| */ |
| public void setColorUniform(@NonNull String uniformName, @ColorLong long color) { |
| Color exSRGB = Color.valueOf(color).convert(ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB)); |
| setUniform(uniformName, exSRGB.getComponents(), true); |
| } |
| |
| /** |
| * Sets the uniform color value corresponding to the shader assigned to the mesh. If the shader |
| * does not have a uniform with that name or if the uniform is declared with a type other than |
| * vec3 or vec4 and corresponding layout(color) annotation then an IllegalArgumentExcepton is |
| * thrown. |
| * |
| * @param uniformName name matching the color uniform declared in the shader program. |
| * @param color the provided sRGB color will be converted into the shader program's output |
| * colorspace and will be made available as a vec4 uniform in the program. |
| */ |
| public void setColorUniform(@NonNull String uniformName, @NonNull Color color) { |
| if (color == null) { |
| throw new NullPointerException("The color parameter must not be null"); |
| } |
| |
| Color exSRGB = color.convert(ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB)); |
| setUniform(uniformName, exSRGB.getComponents(), true); |
| } |
| |
| /** |
| * Sets the uniform value corresponding to the shader assigned to the mesh. If the shader does |
| * not have a uniform with that name or if the uniform is declared with a type other than a |
| * float or float[1] then an IllegalArgumentException is thrown. |
| * |
| * @param uniformName name matching the float uniform declared in the shader program. |
| * @param value float value corresponding to the float uniform with the given name. |
| */ |
| public void setFloatUniform(@NonNull String uniformName, float value) { |
| setFloatUniform(uniformName, value, 0.0f, 0.0f, 0.0f, 1); |
| } |
| |
| /** |
| * Sets the uniform value corresponding to the shader assigned to the mesh. If the shader does |
| * not have a uniform with that name or if the uniform is declared with a type other than a |
| * vec2 or float[2] then an IllegalArgumentException is thrown. |
| * |
| * @param uniformName name matching the float uniform declared in the shader program. |
| * @param value1 first float value corresponding to the float uniform with the given name. |
| * @param value2 second float value corresponding to the float uniform with the given name. |
| */ |
| public void setFloatUniform(@NonNull String uniformName, float value1, float value2) { |
| setFloatUniform(uniformName, value1, value2, 0.0f, 0.0f, 2); |
| } |
| |
| /** |
| * Sets the uniform value corresponding to the shader assigned to the mesh. If the shader does |
| * not have a uniform with that name or if the uniform is declared with a type other than a |
| * vec3 or float[3] then an IllegalArgumentException is thrown. |
| * |
| * @param uniformName name matching the float uniform declared in the shader program. |
| * @param value1 first float value corresponding to the float uniform with the given name. |
| * @param value2 second float value corresponding to the float uniform with the given name. |
| * @param value3 third float value corresponding to the float unifiform with the given |
| * name. |
| */ |
| public void setFloatUniform( |
| @NonNull String uniformName, float value1, float value2, float value3) { |
| setFloatUniform(uniformName, value1, value2, value3, 0.0f, 3); |
| } |
| |
| /** |
| * Sets the uniform value corresponding to the shader assigned to the mesh. If the shader does |
| * not have a uniform with that name or if the uniform is declared with a type other than a |
| * vec4 or float[4] then an IllegalArgumentException is thrown. |
| * |
| * @param uniformName name matching the float uniform declared in the shader program. |
| * @param value1 first float value corresponding to the float uniform with the given name. |
| * @param value2 second float value corresponding to the float uniform with the given name. |
| * @param value3 third float value corresponding to the float uniform with the given name. |
| * @param value4 fourth float value corresponding to the float uniform with the given name. |
| */ |
| public void setFloatUniform( |
| @NonNull String uniformName, float value1, float value2, float value3, float value4) { |
| setFloatUniform(uniformName, value1, value2, value3, value4, 4); |
| } |
| |
| /** |
| * Sets the uniform value corresponding to the shader assigned to the mesh. If the shader does |
| * not have a uniform with that name or if the uniform is declared with a type other than a |
| * float (for N=1), vecN, or float[N], where N is the length of the values param, then an |
| * IllegalArgumentException is thrown. |
| * |
| * @param uniformName name matching the float uniform declared in the shader program. |
| * @param values float value corresponding to the vec4 float uniform with the given name. |
| */ |
| public void setFloatUniform(@NonNull String uniformName, @NonNull float[] values) { |
| setUniform(uniformName, values, false); |
| } |
| |
| private void setFloatUniform( |
| String uniformName, float value1, float value2, float value3, float value4, int count) { |
| if (uniformName == null) { |
| throw new NullPointerException("The uniformName parameter must not be null"); |
| } |
| nativeUpdateUniforms( |
| mNativeMeshWrapper, uniformName, value1, value2, value3, value4, count); |
| } |
| |
| private void setUniform(String uniformName, float[] values, boolean isColor) { |
| if (uniformName == null) { |
| throw new NullPointerException("The uniformName parameter must not be null"); |
| } |
| if (values == null) { |
| throw new NullPointerException("The uniform values parameter must not be null"); |
| } |
| |
| nativeUpdateUniforms(mNativeMeshWrapper, uniformName, values, isColor); |
| } |
| |
| /** |
| * Sets the uniform value corresponding to the shader assigned to the mesh. If the shader does |
| * not have a uniform with that name or if the uniform is declared with a type other than int |
| * or int[1] then an IllegalArgumentException is thrown. |
| * |
| * @param uniformName name matching the int uniform declared in the shader program. |
| * @param value value corresponding to the int uniform with the given name. |
| */ |
| public void setIntUniform(@NonNull String uniformName, int value) { |
| setIntUniform(uniformName, value, 0, 0, 0, 1); |
| } |
| |
| /** |
| * Sets the uniform value corresponding to the shader assigned to the mesh. If the shader does |
| * not have a uniform with that name or if the uniform is declared with a type other than ivec2 |
| * or int[2] then an IllegalArgumentException is thrown. |
| * |
| * @param uniformName name matching the int uniform declared in the shader program. |
| * @param value1 first value corresponding to the int uniform with the given name. |
| * @param value2 second value corresponding to the int uniform with the given name. |
| */ |
| public void setIntUniform(@NonNull String uniformName, int value1, int value2) { |
| setIntUniform(uniformName, value1, value2, 0, 0, 2); |
| } |
| |
| /** |
| * Sets the uniform value corresponding to the shader assigned to the mesh. If the shader does |
| * not have a uniform with that name or if the uniform is declared with a type other than ivec3 |
| * or int[3] then an IllegalArgumentException is thrown. |
| * |
| * @param uniformName name matching the int uniform declared in the shader program. |
| * @param value1 first value corresponding to the int uniform with the given name. |
| * @param value2 second value corresponding to the int uniform with the given name. |
| * @param value3 third value corresponding to the int uniform with the given name. |
| */ |
| public void setIntUniform(@NonNull String uniformName, int value1, int value2, int value3) { |
| setIntUniform(uniformName, value1, value2, value3, 0, 3); |
| } |
| |
| /** |
| * Sets the uniform value corresponding to the shader assigned to the mesh. If the shader does |
| * not have a uniform with that name or if the uniform is declared with a type other than ivec4 |
| * or int[4] then an IllegalArgumentException is thrown. |
| * |
| * @param uniformName name matching the int uniform declared in the shader program. |
| * @param value1 first value corresponding to the int uniform with the given name. |
| * @param value2 second value corresponding to the int uniform with the given name. |
| * @param value3 third value corresponding to the int uniform with the given name. |
| * @param value4 fourth value corresponding to the int uniform with the given name. |
| */ |
| public void setIntUniform( |
| @NonNull String uniformName, int value1, int value2, int value3, int value4) { |
| setIntUniform(uniformName, value1, value2, value3, value4, 4); |
| } |
| |
| /** |
| * Sets the uniform value corresponding to the shader assigned to the mesh. If the shader does |
| * not have a uniform with that name or if the uniform is declared with a type other than an |
| * int (for N=1), ivecN, or int[N], where N is the length of the values param, then an |
| * IllegalArgumentException is thrown. |
| * |
| * @param uniformName name matching the int uniform declared in the shader program. |
| * @param values int values corresponding to the vec4 int uniform with the given name. |
| */ |
| public void setIntUniform(@NonNull String uniformName, @NonNull int[] values) { |
| if (uniformName == null) { |
| throw new NullPointerException("The uniformName parameter must not be null"); |
| } |
| if (values == null) { |
| throw new NullPointerException("The uniform values parameter must not be null"); |
| } |
| nativeUpdateUniforms(mNativeMeshWrapper, uniformName, values); |
| } |
| |
| /** |
| * @hide so only calls from module can utilize it |
| */ |
| long getNativeWrapperInstance() { |
| return mNativeMeshWrapper; |
| } |
| |
| private void setIntUniform( |
| String uniformName, int value1, int value2, int value3, int value4, int count) { |
| if (uniformName == null) { |
| throw new NullPointerException("The uniformName parameter must not be null"); |
| } |
| |
| nativeUpdateUniforms( |
| mNativeMeshWrapper, uniformName, value1, value2, value3, value4, count); |
| } |
| |
| private void meshSetup(long nativeMeshWrapper, boolean isIndexed) { |
| mNativeMeshWrapper = nativeMeshWrapper; |
| this.mIsIndexed = isIndexed; |
| MeshHolder.MESH_SPECIFICATION_REGISTRY.registerNativeAllocation(this, mNativeMeshWrapper); |
| } |
| |
| private static native long nativeGetFinalizer(); |
| |
| private static native long nativeMake(long meshSpec, int mode, Buffer vertexBuffer, |
| boolean isDirect, int vertexCount, int vertexOffset, float left, float top, float right, |
| float bottom); |
| |
| private static native long 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); |
| |
| private static native void nativeUpdateUniforms(long builder, String uniformName, float value1, |
| float value2, float value3, float value4, int count); |
| |
| private static native void nativeUpdateUniforms( |
| long builder, String uniformName, float[] values, boolean isColor); |
| |
| private static native void nativeUpdateUniforms(long builder, String uniformName, int value1, |
| int value2, int value3, int value4, int count); |
| |
| private static native void nativeUpdateUniforms(long builder, String uniformName, int[] values); |
| |
| } |