diff --git a/BUILD.gn b/BUILD.gn index d07d63053e..43a0cd0fc8 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -2519,6 +2519,7 @@ if (skia_enable_tools) { sources = [ "modules/androidkit/src/AndroidKit.cpp", "modules/androidkit/src/Canvas.cpp", + "modules/androidkit/src/ColorFilters.cpp", "modules/androidkit/src/Gradients.cpp", "modules/androidkit/src/Image.cpp", "modules/androidkit/src/ImageFilter.cpp", diff --git a/modules/androidkit/src/AndroidKit.cpp b/modules/androidkit/src/AndroidKit.cpp index c3b6ba6760..593e304c4e 100644 --- a/modules/androidkit/src/AndroidKit.cpp +++ b/modules/androidkit/src/AndroidKit.cpp @@ -24,10 +24,14 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { } REGISTER_NATIVES(Canvas) + REGISTER_NATIVES(ColorFilter) + REGISTER_NATIVES(ComposeColorFilter) + REGISTER_NATIVES(HSLAMatrixColorFilter) REGISTER_NATIVES(Image) REGISTER_NATIVES(ImageFilter) REGISTER_NATIVES(LinearGradient) REGISTER_NATIVES(Matrix) + REGISTER_NATIVES(MatrixColorFilter) REGISTER_NATIVES(Paint) REGISTER_NATIVES(Path) REGISTER_NATIVES(PathBuilder) diff --git a/modules/androidkit/src/ColorFilters.cpp b/modules/androidkit/src/ColorFilters.cpp new file mode 100644 index 0000000000..347de516fe --- /dev/null +++ b/modules/androidkit/src/ColorFilters.cpp @@ -0,0 +1,86 @@ +/* + * Copyright 2021 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include + +#include "include/core/SkColorFilter.h" +#include "modules/androidkit/src/Utils.h" + +namespace { + +static jlong MakeMatrix(JNIEnv* env, jobject, jfloatArray jcm) { + SkASSERT(env->GetArrayLength(jcm) == 20); + + auto cf = SkColorFilters::Matrix(androidkit::utils::CFloats(env, jcm)); + + return reinterpret_cast(cf.release()); +} + +static jlong MakeHSLAMatrix(JNIEnv* env, jobject, jfloatArray jcm) { + SkASSERT(env->GetArrayLength(jcm) == 20); + + auto cf = SkColorFilters::HSLAMatrix(androidkit::utils::CFloats(env, jcm)); + + return reinterpret_cast(cf.release()); +} + +static jlong MakeCompose(JNIEnv*, jobject, jlong outer, jlong inner) { + auto cf = SkColorFilters::Compose(sk_ref_sp(reinterpret_cast(outer)), + sk_ref_sp(reinterpret_cast(inner))); + + return reinterpret_cast(cf.release()); +} + +static void ColorFilter_Release(JNIEnv*, jobject, jlong native_cf) { + SkSafeUnref(reinterpret_cast(native_cf)); +} + +} // namespace + +int register_androidkit_ColorFilter(JNIEnv* env) { + static const JNINativeMethod methods[] = { + {"nRelease" , "(J)V" , reinterpret_cast(ColorFilter_Release) }, + }; + + const auto clazz = env->FindClass("org/skia/androidkit/ColorFilter"); + return clazz + ? env->RegisterNatives(clazz, methods, SK_ARRAY_COUNT(methods)) + : JNI_ERR; +} + +int register_androidkit_MatrixColorFilter(JNIEnv* env) { + static const JNINativeMethod methods[] = { + {"nMakeMatrix", "([F)J", reinterpret_cast(MakeMatrix)}, + }; + + const auto clazz = env->FindClass("org/skia/androidkit/MatrixColorFilter"); + return clazz + ? env->RegisterNatives(clazz, methods, SK_ARRAY_COUNT(methods)) + : JNI_ERR; +} + +int register_androidkit_HSLAMatrixColorFilter(JNIEnv* env) { + static const JNINativeMethod methods[] = { + {"nMakeHSLAMatrix", "([F)J", reinterpret_cast(MakeHSLAMatrix)}, + }; + + const auto clazz = env->FindClass("org/skia/androidkit/HSLAMatrixColorFilter"); + return clazz + ? env->RegisterNatives(clazz, methods, SK_ARRAY_COUNT(methods)) + : JNI_ERR; +} + +int register_androidkit_ComposeColorFilter(JNIEnv* env) { + static const JNINativeMethod methods[] = { + {"nMakeCompose", "(JJ)J", reinterpret_cast(MakeCompose)}, + }; + + const auto clazz = env->FindClass("org/skia/androidkit/ComposeColorFilter"); + return clazz + ? env->RegisterNatives(clazz, methods, SK_ARRAY_COUNT(methods)) + : JNI_ERR; +} diff --git a/modules/androidkit/src/Paint.cpp b/modules/androidkit/src/Paint.cpp index 9b7e113a6f..35e9156936 100644 --- a/modules/androidkit/src/Paint.cpp +++ b/modules/androidkit/src/Paint.cpp @@ -7,34 +7,35 @@ #include +#include "include/core/SkColorFilter.h" #include "include/core/SkPaint.h" #include "include/core/SkShader.h" #include "include/effects/SkImageFilters.h" namespace { -static jlong Paint_Create(JNIEnv* env, jobject) { +static jlong Paint_Create(JNIEnv*, jobject) { return reinterpret_cast(new SkPaint); } -static void Paint_Release(JNIEnv* env, jobject, jlong native_paint) { +static void Paint_Release(JNIEnv*, jobject, jlong native_paint) { delete reinterpret_cast(native_paint); } -static void Paint_SetColor(JNIEnv* env, jobject, jlong native_paint, +static void Paint_SetColor(JNIEnv*, jobject, jlong native_paint, float r, float g, float b, float a) { if (auto* paint = reinterpret_cast(native_paint)) { paint->setColor4f({r, g, b, a}); } } -static void Paint_SetStroke(JNIEnv* env, jobject, jlong native_paint, jboolean stroke) { +static void Paint_SetStroke(JNIEnv*, jobject, jlong native_paint, jboolean stroke) { if (auto* paint = reinterpret_cast(native_paint)) { paint->setStroke(stroke); } } -static void Paint_SetStrokeWidth(JNIEnv* env, jobject, jlong native_paint, jfloat width) { +static void Paint_SetStrokeWidth(JNIEnv*, jobject, jlong native_paint, jfloat width) { if (auto* paint = reinterpret_cast(native_paint)) { paint->setStrokeWidth(width); } @@ -84,13 +85,19 @@ static void Paint_SetStrokeMiter(JNIEnv* env, jobject, jlong native_paint, jfloa } } -static void Paint_SetShader(JNIEnv* env, jobject, jlong native_paint, jlong native_shader) { +static void Paint_SetColorFilter(JNIEnv*, jobject, jlong native_paint, jlong native_cf) { + if (auto* paint = reinterpret_cast(native_paint)) { + paint->setColorFilter(sk_ref_sp(reinterpret_cast(native_cf))); + } +} + +static void Paint_SetShader(JNIEnv*, jobject, jlong native_paint, jlong native_shader) { if (auto* paint = reinterpret_cast(native_paint)) { paint->setShader(sk_ref_sp(reinterpret_cast(native_shader))); } } -static void Paint_SetImageFilter(JNIEnv* env, jobject, jlong native_paint, jlong native_filter) { +static void Paint_SetImageFilter(JNIEnv*, jobject, jlong native_paint, jlong native_filter) { if (auto* paint = reinterpret_cast(native_paint)) { paint->setImageFilter(sk_ref_sp(reinterpret_cast(native_filter))); } @@ -108,6 +115,7 @@ int register_androidkit_Paint(JNIEnv* env) { {"nSetStrokeCap" , "(JI)V" , reinterpret_cast(Paint_SetStrokeCap)}, {"nSetStrokeJoin" , "(JI)V" , reinterpret_cast(Paint_SetStrokeJoin)}, {"nSetStrokeMiter" , "(JF)V" , reinterpret_cast(Paint_SetStrokeMiter)}, + {"nSetColorFilter" , "(JJ)V" , reinterpret_cast(Paint_SetColorFilter)}, {"nSetShader" , "(JJ)V" , reinterpret_cast(Paint_SetShader)}, {"nSetImageFilter" , "(JJ)V" , reinterpret_cast(Paint_SetImageFilter)}, }; diff --git a/modules/androidkit/src/Utils.h b/modules/androidkit/src/Utils.h index 30faaa1dbf..411611ab39 100644 --- a/modules/androidkit/src/Utils.h +++ b/modules/androidkit/src/Utils.h @@ -40,6 +40,33 @@ private: CString& operator=(const CString&) = delete; }; +// RAII helper for float array access +class CFloats { +public: + CFloats(JNIEnv* env, const jfloatArray& jfloats) + : fEnv(env) + , fJFloats(jfloats) + , fCFloats(env->GetFloatArrayElements(jfloats, nullptr)) + {} + + ~CFloats() { + fEnv->ReleaseFloatArrayElements(fJFloats, fCFloats, 0); + } + + operator const float*() const { return fCFloats; } + +private: + JNIEnv* fEnv; + const jfloatArray& fJFloats; + float* fCFloats; + + + CFloats(CFloats&&) = delete; + CFloats(const CFloats&) = delete; + CFloats& operator=(CFloats&&) = delete; + CFloats& operator=(const CFloats&) = delete; +}; + SkSamplingOptions SamplingOptions(jint, jfloat, jfloat); SkTileMode TileMode(jint); diff --git a/platform_tools/android/apps/AndroidKit/src/main/java/org/skia/androidkit/ColorFilter.java b/platform_tools/android/apps/AndroidKit/src/main/java/org/skia/androidkit/ColorFilter.java new file mode 100644 index 0000000000..e57e7e57de --- /dev/null +++ b/platform_tools/android/apps/AndroidKit/src/main/java/org/skia/androidkit/ColorFilter.java @@ -0,0 +1,33 @@ +/* + * Copyright 2021 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +package org.skia.androidkit; + +public class ColorFilter { + private long mNativeInstance; + + /** + * Releases any resources associated with this ColorFilter. + */ + public void release() { + nRelease(mNativeInstance); + mNativeInstance = 0; + } + + @Override + protected void finalize() throws Throwable { + release(); + } + + protected ColorFilter(long native_instance) { + mNativeInstance = native_instance; + } + + long getNativeInstance() { return mNativeInstance; } + + private static native void nRelease(long nativeInstance); +} diff --git a/platform_tools/android/apps/AndroidKit/src/main/java/org/skia/androidkit/ComposeColorFilter.java b/platform_tools/android/apps/AndroidKit/src/main/java/org/skia/androidkit/ComposeColorFilter.java new file mode 100644 index 0000000000..bbfe3be17a --- /dev/null +++ b/platform_tools/android/apps/AndroidKit/src/main/java/org/skia/androidkit/ComposeColorFilter.java @@ -0,0 +1,16 @@ +/* + * Copyright 2021 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +package org.skia.androidkit; + +public class ComposeColorFilter extends ColorFilter { + public ComposeColorFilter(ColorFilter outer, ColorFilter inner) { + super(nMakeCompose(outer.getNativeInstance(), inner.getNativeInstance())); + } + + private static native long nMakeCompose(long outer, long inner); +}; \ No newline at end of file diff --git a/platform_tools/android/apps/AndroidKit/src/main/java/org/skia/androidkit/HSLAMatrixColorFilter.java b/platform_tools/android/apps/AndroidKit/src/main/java/org/skia/androidkit/HSLAMatrixColorFilter.java new file mode 100644 index 0000000000..732d215423 --- /dev/null +++ b/platform_tools/android/apps/AndroidKit/src/main/java/org/skia/androidkit/HSLAMatrixColorFilter.java @@ -0,0 +1,26 @@ +/* + * Copyright 2021 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +package org.skia.androidkit; + +import java.lang.IllegalArgumentException; + +public class HSLAMatrixColorFilter extends ColorFilter { + public HSLAMatrixColorFilter(float[] m) throws IllegalArgumentException { + super(makeNative(m)); + } + + private static long makeNative(float[] m) throws IllegalArgumentException { + if (m.length != 20) { + throw new IllegalArgumentException("Expecting an array of 20 floats."); + } + + return nMakeHSLAMatrix(m); + } + + private static native long nMakeHSLAMatrix(float[] m); +}; diff --git a/platform_tools/android/apps/AndroidKit/src/main/java/org/skia/androidkit/MatrixColorFilter.java b/platform_tools/android/apps/AndroidKit/src/main/java/org/skia/androidkit/MatrixColorFilter.java new file mode 100644 index 0000000000..1e823eb132 --- /dev/null +++ b/platform_tools/android/apps/AndroidKit/src/main/java/org/skia/androidkit/MatrixColorFilter.java @@ -0,0 +1,26 @@ +/* + * Copyright 2021 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +package org.skia.androidkit; + +import java.lang.IllegalArgumentException; + +public class MatrixColorFilter extends ColorFilter { + public MatrixColorFilter(float[] m) throws IllegalArgumentException { + super(makeNative(m)); + } + + private static long makeNative(float[] m) throws IllegalArgumentException { + if (m.length != 20) { + throw new IllegalArgumentException("Expecting an array of 20 floats."); + } + + return nMakeMatrix(m); + } + + private static native long nMakeMatrix(float[] m); +}; diff --git a/platform_tools/android/apps/AndroidKit/src/main/java/org/skia/androidkit/Paint.java b/platform_tools/android/apps/AndroidKit/src/main/java/org/skia/androidkit/Paint.java index 139002ff5a..75e02badcb 100644 --- a/platform_tools/android/apps/AndroidKit/src/main/java/org/skia/androidkit/Paint.java +++ b/platform_tools/android/apps/AndroidKit/src/main/java/org/skia/androidkit/Paint.java @@ -26,6 +26,11 @@ public class Paint { return this; } + public Paint setColorFilter(@Nullable ColorFilter filter) { + nSetColorFilter(mNativeInstance, filter != null ? filter.getNativeInstance() : 0); + return this; + } + public Paint setShader(@Nullable Shader shader) { nSetShader(mNativeInstance, shader != null ? shader.getNativeInstance() : 0); return this; @@ -107,6 +112,7 @@ public class Paint { private static native void nSetStrokeCap(long nativeInstance, int native_cap); private static native void nSetStrokeJoin(long nativeInstance, int native_join); private static native void nSetStrokeMiter(long nativeInstance, float limit); + private static native void nSetColorFilter(long nativeInstance, long nativeCF); private static native void nSetShader(long nativeInstance, long nativeShader); private static native void nSetImageFilter(long nativeInstance, long nativeFilter); } diff --git a/platform_tools/android/apps/androidkitdemo/src/main/java/org/skia/androidkitdemo1/AnimationActivity.java b/platform_tools/android/apps/androidkitdemo/src/main/java/org/skia/androidkitdemo1/AnimationActivity.java index 4beb21f31c..437648c146 100644 --- a/platform_tools/android/apps/androidkitdemo/src/main/java/org/skia/androidkitdemo1/AnimationActivity.java +++ b/platform_tools/android/apps/androidkitdemo/src/main/java/org/skia/androidkitdemo1/AnimationActivity.java @@ -19,6 +19,12 @@ class AnimationRenderer extends SurfaceRenderer { mRadialGradient, mConicalGradient, mSweepGradient; + private ColorFilter mColorFilter = new MatrixColorFilter(new float[]{ + 0.75f, 0, 0, 0, 0, + 0, 1, 0, 0, 0.5f, + 0, 0, 1, 0, 0, + 0, 0, 0, 1, 0, + }); @Override protected void onSurfaceInitialized(Surface surface) { @@ -38,8 +44,8 @@ class AnimationRenderer extends SurfaceRenderer { float[] pos = {0, 0.5f, 1}; - mLinearGradient = new LinearGradient(0, 0, sw, 0, - colors1, pos, TileMode.CLAMP); + mLinearGradient = new LinearGradient(0, 0, sw/4, 0, + colors1, pos, TileMode.REPEAT); mRadialGradient = new RadialGradient(sw/2, sh/4, Math.min(sw, sh)/2, colors2, pos, TileMode.REPEAT); mConicalGradient = new TwoPointConicalGradient(sw/4, sh/2, sw/4, @@ -72,7 +78,7 @@ class AnimationRenderer extends SurfaceRenderer { kHeight = 200; canvas.drawRect(cx - kWidth/2, cy - kHeight/2, cx + kWidth/2, cy + kHeight/2, - new Paint().setShader(shader)); + new Paint().setShader(shader).setColorFilter(mColorFilter)); } }