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