[androidkit] Color filter support

Add MatrixColorFilter, HSLAMatrixColorFilter and ComposeColorFilter.

Change-Id: I047368adcd13452566a40e91a6f594dd525efd5f
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/422517
Commit-Queue: Florin Malita <fmalita@google.com>
Reviewed-by: Jorge Betancourt <jmbetancourt@google.com>
This commit is contained in:
Florin Malita 2021-06-28 16:34:14 -04:00 committed by Skia Commit-Bot
parent 78af79e98d
commit 1df8756419
11 changed files with 249 additions and 10 deletions

View File

@ -2519,6 +2519,7 @@ if (skia_enable_tools) {
sources = [ sources = [
"modules/androidkit/src/AndroidKit.cpp", "modules/androidkit/src/AndroidKit.cpp",
"modules/androidkit/src/Canvas.cpp", "modules/androidkit/src/Canvas.cpp",
"modules/androidkit/src/ColorFilters.cpp",
"modules/androidkit/src/Gradients.cpp", "modules/androidkit/src/Gradients.cpp",
"modules/androidkit/src/Image.cpp", "modules/androidkit/src/Image.cpp",
"modules/androidkit/src/ImageFilter.cpp", "modules/androidkit/src/ImageFilter.cpp",

View File

@ -24,10 +24,14 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
} }
REGISTER_NATIVES(Canvas) REGISTER_NATIVES(Canvas)
REGISTER_NATIVES(ColorFilter)
REGISTER_NATIVES(ComposeColorFilter)
REGISTER_NATIVES(HSLAMatrixColorFilter)
REGISTER_NATIVES(Image) REGISTER_NATIVES(Image)
REGISTER_NATIVES(ImageFilter) REGISTER_NATIVES(ImageFilter)
REGISTER_NATIVES(LinearGradient) REGISTER_NATIVES(LinearGradient)
REGISTER_NATIVES(Matrix) REGISTER_NATIVES(Matrix)
REGISTER_NATIVES(MatrixColorFilter)
REGISTER_NATIVES(Paint) REGISTER_NATIVES(Paint)
REGISTER_NATIVES(Path) REGISTER_NATIVES(Path)
REGISTER_NATIVES(PathBuilder) REGISTER_NATIVES(PathBuilder)

View File

@ -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 <jni.h>
#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<jlong>(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<jlong>(cf.release());
}
static jlong MakeCompose(JNIEnv*, jobject, jlong outer, jlong inner) {
auto cf = SkColorFilters::Compose(sk_ref_sp(reinterpret_cast<SkColorFilter*>(outer)),
sk_ref_sp(reinterpret_cast<SkColorFilter*>(inner)));
return reinterpret_cast<jlong>(cf.release());
}
static void ColorFilter_Release(JNIEnv*, jobject, jlong native_cf) {
SkSafeUnref(reinterpret_cast<SkColorFilter*>(native_cf));
}
} // namespace
int register_androidkit_ColorFilter(JNIEnv* env) {
static const JNINativeMethod methods[] = {
{"nRelease" , "(J)V" , reinterpret_cast<void*>(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<void*>(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<void*>(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<void*>(MakeCompose)},
};
const auto clazz = env->FindClass("org/skia/androidkit/ComposeColorFilter");
return clazz
? env->RegisterNatives(clazz, methods, SK_ARRAY_COUNT(methods))
: JNI_ERR;
}

View File

@ -7,34 +7,35 @@
#include <jni.h> #include <jni.h>
#include "include/core/SkColorFilter.h"
#include "include/core/SkPaint.h" #include "include/core/SkPaint.h"
#include "include/core/SkShader.h" #include "include/core/SkShader.h"
#include "include/effects/SkImageFilters.h" #include "include/effects/SkImageFilters.h"
namespace { namespace {
static jlong Paint_Create(JNIEnv* env, jobject) { static jlong Paint_Create(JNIEnv*, jobject) {
return reinterpret_cast<jlong>(new SkPaint); return reinterpret_cast<jlong>(new SkPaint);
} }
static void Paint_Release(JNIEnv* env, jobject, jlong native_paint) { static void Paint_Release(JNIEnv*, jobject, jlong native_paint) {
delete reinterpret_cast<SkPaint*>(native_paint); delete reinterpret_cast<SkPaint*>(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) { float r, float g, float b, float a) {
if (auto* paint = reinterpret_cast<SkPaint*>(native_paint)) { if (auto* paint = reinterpret_cast<SkPaint*>(native_paint)) {
paint->setColor4f({r, g, b, a}); 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<SkPaint*>(native_paint)) { if (auto* paint = reinterpret_cast<SkPaint*>(native_paint)) {
paint->setStroke(stroke); 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<SkPaint*>(native_paint)) { if (auto* paint = reinterpret_cast<SkPaint*>(native_paint)) {
paint->setStrokeWidth(width); 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<SkPaint*>(native_paint)) {
paint->setColorFilter(sk_ref_sp(reinterpret_cast<SkColorFilter*>(native_cf)));
}
}
static void Paint_SetShader(JNIEnv*, jobject, jlong native_paint, jlong native_shader) {
if (auto* paint = reinterpret_cast<SkPaint*>(native_paint)) { if (auto* paint = reinterpret_cast<SkPaint*>(native_paint)) {
paint->setShader(sk_ref_sp(reinterpret_cast<SkShader*>(native_shader))); paint->setShader(sk_ref_sp(reinterpret_cast<SkShader*>(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<SkPaint*>(native_paint)) { if (auto* paint = reinterpret_cast<SkPaint*>(native_paint)) {
paint->setImageFilter(sk_ref_sp(reinterpret_cast<SkImageFilter*>(native_filter))); paint->setImageFilter(sk_ref_sp(reinterpret_cast<SkImageFilter*>(native_filter)));
} }
@ -108,6 +115,7 @@ int register_androidkit_Paint(JNIEnv* env) {
{"nSetStrokeCap" , "(JI)V" , reinterpret_cast<void*>(Paint_SetStrokeCap)}, {"nSetStrokeCap" , "(JI)V" , reinterpret_cast<void*>(Paint_SetStrokeCap)},
{"nSetStrokeJoin" , "(JI)V" , reinterpret_cast<void*>(Paint_SetStrokeJoin)}, {"nSetStrokeJoin" , "(JI)V" , reinterpret_cast<void*>(Paint_SetStrokeJoin)},
{"nSetStrokeMiter" , "(JF)V" , reinterpret_cast<void*>(Paint_SetStrokeMiter)}, {"nSetStrokeMiter" , "(JF)V" , reinterpret_cast<void*>(Paint_SetStrokeMiter)},
{"nSetColorFilter" , "(JJ)V" , reinterpret_cast<void*>(Paint_SetColorFilter)},
{"nSetShader" , "(JJ)V" , reinterpret_cast<void*>(Paint_SetShader)}, {"nSetShader" , "(JJ)V" , reinterpret_cast<void*>(Paint_SetShader)},
{"nSetImageFilter" , "(JJ)V" , reinterpret_cast<void*>(Paint_SetImageFilter)}, {"nSetImageFilter" , "(JJ)V" , reinterpret_cast<void*>(Paint_SetImageFilter)},
}; };

View File

@ -40,6 +40,33 @@ private:
CString& operator=(const CString&) = delete; 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); SkSamplingOptions SamplingOptions(jint, jfloat, jfloat);
SkTileMode TileMode(jint); SkTileMode TileMode(jint);

View File

@ -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);
}

View File

@ -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);
};

View File

@ -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);
};

View File

@ -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);
};

View File

@ -26,6 +26,11 @@ public class Paint {
return this; return this;
} }
public Paint setColorFilter(@Nullable ColorFilter filter) {
nSetColorFilter(mNativeInstance, filter != null ? filter.getNativeInstance() : 0);
return this;
}
public Paint setShader(@Nullable Shader shader) { public Paint setShader(@Nullable Shader shader) {
nSetShader(mNativeInstance, shader != null ? shader.getNativeInstance() : 0); nSetShader(mNativeInstance, shader != null ? shader.getNativeInstance() : 0);
return this; return this;
@ -107,6 +112,7 @@ public class Paint {
private static native void nSetStrokeCap(long nativeInstance, int native_cap); private static native void nSetStrokeCap(long nativeInstance, int native_cap);
private static native void nSetStrokeJoin(long nativeInstance, int native_join); private static native void nSetStrokeJoin(long nativeInstance, int native_join);
private static native void nSetStrokeMiter(long nativeInstance, float limit); 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 nSetShader(long nativeInstance, long nativeShader);
private static native void nSetImageFilter(long nativeInstance, long nativeFilter); private static native void nSetImageFilter(long nativeInstance, long nativeFilter);
} }

View File

@ -19,6 +19,12 @@ class AnimationRenderer extends SurfaceRenderer {
mRadialGradient, mRadialGradient,
mConicalGradient, mConicalGradient,
mSweepGradient; 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 @Override
protected void onSurfaceInitialized(Surface surface) { protected void onSurfaceInitialized(Surface surface) {
@ -38,8 +44,8 @@ class AnimationRenderer extends SurfaceRenderer {
float[] pos = {0, 0.5f, 1}; float[] pos = {0, 0.5f, 1};
mLinearGradient = new LinearGradient(0, 0, sw, 0, mLinearGradient = new LinearGradient(0, 0, sw/4, 0,
colors1, pos, TileMode.CLAMP); colors1, pos, TileMode.REPEAT);
mRadialGradient = new RadialGradient(sw/2, sh/4, Math.min(sw, sh)/2, mRadialGradient = new RadialGradient(sw/2, sh/4, Math.min(sw, sh)/2,
colors2, pos, TileMode.REPEAT); colors2, pos, TileMode.REPEAT);
mConicalGradient = new TwoPointConicalGradient(sw/4, sh/2, sw/4, mConicalGradient = new TwoPointConicalGradient(sw/4, sh/2, sw/4,
@ -72,7 +78,7 @@ class AnimationRenderer extends SurfaceRenderer {
kHeight = 200; kHeight = 200;
canvas.drawRect(cx - kWidth/2, cy - kHeight/2, cx + kWidth/2, cy + kHeight/2, canvas.drawRect(cx - kWidth/2, cy - kHeight/2, cx + kWidth/2, cy + kHeight/2,
new Paint().setShader(shader)); new Paint().setShader(shader).setColorFilter(mColorFilter));
} }
} }