Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0e8df402 authored by Angel Aguayo's avatar Angel Aguayo Committed by Android (Google) Code Review
Browse files

Merge "Exposed SkMeshSpecification"

parents b01ee9c3 79e5f58f
Loading
Loading
Loading
Loading
+194 −0
Original line number Diff line number Diff line
/*
 * 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.IntDef;

import libcore.util.NativeAllocationRegistry;

/**
 * Class responsible for holding specifications for {@link Mesh} creations. This class
 * generates a {@link MeshSpecification} via the Make method, where multiple parameters to set up
 * the mesh are supplied, including attributes, vertex stride, varyings, and
 * vertex/fragment shaders. There are also additional methods to provide an optional
 * {@link ColorSpace} as well as an alpha type.
 *
 * Note that there are several limitations on various mesh specifications:
 * 1. The max amount of attributes allowed is 8.
 * 2. The offset alignment length is 4 bytes.
 * 2. The max stride length is 1024.
 * 3. The max amount of varyings is 6.
 *
 * These should be kept in mind when generating a mesh specification, as exceeding them will
 * lead to errors.
 *
 * @hide
 */
public class MeshSpecification {
    private long mNativeMeshSpec;

    /**
     * Constants for {@link #make(Attribute[], int, Varying[], String, String, ColorSpace, int)}
     * to determine alpha type
     */
    @IntDef({UNKNOWN, OPAQUE, PREMUL, UNPREMULT})
    public @interface AlphaType {
    }

    public static final int UNKNOWN = 0;
    public static final int OPAQUE = 1;
    public static final int PREMUL = 2;
    public static final int UNPREMULT = 3;

    /**
     * Constants for {@link Attribute} and {@link Varying} for determining the data type.
     */
    @IntDef({FLOAT, FLOAT2, FLOAT3, FLOAT4, UBYTE4})
    public @interface Type {
    }

    public static final int FLOAT = 0;
    public static final int FLOAT2 = 1;
    public static final int FLOAT3 = 2;
    public static final int FLOAT4 = 3;
    public static final int UBYTE4 = 4;

    /**
     * Data class to represent a single attribute in a shader. Note that type parameter must be
     * one of {@link #FLOAT}, {@link #FLOAT2}, {@link #FLOAT3}, {@link #FLOAT4}, or {@link #UBYTE4}.
     */
    public static class Attribute {
        @Type
        private int mType;
        private int mOffset;
        private String mName;

        public Attribute(@Type int type, int offset, String name) {
            mType = type;
            mOffset = offset;
            mName = name;
        }
    }

    /**
     * Data class to represent a single varying variable. Note that type parameter must be
     * one of {@link #FLOAT}, {@link #FLOAT2}, {@link #FLOAT3}, {@link #FLOAT4}, or {@link #UBYTE4}.
     */
    public static class Varying {
        @Type
        private int mType;
        private String mName;

        public Varying(@Type int type, String name) {
            mType = type;
            mName = name;
        }
    }

    private static class MeshSpecificationHolder {
        public static final NativeAllocationRegistry MESH_SPECIFICATION_REGISTRY =
                NativeAllocationRegistry.createMalloced(
                        MeshSpecification.class.getClassLoader(), nativeGetFinalizer());
    }

    /**
     * Creates a {@link MeshSpecification} object.
     *
     * @param attributes     list of attributes represented by {@link Attribute}. Can hold a max of
     *                       8.
     * @param vertexStride   length of vertex stride. Max of 1024 is accepted.
     * @param varyings       List of varyings represented by {@link Varying}. Can hold a max of 6.
     * @param vertexShader   vertex shader to be supplied to the mesh.
     * @param fragmentShader fragment shader to be suppied to the mesh.
     * @return {@link MeshSpecification} object for use when creating {@link Mesh}
     */
    public static MeshSpecification make(Attribute[] attributes, int vertexStride,
            Varying[] varyings, String vertexShader, String fragmentShader) {
        long nativeMeshSpec =
                nativeMake(attributes, vertexStride, varyings, vertexShader, fragmentShader);
        if (nativeMeshSpec == 0) {
            throw new IllegalArgumentException("MeshSpecification construction failed");
        }
        return new MeshSpecification(nativeMeshSpec);
    }

    /**
     * Creates a {@link MeshSpecification} object.
     *
     * @param attributes     list of attributes represented by {@link Attribute}. Can hold a max of
     *                       8.
     * @param vertexStride   length of vertex stride. Max of 1024 is accepted.
     * @param varyings       List of varyings represented by {@link Varying}. Can hold a max of
     *                       6.
     * @param vertexShader   vertex shader to be supplied to the mesh.
     * @param fragmentShader fragment shader to be supplied to the mesh.
     * @param colorSpace     {@link ColorSpace} to tell what color space to work in.
     * @return {@link MeshSpecification} object for use when creating {@link Mesh}
     */
    public static MeshSpecification make(Attribute[] attributes, int vertexStride,
            Varying[] varyings, String vertexShader, String fragmentShader, ColorSpace colorSpace) {
        long nativeMeshSpec = nativeMakeWithCS(attributes, vertexStride, varyings, vertexShader,
                fragmentShader, colorSpace.getNativeInstance());
        if (nativeMeshSpec == 0) {
            throw new IllegalArgumentException("MeshSpecification construction failed");
        }
        return new MeshSpecification(nativeMeshSpec);
    }

    /**
     * Creates a {@link MeshSpecification} object.
     *
     * @param attributes     list of attributes represented by {@link Attribute}. Can hold a max of
     *                       8.
     * @param vertexStride   length of vertex stride. Max of 1024 is accepted.
     * @param varyings       List of varyings represented by {@link Varying}. Can hold a max of 6.
     * @param vertexShader   vertex shader code to be supplied to the mesh.
     * @param fragmentShader fragment shader code to be suppied to the mesh.
     * @param colorSpace     {@link ColorSpace} to tell what color space to work in.
     * @param alphaType      Describes how to interpret the alpha component for a pixel. Must be
     *                       one of {@link AlphaType} values.
     * @return {@link MeshSpecification} object for use when creating {@link Mesh}
     */
    public static MeshSpecification make(Attribute[] attributes, int vertexStride,
            Varying[] varyings, String vertexShader, String fragmentShader, ColorSpace colorSpace,
            @AlphaType int alphaType) {
        long nativeMeshSpec = nativeMakeWithAlpha(attributes, vertexStride, varyings, vertexShader,
                fragmentShader, colorSpace.getNativeInstance(), alphaType);
        if (nativeMeshSpec == 0) {
            throw new IllegalArgumentException("MeshSpecification construction failed");
        }
        return new MeshSpecification(nativeMeshSpec);
    }

    private MeshSpecification(long meshSpec) {
        mNativeMeshSpec = meshSpec;
        MeshSpecificationHolder.MESH_SPECIFICATION_REGISTRY.registerNativeAllocation(
                this, meshSpec);
    }

    private static native long nativeGetFinalizer();

    private static native long nativeMake(Attribute[] attributes, int vertexStride,
            Varying[] varyings, String vertexShader, String fragmentShader);

    private static native long nativeMakeWithCS(Attribute[] attributes, int vertexStride,
            Varying[] varyings, String vertexShader, String fragmentShader, long colorSpace);

    private static native long nativeMakeWithAlpha(Attribute[] attributes, int vertexStride,
            Varying[] varyings, String vertexShader, String fragmentShader, long colorSpace,
            int alphaType);
}
+156 −0
Original line number Diff line number Diff line
/*
 * 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.
 */

#include <SkMesh.h>

#include "GraphicsJNI.h"
#include "graphics_jni_helpers.h"

namespace android {

using Attribute = SkMeshSpecification::Attribute;
using Varying = SkMeshSpecification::Varying;

static struct {
    jclass clazz{};
    jfieldID type{};
    jfieldID offset{};
    jfieldID name{};
} gAttributeInfo;

static struct {
    jclass clazz{};
    jfieldID type{};
    jfieldID name{};
} gVaryingInfo;

std::vector<Attribute> extractAttributes(JNIEnv* env, jobjectArray attributes) {
    int size = env->GetArrayLength(attributes);
    std::vector<Attribute> attVector;
    attVector.reserve(size);
    for (int i = 0; i < size; i++) {
        jobject attribute = env->GetObjectArrayElement(attributes, i);
        auto name = (jstring)env->GetObjectField(attribute, gAttributeInfo.name);
        auto attName = ScopedUtfChars(env, name);
        Attribute temp{Attribute::Type(env->GetIntField(attribute, gAttributeInfo.type)),
                       static_cast<size_t>(env->GetIntField(attribute, gAttributeInfo.offset)),
                       SkString(attName.c_str())};
        attVector.push_back(std::move(temp));
    }

    return attVector;
}

std::vector<Varying> extractVaryings(JNIEnv* env, jobjectArray varyings) {
    int size = env->GetArrayLength(varyings);
    std::vector<Varying> varyVector;
    varyVector.reserve(size);
    for (int i = 0; i < size; i++) {
        jobject varying = env->GetObjectArrayElement(varyings, i);
        auto name = (jstring)env->GetObjectField(varying, gVaryingInfo.name);
        auto varyName = ScopedUtfChars(env, name);
        Varying temp{Varying::Type(env->GetIntField(varying, gVaryingInfo.type)),
                     SkString(varyName.c_str())};
        varyVector.push_back(std::move(temp));
    }

    return varyVector;
}

static jlong Make(JNIEnv* env, jobject thiz, jobjectArray attributeArray, jint vertexStride,
                  jobjectArray varyingArray, jstring vertexShader, jstring fragmentShader) {
    auto attributes = extractAttributes(env, attributeArray);
    auto varyings = extractVaryings(env, varyingArray);
    auto skVertexShader = ScopedUtfChars(env, vertexShader);
    auto skFragmentShader = ScopedUtfChars(env, fragmentShader);
    auto meshSpec = SkMeshSpecification::Make(attributes, vertexStride, varyings,
                                              SkString(skVertexShader.c_str()),
                                              SkString(skFragmentShader.c_str()))
                            .specification;
    return reinterpret_cast<jlong>(meshSpec.release());
}

static jlong MakeWithCS(JNIEnv* env, jobject thiz, jobjectArray attributeArray, jint vertexStride,
                        jobjectArray varyingArray, jstring vertexShader, jstring fragmentShader,
                        jlong colorSpace) {
    auto attributes = extractAttributes(env, attributeArray);
    auto varyings = extractVaryings(env, varyingArray);
    auto skVertexShader = ScopedUtfChars(env, vertexShader);
    auto skFragmentShader = ScopedUtfChars(env, fragmentShader);
    auto meshSpec = SkMeshSpecification::Make(attributes, vertexStride, varyings,
                                              SkString(skVertexShader.c_str()),
                                              SkString(skFragmentShader.c_str()),
                                              GraphicsJNI::getNativeColorSpace(colorSpace))
                            .specification;

    return reinterpret_cast<jlong>(meshSpec.release());
}

static jlong MakeWithAlpha(JNIEnv* env, jobject thiz, jobjectArray attributeArray,
                           jint vertexStride, jobjectArray varyingArray, jstring vertexShader,
                           jstring fragmentShader, jlong colorSpace, jint alphaType) {
    auto attributes = extractAttributes(env, attributeArray);
    auto varyings = extractVaryings(env, varyingArray);
    auto skVertexShader = ScopedUtfChars(env, vertexShader);
    auto skFragmentShader = ScopedUtfChars(env, fragmentShader);
    auto meshSpec = SkMeshSpecification::Make(
                            attributes, vertexStride, varyings, SkString(skVertexShader.c_str()),
                            SkString(skFragmentShader.c_str()),
                            GraphicsJNI::getNativeColorSpace(colorSpace), SkAlphaType(alphaType))
                            .specification;
    return reinterpret_cast<jlong>(meshSpec.release());
}

static void MeshSpecification_safeUnref(SkMeshSpecification* meshSpec) {
    SkSafeUnref(meshSpec);
}

static jlong getMeshSpecificationFinalizer() {
    return static_cast<jlong>(reinterpret_cast<uintptr_t>(&MeshSpecification_safeUnref));
}

static const JNINativeMethod gMeshSpecificationMethods[] = {
        {"nativeGetFinalizer", "()J", (void*)getMeshSpecificationFinalizer},
        {"nativeMake",
         "([Landroid/graphics/MeshSpecification$Attribute;I[Landroid/graphics/"
         "MeshSpecification$Varying;"
         "Ljava/lang/String;Ljava/lang/String;)J",
         (void*)Make},
        {"nativeMakeWithCS",
         "([Landroid/graphics/MeshSpecification$Attribute;I"
         "[Landroid/graphics/MeshSpecification$Varying;Ljava/lang/String;Ljava/lang/String;J)J",
         (void*)MakeWithCS},
        {"nativeMakeWithAlpha",
         "([Landroid/graphics/MeshSpecification$Attribute;I"
         "[Landroid/graphics/MeshSpecification$Varying;Ljava/lang/String;Ljava/lang/String;JI)J",
         (void*)MakeWithAlpha}};

int register_android_graphics_MeshSpecification(JNIEnv* env) {
    android::RegisterMethodsOrDie(env, "android/graphics/MeshSpecification",
                                  gMeshSpecificationMethods, NELEM(gMeshSpecificationMethods));

    gAttributeInfo.clazz = env->FindClass("android/graphics/MeshSpecification$Attribute");
    gAttributeInfo.type = env->GetFieldID(gAttributeInfo.clazz, "mType", "I");
    gAttributeInfo.offset = env->GetFieldID(gAttributeInfo.clazz, "mOffset", "I");
    gAttributeInfo.name = env->GetFieldID(gAttributeInfo.clazz, "mName", "Ljava/lang/String;");

    gVaryingInfo.clazz = env->FindClass("android/graphics/MeshSpecification$Varying");
    gVaryingInfo.type = env->GetFieldID(gVaryingInfo.clazz, "mType", "I");
    gVaryingInfo.name = env->GetFieldID(gVaryingInfo.clazz, "mName", "Ljava/lang/String;");
    return 0;
}

}  // namespace android
 No newline at end of file