Added support for focal points in RadialGradient

Updated RadialGradient API to accept optional starting
(focal) point parameters. Internally create a two point
conical shader if focal points are provided.

Fixes: 167695230
Test: Added CTS test to verify focal points on RadialGradient
Change-Id: I18baba3d0a7285c3f787af8f441c797414bae2cf
This commit is contained in:
Nader Jawad
2020-10-16 18:00:04 -07:00
parent 07e6258728
commit e7ab841f37
4 changed files with 104 additions and 25 deletions

View File

@@ -15443,6 +15443,7 @@ package android.graphics {
public class RadialGradient extends android.graphics.Shader {
ctor public RadialGradient(float, float, float, @ColorInt @NonNull int[], @Nullable float[], @NonNull android.graphics.Shader.TileMode);
ctor public RadialGradient(float, float, float, @ColorLong @NonNull long[], @Nullable float[], @NonNull android.graphics.Shader.TileMode);
ctor public RadialGradient(float, float, @FloatRange(from=0.0f) float, float, float, @FloatRange(from=0.0f, fromInclusive=false) float, @ColorLong @NonNull long[], @Nullable float[], @NonNull android.graphics.Shader.TileMode);
ctor public RadialGradient(float, float, float, @ColorInt int, @ColorInt int, @NonNull android.graphics.Shader.TileMode);
ctor public RadialGradient(float, float, float, @ColorLong long, @ColorLong long, @NonNull android.graphics.Shader.TileMode);
}

View File

@@ -18,6 +18,7 @@ package android.graphics;
import android.annotation.ColorInt;
import android.annotation.ColorLong;
import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
@@ -34,6 +35,10 @@ public class RadialGradient extends Shader {
@UnsupportedAppUsage
private TileMode mTileMode;
private final float mFocalX;
private final float mFocalY;
private final float mFocalRadius;
// @ColorInts are replaced by @ColorLongs, but these remain due to @UnsupportedAppUsage.
@UnsupportedAppUsage
@ColorInt
@@ -64,8 +69,8 @@ public class RadialGradient extends Shader {
public RadialGradient(float centerX, float centerY, float radius,
@NonNull @ColorInt int[] colors, @Nullable float[] stops,
@NonNull TileMode tileMode) {
this(centerX, centerY, radius, convertColors(colors), stops, tileMode,
ColorSpace.get(ColorSpace.Named.SRGB));
this(centerX, centerY, 0f, centerX, centerY, radius, convertColors(colors),
stops, tileMode, ColorSpace.get(ColorSpace.Named.SRGB));
}
/**
@@ -88,27 +93,79 @@ public class RadialGradient extends Shader {
public RadialGradient(float centerX, float centerY, float radius,
@NonNull @ColorLong long[] colors, @Nullable float[] stops,
@NonNull TileMode tileMode) {
this(centerX, centerY, radius, colors.clone(), stops, tileMode, detectColorSpace(colors));
this(centerX, centerY, 0f, centerX, centerY, radius, colors.clone(), stops,
tileMode, detectColorSpace(colors));
}
/**
* Create a shader that draws a radial gradient given the start and end points as well as
* starting and ending radii. The starting point is often referred to as the focal center and
* represents the starting circle of the radial gradient.
*
* @param startX The x-coordinate of the center of the starting circle of the radial gradient,
* often referred to as the focal point.
* @param startY The y-coordinate of the center of the starting circle of the radial gradient,
* often referred to as the focal point.
* @param startRadius The radius of the starting circle of the radial gradient, often referred
* to as the focal radius. Must be greater than or equal to zero.
* @param endX The x-coordinate of the center of the radius for the end circle of the
* radial gradient
* @param endY The y-coordinate of the center of the radius for the end circle of the
* radial gradient
* @param endRadius The radius of the ending circle for this gradient. This must be strictly
* greater than zero. A radius value equal to zero is not allowed.
* @param colors The colors to be distributed between the center and edge of the circle
* @param stops May be <code>null</code>. Valid values are between <code>0.0f</code> and
* <code>1.0f</code>. The relative position of each corresponding color in
* the colors array. If <code>null</code>, colors are distributed evenly
* between the center and edge of the circle.
* @param tileMode The Shader tiling mode
*
* @throws IllegalArgumentException If one of the following circumstances:
* - There are less than two colors
* - The colors do not share the same {@link ColorSpace}
* - The colors do not use a valid {@link ColorSpace}
* - The {@code stops} parameter is not {@code null} and has a different length
* from {@code colors}.
* - The {@param startRadius} is negative
* - The {@param endRadius} is less than or equal to zero
*/
public RadialGradient(float startX, float startY, @FloatRange(from = 0.0f) float startRadius,
float endX, float endY, @FloatRange(from = 0.0f, fromInclusive = false) float endRadius,
@NonNull @ColorLong long[] colors, @Nullable float[] stops,
@NonNull TileMode tileMode) {
this(startX, startY, startRadius, endX, endY, endRadius, colors.clone(), stops, tileMode,
detectColorSpace(colors));
}
/**
* Base constructor. Assumes @param colors is a copy that this object can hold onto,
* and all colors share @param colorSpace.
*/
private RadialGradient(float centerX, float centerY, float radius,
@NonNull @ColorLong long[] colors, @Nullable float[] stops,
@NonNull TileMode tileMode, ColorSpace colorSpace) {
private RadialGradient(float startX, float startY, float startRadius, float endX, float endY,
float endRadius, @NonNull @ColorLong long[] colors, @Nullable float[] stops,
@NonNull TileMode tileMode, ColorSpace colorSpace
) {
super(colorSpace);
if (radius <= 0) {
throw new IllegalArgumentException("radius must be > 0");
// A focal or starting radius of zero with a focal point that matches the center is
// identical to a regular radial gradient
if (startRadius < 0) {
throw new IllegalArgumentException("starting/focal radius must be >= 0");
}
if (endRadius <= 0) {
throw new IllegalArgumentException("ending radius must be > 0");
}
if (stops != null && colors.length != stops.length) {
throw new IllegalArgumentException("color and position arrays must be of equal length");
}
mX = centerX;
mY = centerY;
mRadius = radius;
mX = endX;
mY = endY;
mRadius = endRadius;
mFocalX = startX;
mFocalY = startY;
mFocalRadius = startRadius;
mColorLongs = colors;
mPositions = stops != null ? stops.clone() : null;
mTileMode = tileMode;
@@ -150,12 +207,12 @@ public class RadialGradient extends Shader {
/** @hide */
@Override
protected long createNativeInstance(long nativeMatrix) {
return nativeCreate(nativeMatrix, mX, mY, mRadius,
mColorLongs, mPositions, mTileMode.nativeInt,
colorSpace().getNativeInstance());
return nativeCreate(nativeMatrix, mFocalX, mFocalY, mFocalRadius, mX, mY, mRadius,
mColorLongs, mPositions, mTileMode.nativeInt, colorSpace().getNativeInstance());
}
private static native long nativeCreate(long matrix, float x, float y, float radius,
@ColorLong long[] colors, float[] positions, int tileMode, long colorSpaceHandle);
private static native long nativeCreate(long matrix, float startX, float startY,
float startRadius, float endX, float endY, float endRadius, @ColorLong long[] colors,
float[] positions, int tileMode, long colorSpaceHandle);
}

View File

@@ -133,11 +133,25 @@ static jlong LinearGradient_create(JNIEnv* env, jobject, jlong matrixPtr,
///////////////////////////////////////////////////////////////////////////////////////////////
static jlong RadialGradient_create(JNIEnv* env, jobject, jlong matrixPtr, jfloat x, jfloat y,
jfloat radius, jlongArray colorArray, jfloatArray posArray, jint tileMode,
static jlong RadialGradient_create(JNIEnv* env,
jobject,
jlong matrixPtr,
jfloat startX,
jfloat startY,
jfloat startRadius,
jfloat endX,
jfloat endY,
jfloat endRadius,
jlongArray colorArray,
jfloatArray posArray,
jint tileMode,
jlong colorSpaceHandle) {
SkPoint center;
center.set(x, y);
SkPoint start;
start.set(startX, startY);
SkPoint end;
end.set(endX, endY);
std::vector<SkColor4f> colors = convertColorLongs(env, colorArray);
@@ -148,11 +162,17 @@ static jlong RadialGradient_create(JNIEnv* env, jobject, jlong matrixPtr, jfloat
#error Need to convert float array to SkScalar array before calling the following function.
#endif
sk_sp<SkShader> shader = SkGradientShader::MakeRadial(center, radius, &colors[0],
GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(),
static_cast<SkTileMode>(tileMode), sGradientShaderFlags, nullptr);
auto colorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
auto skTileMode = static_cast<SkTileMode>(tileMode);
sk_sp<SkShader> shader = SkGradientShader::MakeTwoPointConical(start, startRadius, end,
endRadius, &colors[0], std::move(colorSpace), pos, colors.size(), skTileMode,
sGradientShaderFlags, nullptr);
ThrowIAE_IfNull(env, shader);
// Explicitly create a new shader with the specified matrix to match existing behavior.
// Passing in the matrix in the instantiation above can throw exceptions for non-invertible
// matrices. However, makeWithLocalMatrix will still allow for the shader to be created
// and skia handles null-shaders internally (i.e. is ignored)
const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
if (matrix) {
shader = shader->makeWithLocalMatrix(*matrix);
@@ -279,7 +299,7 @@ static const JNINativeMethod gLinearGradientMethods[] = {
};
static const JNINativeMethod gRadialGradientMethods[] = {
{ "nativeCreate", "(JFFF[J[FIJ)J", (void*)RadialGradient_create },
{ "nativeCreate", "(JFFFFFF[J[FIJ)J", (void*)RadialGradient_create },
};
static const JNINativeMethod gSweepGradientMethods[] = {

View File

@@ -15425,6 +15425,7 @@ package android.graphics {
public class RadialGradient extends android.graphics.Shader {
ctor public RadialGradient(float, float, float, @ColorInt @NonNull int[], @Nullable float[], @NonNull android.graphics.Shader.TileMode);
ctor public RadialGradient(float, float, float, @ColorLong @NonNull long[], @Nullable float[], @NonNull android.graphics.Shader.TileMode);
ctor public RadialGradient(float, float, @FloatRange(from=0.0f) float, float, float, @FloatRange(from=0.0f, fromInclusive=false) float, @ColorLong @NonNull long[], @Nullable float[], @NonNull android.graphics.Shader.TileMode);
ctor public RadialGradient(float, float, float, @ColorInt int, @ColorInt int, @NonNull android.graphics.Shader.TileMode);
ctor public RadialGradient(float, float, float, @ColorLong long, @ColorLong long, @NonNull android.graphics.Shader.TileMode);
}