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

Commit 25e8d87e authored by Jorge Betancourt's avatar Jorge Betancourt Committed by Android (Google) Code Review
Browse files

Merge "add runtime color filter support to platform" into main

parents 70d69921 2c9a6886
Loading
Loading
Loading
Loading
+19 −0
Original line number Diff line number Diff line
@@ -17361,6 +17361,25 @@ package android.graphics {
    method public boolean setUseCompositingLayer(boolean, @Nullable android.graphics.Paint);
  }
  @FlaggedApi("com.android.graphics.hwui.flags.runtime_color_filters_blenders") public class RuntimeColorFilter extends android.graphics.ColorFilter {
    ctor public RuntimeColorFilter(@NonNull String);
    method public void setColorUniform(@NonNull String, @ColorInt int);
    method public void setColorUniform(@NonNull String, @ColorLong long);
    method public void setColorUniform(@NonNull String, @NonNull android.graphics.Color);
    method public void setFloatUniform(@NonNull String, float);
    method public void setFloatUniform(@NonNull String, float, float);
    method public void setFloatUniform(@NonNull String, float, float, float);
    method public void setFloatUniform(@NonNull String, float, float, float, float);
    method public void setFloatUniform(@NonNull String, @NonNull float[]);
    method public void setInputColorFilter(@NonNull String, @NonNull android.graphics.ColorFilter);
    method public void setInputShader(@NonNull String, @NonNull android.graphics.Shader);
    method public void setIntUniform(@NonNull String, int);
    method public void setIntUniform(@NonNull String, int, int);
    method public void setIntUniform(@NonNull String, int, int, int);
    method public void setIntUniform(@NonNull String, int, int, int, int);
    method public void setIntUniform(@NonNull String, @NonNull int[]);
  }
  public class RuntimeShader extends android.graphics.Shader {
    ctor public RuntimeShader(@NonNull String);
    method public void setColorUniform(@NonNull String, @ColorInt int);
+305 −0
Original line number Diff line number Diff line
/*
 * Copyright 2024 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.FlaggedApi;
import android.annotation.NonNull;

import com.android.graphics.hwui.flags.Flags;


/**
 * <p>A {@link RuntimeColorFilter} calculates a per-pixel color based on the output of a user
 *  * defined Android Graphics Shading Language (AGSL) function.</p>
 *
 * <p>This AGSL function takes in an input color to be operated on. This color is in sRGB and the
 *  * output is also interpreted as sRGB. The AGSL function signature expects a single input
 *  * of color (packed as a half4 or float4 or vec4).</p>
 *
 * <pre class="prettyprint">
 * vec4 main(half4 in_color);
 * </pre>
 */
@FlaggedApi(Flags.FLAG_RUNTIME_COLOR_FILTERS_BLENDERS)
public class RuntimeColorFilter extends ColorFilter {

    private String mAgsl;

    /**
     * Creates a new RuntimeColorFilter.
     *
     * @param agsl The text of AGSL color filter program to run.
     */
    public RuntimeColorFilter(@NonNull String agsl) {
        if (agsl == null) {
            throw new NullPointerException("RuntimeColorFilter requires a non-null AGSL string");
        }
        mAgsl = agsl;
        // call to parent class to register native RuntimeColorFilter
        // TODO: find way to get super class to create native instance without requiring the storage
        // of agsl string
        getNativeInstance();

    }
    /**
     * Sets the uniform color value corresponding to this color filter.  If the effect 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 IllegalArgumentException is thrown.
     *
     * @param uniformName name matching the color uniform declared in the AGSL program
     * @param color the provided sRGB color
     */
    public void setColorUniform(@NonNull String uniformName, @ColorInt int color) {
        setUniform(uniformName, Color.valueOf(color).getComponents(), true);
    }

    /**
     * Sets the uniform color value corresponding to this color filter.  If the effect 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 IllegalArgumentException is thrown.
     *
     * @param uniformName name matching the color uniform declared in the AGSL program
     * @param color the provided sRGB color
     */
    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 this color filter.  If the effect 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 IllegalArgumentException is thrown.
     *
     * @param uniformName name matching the color uniform declared in the AGSL program
     * @param color the provided sRGB color
     */
    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 this color filter.  If the effect 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 uniform declared in the AGSL program
     */
    public void setFloatUniform(@NonNull String uniformName, float value) {
        setFloatUniform(uniformName, value, 0.0f, 0.0f, 0.0f, 1);
    }

    /**
     * Sets the uniform value corresponding to this color filter.  If the effect 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 uniform declared in the AGSL program
     */
    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 this color filter.  If the effect 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 uniform declared in the AGSL program
     */
    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 this color filter.  If the effect 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 uniform declared in the AGSL program
     */
    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 this color filter.  If the effect 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 uniform declared in the AGSL program
     */
    public void setFloatUniform(@NonNull String uniformName, @NonNull float[] values) {
        setUniform(uniformName, values, false);
    }

    private void setFloatUniform(@NonNull 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(getNativeInstance(), uniformName, value1, value2, value3, value4,
                count);
    }

    private void setUniform(@NonNull String uniformName, @NonNull 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(getNativeInstance(), uniformName, values, isColor);
    }

    /**
     * Sets the uniform value corresponding to this color filter.  If the effect does not have a
     * uniform with that name or if the uniform is declared with a type other than an int or int[1]
     * then an IllegalArgumentException is thrown.
     *
     * @param uniformName name matching the uniform declared in the AGSL program
     */
    public void setIntUniform(@NonNull String uniformName, int value) {
        setIntUniform(uniformName, value, 0, 0, 0, 1);
    }

    /**
     * Sets the uniform value corresponding to this color filter.  If the effect does not have a
     * uniform with that name or if the uniform is declared with a type other than an ivec2 or
     * int[2] then an IllegalArgumentException is thrown.
     *
     * @param uniformName name matching the uniform declared in the AGSL program
     */
    public void setIntUniform(@NonNull String uniformName, int value1, int value2) {
        setIntUniform(uniformName, value1, value2, 0, 0, 2);
    }

    /**
     * Sets the uniform value corresponding to this color filter.  If the effect does not have a
     * uniform with that name or if the uniform is declared with a type other than an ivec3 or
     * int[3] then an IllegalArgumentException is thrown.
     *
     * @param uniformName name matching the uniform declared in the AGSL program
     */
    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 this color filter.  If the effect does not have a
     * uniform with that name or if the uniform is declared with a type other than an ivec4 or
     * int[4] then an IllegalArgumentException is thrown.
     *
     * @param uniformName name matching the uniform declared in the AGSL program
     */
    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 this color filter.  If the effect 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 uniform declared in the AGSL program
     */
    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(getNativeInstance(), uniformName, values);
    }

    private void setIntUniform(@NonNull 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(getNativeInstance(), uniformName, value1, value2, value3, value4,
                count);
    }

    /**
     * Assigns the uniform shader to the provided shader parameter.  If the shader program does not
     * have a uniform shader with that name then an IllegalArgumentException is thrown.
     *
     * @param shaderName name matching the uniform declared in the AGSL program
     * @param shader shader passed into the AGSL program for sampling
     */
    public void setInputShader(@NonNull String shaderName, @NonNull Shader shader) {
        if (shaderName == null) {
            throw new NullPointerException("The shaderName parameter must not be null");
        }
        if (shader == null) {
            throw new NullPointerException("The shader parameter must not be null");
        }
        nativeUpdateChild(getNativeInstance(), shaderName, shader.getNativeInstance());
    }

    /**
     * Assigns the uniform color filter to the provided color filter parameter.  If the shader
     * program does not have a uniform color filter with that name then an IllegalArgumentException
     * is thrown.
     *
     * @param filterName name matching the uniform declared in the AGSL program
     * @param colorFilter filter passed into the AGSL program for sampling
     */
    public void setInputColorFilter(@NonNull String filterName, @NonNull ColorFilter colorFilter) {
        if (filterName == null) {
            throw new NullPointerException("The filterName parameter must not be null");
        }
        if (colorFilter == null) {
            throw new NullPointerException("The colorFilter parameter must not be null");
        }
        nativeUpdateChild(getNativeInstance(), filterName, colorFilter.getNativeInstance());
    }

    /** @hide */
    @Override
    protected long createNativeInstance() {
        return nativeCreateRuntimeColorFilter(mAgsl);
    }

    private static native long nativeCreateRuntimeColorFilter(String agsl);
    private static native void nativeUpdateUniforms(
            long colorFilter, String uniformName, float[] uniforms, boolean isColor);
    private static native void nativeUpdateUniforms(
            long colorFilter, String uniformName, float value1, float value2, float value3,
            float value4, int count);
    private static native void nativeUpdateUniforms(
            long colorFilter, String uniformName, int[] uniforms);
    private static native void nativeUpdateUniforms(
            long colorFilter, String uniformName, int value1, int value2, int value3,
            int value4, int count);
    private static native void nativeUpdateChild(long colorFilter, String childName, long child);

}
+1 −0
Original line number Diff line number Diff line
@@ -384,6 +384,7 @@ cc_defaults {
        "jni/ScopedParcel.cpp",
        "jni/Shader.cpp",
        "jni/RenderEffect.cpp",
        "jni/RuntimeEffectUtils.cpp",
        "jni/Typeface.cpp",
        "jni/Utils.cpp",
        "jni/YuvToJpegEncoder.cpp",
+31 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@
#include <memory>

#include "GraphicsJNI.h"
#include "RuntimeEffectUtils.h"
#include "SkColorFilter.h"

namespace android {
@@ -113,6 +114,36 @@ private:
    std::vector<float> mMatrix;
};

class RuntimeColorFilter : public ColorFilter {
public:
    RuntimeColorFilter(SkRuntimeEffectBuilder* builder) : mBuilder(builder) {}

    void updateUniforms(JNIEnv* env, const char* name, const float vals[], int count,
                        bool isColor) {
        UpdateFloatUniforms(env, mBuilder, name, vals, count, isColor);
        discardInstance();
    }

    void updateUniforms(JNIEnv* env, const char* name, const int vals[], int count) {
        UpdateIntUniforms(env, mBuilder, name, vals, count);
        discardInstance();
    }

    void updateChild(JNIEnv* env, const char* name, SkFlattenable* childEffect) {
        UpdateChild(env, mBuilder, name, childEffect);
        discardInstance();
    }

private:
    sk_sp<SkColorFilter> createInstance() override {
        // TODO: throw error if null
        return mBuilder->makeColorFilter();
    }

private:
    SkRuntimeEffectBuilder* mBuilder;
};

}  // namespace uirenderer
}  // namespace android

+92 −1
Original line number Diff line number Diff line
@@ -18,7 +18,9 @@
#include "ColorFilter.h"

#include "GraphicsJNI.h"
#include "RuntimeEffectUtils.h"
#include "SkBlendMode.h"
#include "include/effects/SkRuntimeEffect.h"

namespace android {

@@ -89,6 +91,78 @@ public:
            filter->setMatrix(getMatrixFromJFloatArray(env, jarray));
        }
    }

    static jlong RuntimeColorFilter_createColorFilter(JNIEnv* env, jobject, jstring agsl) {
        ScopedUtfChars strSksl(env, agsl);
        auto result = SkRuntimeEffect::MakeForColorFilter(SkString(strSksl.c_str()),
                                                          SkRuntimeEffect::Options{});
        if (result.effect.get() == nullptr) {
            doThrowIAE(env, result.errorText.c_str());
            return 0;
        }
        auto builder = new SkRuntimeEffectBuilder(std::move(result.effect));
        auto* runtimeColorFilter = new RuntimeColorFilter(builder);
        runtimeColorFilter->incStrong(nullptr);
        return static_cast<jlong>(reinterpret_cast<uintptr_t>(runtimeColorFilter));
    }

    static void RuntimeColorFilter_updateUniformsFloatArray(JNIEnv* env, jobject,
                                                            jlong colorFilterPtr,
                                                            jstring uniformName,
                                                            jfloatArray uniforms,
                                                            jboolean isColor) {
        auto* filter = reinterpret_cast<RuntimeColorFilter*>(colorFilterPtr);
        ScopedUtfChars name(env, uniformName);
        AutoJavaFloatArray autoValues(env, uniforms, 0, kRO_JNIAccess);
        if (filter) {
            filter->updateUniforms(env, name.c_str(), autoValues.ptr(), autoValues.length(),
                                   isColor);
        }
    }

    static void RuntimeColorFilter_updateUniformsFloats(JNIEnv* env, jobject, jlong colorFilterPtr,
                                                        jstring uniformName, jfloat value1,
                                                        jfloat value2, jfloat value3, jfloat value4,
                                                        jint count) {
        auto* filter = reinterpret_cast<RuntimeColorFilter*>(colorFilterPtr);
        ScopedUtfChars name(env, uniformName);
        const float values[4] = {value1, value2, value3, value4};
        if (filter) {
            filter->updateUniforms(env, name.c_str(), values, count, false);
        }
    }

    static void RuntimeColorFilter_updateUniformsIntArray(JNIEnv* env, jobject,
                                                          jlong colorFilterPtr, jstring uniformName,
                                                          jintArray uniforms) {
        auto* filter = reinterpret_cast<RuntimeColorFilter*>(colorFilterPtr);
        ScopedUtfChars name(env, uniformName);
        AutoJavaIntArray autoValues(env, uniforms, 0);
        if (filter) {
            filter->updateUniforms(env, name.c_str(), autoValues.ptr(), autoValues.length());
        }
    }

    static void RuntimeColorFilter_updateUniformsInts(JNIEnv* env, jobject, jlong colorFilterPtr,
                                                      jstring uniformName, jint value1, jint value2,
                                                      jint value3, jint value4, jint count) {
        auto* filter = reinterpret_cast<RuntimeColorFilter*>(colorFilterPtr);
        ScopedUtfChars name(env, uniformName);
        const int values[4] = {value1, value2, value3, value4};
        if (filter) {
            filter->updateUniforms(env, name.c_str(), values, count);
        }
    }

    static void RuntimeColorFilter_updateChild(JNIEnv* env, jobject, jlong colorFilterPtr,
                                               jstring childName, jlong childPtr) {
        auto* filter = reinterpret_cast<RuntimeColorFilter*>(colorFilterPtr);
        ScopedUtfChars name(env, childName);
        auto* child = reinterpret_cast<SkFlattenable*>(childPtr);
        if (filter && child) {
            filter->updateChild(env, name.c_str(), child);
        }
    }
};

static const JNINativeMethod colorfilter_methods[] = {
@@ -107,6 +181,20 @@ static const JNINativeMethod colormatrix_methods[] = {
        {"nativeColorMatrixFilter", "([F)J", (void*)ColorFilterGlue::CreateColorMatrixFilter},
        {"nativeSetColorMatrix", "(J[F)V", (void*)ColorFilterGlue::SetColorMatrix}};

static const JNINativeMethod runtime_color_filter_methods[] = {
        {"nativeCreateRuntimeColorFilter", "(Ljava/lang/String;)J",
         (void*)ColorFilterGlue::RuntimeColorFilter_createColorFilter},
        {"nativeUpdateUniforms", "(JLjava/lang/String;[FZ)V",
         (void*)ColorFilterGlue::RuntimeColorFilter_updateUniformsFloatArray},
        {"nativeUpdateUniforms", "(JLjava/lang/String;FFFFI)V",
         (void*)ColorFilterGlue::RuntimeColorFilter_updateUniformsFloats},
        {"nativeUpdateUniforms", "(JLjava/lang/String;[I)V",
         (void*)ColorFilterGlue::RuntimeColorFilter_updateUniformsIntArray},
        {"nativeUpdateUniforms", "(JLjava/lang/String;IIIII)V",
         (void*)ColorFilterGlue::RuntimeColorFilter_updateUniformsInts},
        {"nativeUpdateChild", "(JLjava/lang/String;J)V",
         (void*)ColorFilterGlue::RuntimeColorFilter_updateChild}};

int register_android_graphics_ColorFilter(JNIEnv* env) {
    android::RegisterMethodsOrDie(env, "android/graphics/ColorFilter", colorfilter_methods,
                                  NELEM(colorfilter_methods));
@@ -118,6 +206,9 @@ int register_android_graphics_ColorFilter(JNIEnv* env) {
                                  NELEM(lighting_methods));
    android::RegisterMethodsOrDie(env, "android/graphics/ColorMatrixColorFilter",
                                  colormatrix_methods, NELEM(colormatrix_methods));
    android::RegisterMethodsOrDie(env, "android/graphics/RuntimeColorFilter",
                                  runtime_color_filter_methods,
                                  NELEM(runtime_color_filter_methods));

    return 0;
}
Loading