diff --git a/api/current.txt b/api/current.txt
index 6b42af82000ec..3aa736a82b8a6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -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);
}
diff --git a/graphics/java/android/graphics/RadialGradient.java b/graphics/java/android/graphics/RadialGradient.java
index 089d6de44adba..864331a9674eb 100644
--- a/graphics/java/android/graphics/RadialGradient.java
+++ b/graphics/java/android/graphics/RadialGradient.java
@@ -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 null. Valid values are between 0.0f and
+ * 1.0f. The relative position of each corresponding color in
+ * the colors array. If null, 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);
}
diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp
index e36e35504494e..45795ff255aa6 100644
--- a/libs/hwui/jni/Shader.cpp
+++ b/libs/hwui/jni/Shader.cpp
@@ -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 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 shader = SkGradientShader::MakeRadial(center, radius, &colors[0],
- GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(),
- static_cast(tileMode), sGradientShaderFlags, nullptr);
+ auto colorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
+ auto skTileMode = static_cast(tileMode);
+ sk_sp 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(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[] = {
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index 45d22144fe8f4..dad997bc22d62 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -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);
}