[androidkit] Add remaining gradients

Consolidate the common logic in a Gradient base class, and add
radial/conical/sweep factories.

Change-Id: Ife15ae78e7c6df48cfa67fe20054f9bd9a8a1e90
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/420897
Commit-Queue: Florin Malita <fmalita@chromium.org>
Commit-Queue: Florin Malita <fmalita@google.com>
Reviewed-by: Mike Reed <reed@google.com>
Reviewed-by: Jorge Betancourt <jmbetancourt@google.com>
This commit is contained in:
Florin Malita 2021-06-24 12:18:28 -04:00 committed by Skia Commit-Bot
parent a1e7f44c63
commit b445cbeee2
8 changed files with 390 additions and 52 deletions

View File

@ -31,10 +31,13 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
REGISTER_NATIVES(Paint)
REGISTER_NATIVES(Path)
REGISTER_NATIVES(PathBuilder)
REGISTER_NATIVES(RadialGradient)
REGISTER_NATIVES(RuntimeShaderBuilder)
REGISTER_NATIVES(Shader)
REGISTER_NATIVES(SkottieAnimation)
REGISTER_NATIVES(Surface)
REGISTER_NATIVES(SweepGradient)
REGISTER_NATIVES(TwoPointConicalGradient)
return JNI_VERSION_1_6;
}

View File

@ -13,28 +13,111 @@
namespace {
static jlong MakeLinear(JNIEnv* env, jobject, jfloat x0, jfloat y0, jfloat x1, jfloat y1,
jfloatArray jcolors, jfloatArray jpos, jint jtm, jlong native_lm) {
const auto count = env->GetArrayLength(jpos);
SkASSERT(env->GetArrayLength(jcolors) == 4*count);
auto* colors = env->GetFloatArrayElements(jcolors, nullptr);
auto* pos = env->GetFloatArrayElements(jpos, nullptr);
const SkPoint pts[] = {{x0, y0}, {x1, y1}};
const auto tm = androidkit::utils::TileMode(jtm);
SkMatrix lm;
if (const auto* lm44 = reinterpret_cast<const SkM44*>(native_lm)) {
lm = lm44->asM33();
// Helper for common gradient data access.
class GradientData {
public:
GradientData(JNIEnv* env, const jfloatArray& jcolors, const jfloatArray& jpos,
jint jtm, jlong native_lm)
: fEnv(env)
, fJColors(jcolors)
, fJPos(jpos)
, fColors(env->GetFloatArrayElements(jcolors, nullptr))
, fPos(env->GetFloatArrayElements(jpos, nullptr))
, fCount(env->GetArrayLength(jpos))
, fTileMode(androidkit::utils::TileMode(jtm))
, fLocalMatrix(native_lm ? reinterpret_cast<const SkM44*>(native_lm)->asM33() : SkMatrix())
{
SkASSERT(env->GetArrayLength(jcolors) == 4*fCount);
}
auto shader = SkGradientShader::MakeLinear(pts, reinterpret_cast<const SkColor4f*>(colors),
nullptr, pos, count, tm, 0, &lm);
~GradientData() {
fEnv->ReleaseFloatArrayElements(fJPos, fPos, 0);
fEnv->ReleaseFloatArrayElements(fJColors, fColors, 0);
}
env->ReleaseFloatArrayElements(jpos, pos, 0);
env->ReleaseFloatArrayElements(jcolors, colors, 0);
int count() const { return fCount; }
const SkColor4f* colors() const { return reinterpret_cast<const SkColor4f*>(fColors); }
const float* pos() const { return fPos; }
const SkTileMode& tileMode() const { return fTileMode; }
const SkMatrix& localMatrix() const { return fLocalMatrix; }
private:
JNIEnv* fEnv;
const jfloatArray& fJColors;
const jfloatArray& fJPos;
float* fColors;
float* fPos;
const int fCount;
const SkTileMode fTileMode;
const SkMatrix fLocalMatrix;
};
static jlong MakeLinear(JNIEnv* env, jobject, jfloat x0, jfloat y0, jfloat x1, jfloat y1,
jfloatArray jcolors, jfloatArray jpos, jint jtm, jlong native_lm) {
const GradientData gdata(env, jcolors, jpos, jtm, native_lm);
const SkPoint pts[] = {{x0, y0}, {x1, y1}};
auto shader = SkGradientShader::MakeLinear(pts,
gdata.colors(),
nullptr,
gdata.pos(),
gdata.count(),
gdata.tileMode(),
0,
&gdata.localMatrix());
return reinterpret_cast<jlong>(shader.release());
}
static jlong MakeRadial(JNIEnv* env, jobject, jfloat x, jfloat y, jfloat r,
jfloatArray jcolors, jfloatArray jpos, jint jtm, jlong native_lm) {
const GradientData gdata(env, jcolors, jpos, jtm, native_lm);
auto shader = SkGradientShader::MakeRadial({x,y}, r,
gdata.colors(),
nullptr,
gdata.pos(),
gdata.count(),
gdata.tileMode(),
0,
&gdata.localMatrix());
return reinterpret_cast<jlong>(shader.release());
}
static jlong MakeTwoPointConical(JNIEnv* env, jobject,
jfloat x0, jfloat y0, jfloat r0,
jfloat x1, jfloat y1, jfloat r1,
jfloatArray jcolors, jfloatArray jpos, jint jtm, jlong native_lm) {
const GradientData gdata(env, jcolors, jpos, jtm, native_lm);
auto shader = SkGradientShader::MakeTwoPointConical({x0,y0}, r0,
{x1,y1}, r1,
gdata.colors(),
nullptr,
gdata.pos(),
gdata.count(),
gdata.tileMode(),
0,
&gdata.localMatrix());
return reinterpret_cast<jlong>(shader.release());
}
static jlong MakeSweep(JNIEnv* env, jobject, jfloat x, jfloat y, jfloat sa, jfloat ea,
jfloatArray jcolors, jfloatArray jpos, jint jtm, jlong native_lm) {
const GradientData gdata(env, jcolors, jpos, jtm, native_lm);
auto shader = SkGradientShader::MakeSweep(x, y,
gdata.colors(),
nullptr,
gdata.pos(),
gdata.count(),
gdata.tileMode(),
sa, ea,
0,
&gdata.localMatrix());
return reinterpret_cast<jlong>(shader.release());
}
@ -51,3 +134,36 @@ int register_androidkit_LinearGradient(JNIEnv* env) {
? env->RegisterNatives(clazz, methods, SK_ARRAY_COUNT(methods))
: JNI_ERR;
}
int register_androidkit_RadialGradient(JNIEnv* env) {
static const JNINativeMethod methods[] = {
{"nMakeRadial", "(FFF[F[FIJ)J", reinterpret_cast<void*>(MakeRadial)},
};
const auto clazz = env->FindClass("org/skia/androidkit/RadialGradient");
return clazz
? env->RegisterNatives(clazz, methods, SK_ARRAY_COUNT(methods))
: JNI_ERR;
}
int register_androidkit_TwoPointConicalGradient(JNIEnv* env) {
static const JNINativeMethod methods[] = {
{"nMakeTwoPointConical", "(FFFFFF[F[FIJ)J", reinterpret_cast<void*>(MakeTwoPointConical)},
};
const auto clazz = env->FindClass("org/skia/androidkit/TwoPointConicalGradient");
return clazz
? env->RegisterNatives(clazz, methods, SK_ARRAY_COUNT(methods))
: JNI_ERR;
}
int register_androidkit_SweepGradient(JNIEnv* env) {
static const JNINativeMethod methods[] = {
{"nMakeSweep", "(FFFF[F[FIJ)J", reinterpret_cast<void*>(MakeSweep)},
};
const auto clazz = env->FindClass("org/skia/androidkit/SweepGradient");
return clazz
? env->RegisterNatives(clazz, methods, SK_ARRAY_COUNT(methods))
: JNI_ERR;
}

View File

@ -0,0 +1,58 @@
/*
* 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 android.support.annotation.Nullable;
import java.lang.IllegalArgumentException;
public class Gradient extends Shader {
protected interface GradientFactory {
long make(float[] colors, float[] pos, int tileMode, long nativeLocalMatrix);
}
protected Gradient(int[] colors, float[] pos, TileMode tm, Matrix lm,
GradientFactory gf) throws IllegalArgumentException {
super(makeGradient(colors, pos, tm, lm, gf));
}
protected Gradient(float[] colors, float[] pos, TileMode tm, Matrix lm,
GradientFactory gf) throws IllegalArgumentException {
super(makeGradient(colors, pos, tm, lm, gf));
}
private static long makeGradient(int[] colors, float[] pos, TileMode tm, Matrix lm,
GradientFactory gf) throws IllegalArgumentException {
if (colors.length != pos.length) {
throw new IllegalArgumentException("Expecting equal-length colors and positions.");
}
float[] fcolors = new float[colors.length * 4];
for (int i = 0; i < colors.length; ++i) {
fcolors[4*i + 0] = ((colors[i] >> 16) & 0xff) / 255.0f;
fcolors[4*i + 1] = ((colors[i] >> 8) & 0xff) / 255.0f;
fcolors[4*i + 2] = ((colors[i] >> 0) & 0xff) / 255.0f;
fcolors[4*i + 3] = ((colors[i] >> 24) & 0xff) / 255.0f;
}
return gf.make(fcolors, pos, tm.ordinal(), lm != null ? lm.getNativeInstance() : 0);
}
private static long makeGradient(float[] colors, float[] pos, TileMode tm, Matrix lm,
GradientFactory gf) throws IllegalArgumentException {
if (colors.length % 4 != 0) {
throw new IllegalArgumentException("Colors length must be a multiple of 4.");
}
if (colors.length / 4 != pos.length) {
throw new IllegalArgumentException("Colors must be 4x positions length.");
}
return gf.make(colors, pos, tm.ordinal(), lm != null ? lm.getNativeInstance() : 0);
}
}

View File

@ -10,39 +10,32 @@ package org.skia.androidkit;
import android.support.annotation.Nullable;
import java.lang.IllegalArgumentException;
public class LinearGradient extends Shader {
public LinearGradient(float x0, float y0, float x1, float y1, Color[] colors,
public class LinearGradient extends Gradient {
public LinearGradient(float x0, float y0, float x1, float y1, int[] colors,
float[] pos, TileMode tm,
@Nullable Matrix localMatrix) throws IllegalArgumentException {
super(makeNative(x0, y0, x1, y1, colors, pos, tm, localMatrix));
super(colors, pos, tm, localMatrix,
(c, p, t, m) -> nMakeLinear(x0, y0, x1, y1, c, p, t, m));
}
public LinearGradient(float x0, float y0, float x1, float y1, Color[] colors,
public LinearGradient(float x0, float y0, float x1, float y1, int[] colors,
float[] pos, TileMode tm) throws IllegalArgumentException {
this(x0, y0, x1, y1, colors, pos, tm, null);
}
private static long makeNative(float x0, float y0, float x1, float y1, Color[] colors,
float[] pos, TileMode tm,
@Nullable Matrix localMatrix) throws IllegalArgumentException {
if (colors.length != pos.length) {
throw new IllegalArgumentException("Expecting equal-length colors and positions.");
}
public LinearGradient(float x0, float y0, float x1, float y1, float[] colors,
float[] pos, TileMode tm,
@Nullable Matrix localMatrix) throws IllegalArgumentException {
super(colors, pos, tm, localMatrix,
(c, p, t, m) -> nMakeLinear(x0, y0, x1, y1, c, p, t, m));
}
float[] fcolors = new float[colors.length * 4];
for (int i = 0; i < colors.length; ++i) {
fcolors[4*i + 0] = colors[i].r();
fcolors[4*i + 1] = colors[i].g();
fcolors[4*i + 2] = colors[i].b();
fcolors[4*i + 3] = colors[i].a();
}
long nativeLocalMatrix = localMatrix != null ? localMatrix.getNativeInstance() : 0;
return nMakeLinear(x0, y0, x1, y1, fcolors, pos, tm.ordinal(), nativeLocalMatrix);
public LinearGradient(float x0, float y0, float x1, float y1, float[] colors,
float[] pos, TileMode tm) throws IllegalArgumentException {
this(x0, y0, x1, y1, colors, pos, tm, null);
}
private static native long nMakeLinear(float x0, float y0, float x1, float y1,
float[] colors, float[] pos, int tilemode,
long nativeLocalMatrix);
}
}

View File

@ -0,0 +1,38 @@
/*
* 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 android.support.annotation.Nullable;
import java.lang.IllegalArgumentException;
public class RadialGradient extends Gradient {
public RadialGradient(float x, float y, float r, int[] colors, float[] pos, TileMode tm,
@Nullable Matrix localMatrix) throws IllegalArgumentException {
super(colors, pos, tm, localMatrix,
(c, p, t, m) -> nMakeRadial(x, y, r, c, p, t, m));
}
public RadialGradient(float x, float y, float r, int[] colors,
float[] pos, TileMode tm) throws IllegalArgumentException {
this(x, y, r, colors, pos, tm, null);
}
public RadialGradient(float x, float y, float r, float[] colors, float[] pos, TileMode tm,
@Nullable Matrix localMatrix) throws IllegalArgumentException {
super(colors, pos, tm, localMatrix,
(c, p, t, m) -> nMakeRadial(x, y, r, c, p, t, m));
}
public RadialGradient(float x, float y, float r, float[] colors,
float[] pos, TileMode tm) throws IllegalArgumentException {
this(x, y, r, colors, pos, tm, null);
}
private static native long nMakeRadial(float x, float y, float r, float[] colors, float[] pos,
int tilemode, long nativeLocalMatrix);
}

View File

@ -0,0 +1,51 @@
/*
* 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 android.support.annotation.Nullable;
import java.lang.IllegalArgumentException;
public class SweepGradient extends Gradient {
public SweepGradient(float x, float y, float startAngle, float endAngle,
int[] colors, float[] pos, TileMode tm,
@Nullable Matrix localMatrix) throws IllegalArgumentException {
super(colors, pos, tm, localMatrix,
(c, p, t, m) -> nMakeSweep(x, y, startAngle, endAngle, c, p, t, m));
}
public SweepGradient(float x, float y, float startAngle, float endAngle,
int[] colors, float[] pos, TileMode tm) throws IllegalArgumentException {
this(x, y, startAngle, endAngle, colors, pos, tm, null);
}
public SweepGradient(float x, float y, int[] colors,
float[] pos) throws IllegalArgumentException {
this(x, y, 0, 360, colors, pos, TileMode.CLAMP, null);
}
public SweepGradient(float x, float y, float startAngle, float endAngle,
float[] colors, float[] pos, TileMode tm,
@Nullable Matrix localMatrix) throws IllegalArgumentException {
super(colors, pos, tm, localMatrix,
(c, p, t, m) -> nMakeSweep(x, y, startAngle, endAngle, c, p, t, m));
}
public SweepGradient(float x, float y, float startAngle, float endAngle,
float[] colors, float[] pos, TileMode tm) throws IllegalArgumentException {
this(x, y, startAngle, endAngle, colors, pos, tm, null);
}
public SweepGradient(float x, float y, float[] colors,
float[] pos) throws IllegalArgumentException {
this(x, y, 0, 360, colors, pos, TileMode.CLAMP, null);
}
private static native long nMakeSweep(float x, float y, float sa, float ea,
float[] colors, float[] pos,
int tilemode, long nativeLocalMatrix);
}

View File

@ -0,0 +1,44 @@
/*
* 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 android.support.annotation.Nullable;
import java.lang.IllegalArgumentException;
public class TwoPointConicalGradient extends Gradient {
public TwoPointConicalGradient(float x0, float y0, float r0, float x1, float y1, float r1,
int[] colors, float[] pos, TileMode tm,
@Nullable Matrix localMatrix) throws IllegalArgumentException {
super(colors, pos, tm, localMatrix,
(c, p, t, m) -> nMakeTwoPointConical(x0, y0, r0, x1, y1, r1, c, p, t, m));
}
public TwoPointConicalGradient(float x0, float y0, float r0, float x1, float y1, float r1,
int[] colors, float[] pos,
TileMode tm) throws IllegalArgumentException {
this(x0, y0, r0, x1, y1, r1, colors, pos, tm, null);
}
public TwoPointConicalGradient(float x0, float y0, float r0, float x1, float y1, float r1,
float[] colors, float[] pos, TileMode tm,
@Nullable Matrix localMatrix) throws IllegalArgumentException {
super(colors, pos, tm, localMatrix,
(c, p, t, m) -> nMakeTwoPointConical(x0, y0, r0, x1, y1, r1, c, p, t, m));
}
public TwoPointConicalGradient(float x0, float y0, float r0, float x1, float y1, float r1,
float[] colors, float[] pos,
TileMode tm) throws IllegalArgumentException {
this(x0, y0, r0, x1, y1, r1, colors, pos, tm, null);
}
private static native long nMakeTwoPointConical(float x0, float y0, float r0,
float x1, float y1, float r1,
float[] colors, float[] pos, int tilemode,
long nativeLocalMatrix);
}

View File

@ -15,8 +15,38 @@ import org.skia.androidkit.*;
import org.skia.androidkit.util.*;
class AnimationRenderer extends SurfaceRenderer {
private Shader mLinearGradient,
mRadialGradient,
mConicalGradient,
mSweepGradient;
@Override
protected void onSurfaceInitialized(Surface surface) {}
protected void onSurfaceInitialized(Surface surface) {
float sw = surface.getWidth(),
sh = surface.getHeight();
float[] colors1 = {
1,0,0,1,
0,1,0,1,
0,0,1,1
};
int[] colors2 = {
0xffffff00,
0xff00ffff,
0xffff00ff
};
float[] pos = {0, 0.5f, 1};
mLinearGradient = new LinearGradient(0, 0, sw, 0,
colors1, pos, TileMode.CLAMP);
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,
sw/2, sh/2, sw/2,
colors1, pos, TileMode.MIRROR);
mSweepGradient = new SweepGradient(sw/2, sh/4, 0, 90, colors2, pos, TileMode.REPEAT);
}
@Override
protected void onRenderFrame(Canvas canvas, long ms) {
@ -26,18 +56,23 @@ class AnimationRenderer extends SurfaceRenderer {
canvas.drawColor(0xffffffe0);
Color[] colors = { new Color(1,0,0,1),
new Color(0,1,0,1),
new Color(0,0,1,1)};
float[] pos = {0, 0.5f, 1};
Shader gradient = new LinearGradient(0, 0, canvas.getWidth(), 0,
colors, pos, TileMode.CLAMP);
float cw = canvas.getWidth(),
ch = canvas.getHeight(),
osc1 = (float)(java.lang.Math.cos(ms * kSpeed / 1000)),
osc2 = (float)(java.lang.Math.sin(ms * kSpeed / 1000));
Paint p = new Paint().setShader(gradient);
drawRect(canvas, (1 + osc1)*cw/2, ch/2, mLinearGradient);
drawRect(canvas, (1 - osc1)*cw/2, ch/2, mConicalGradient);
drawRect(canvas, cw/2, (1 + osc2)*ch/2, mRadialGradient);
drawRect(canvas, cw/2, (1 - osc2)*ch/2, mSweepGradient);
}
float x = (float)(java.lang.Math.cos(ms * kSpeed / 1000) + 1) * canvas.getWidth()/2;
canvas.drawRect(x - kWidth/2, (canvas.getHeight() - kHeight)/2,
x + kWidth/2, (canvas.getHeight() + kHeight)/2, p);
private void drawRect(Canvas canvas, float cx, float cy, Shader shader) {
final float kWidth = 400,
kHeight = 200;
canvas.drawRect(cx - kWidth/2, cy - kHeight/2, cx + kWidth/2, cy + kHeight/2,
new Paint().setShader(shader));
}
}