Merge "Use ColorLongs in Linear-, Radial-, and Sweep-Gradient"
diff --git a/api/current.txt b/api/current.txt
index a8d96ec..069fa99 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -14249,7 +14249,9 @@
public class LinearGradient extends android.graphics.Shader {
ctor public LinearGradient(float, float, float, float, @NonNull @ColorInt int[], @Nullable float[], @NonNull android.graphics.Shader.TileMode);
+ ctor public LinearGradient(float, float, float, float, @NonNull @ColorLong long[], @Nullable float[], @NonNull android.graphics.Shader.TileMode);
ctor public LinearGradient(float, float, float, float, @ColorInt int, @ColorInt int, @NonNull android.graphics.Shader.TileMode);
+ ctor public LinearGradient(float, float, float, float, @ColorLong long, @ColorLong long, @NonNull android.graphics.Shader.TileMode);
}
public class MaskFilter {
@@ -14778,7 +14780,9 @@
public class RadialGradient extends android.graphics.Shader {
ctor public RadialGradient(float, float, float, @NonNull @ColorInt int[], @Nullable float[], @NonNull android.graphics.Shader.TileMode);
+ ctor public RadialGradient(float, float, float, @NonNull @ColorLong 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);
}
public final class RecordingCanvas extends android.graphics.Canvas {
@@ -15038,7 +15042,9 @@
public class SweepGradient extends android.graphics.Shader {
ctor public SweepGradient(float, float, @NonNull @ColorInt int[], @Nullable float[]);
+ ctor public SweepGradient(float, float, @NonNull @ColorLong long[], @Nullable float[]);
ctor public SweepGradient(float, float, @ColorInt int, @ColorInt int);
+ ctor public SweepGradient(float, float, @ColorLong long, @ColorLong long);
}
public class Typeface {
diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp
index ed6a84b..e741979 100644
--- a/core/jni/android/graphics/Shader.cpp
+++ b/core/jni/android/graphics/Shader.cpp
@@ -8,6 +8,8 @@
#include <jni.h>
+#include <vector>
+
using namespace android::uirenderer;
/**
@@ -18,11 +20,11 @@
*/
static const uint32_t sGradientShaderFlags = SkGradientShader::kInterpolateColorsInPremul_Flag;
-static void ThrowIAE_IfNull(JNIEnv* env, void* ptr) {
- if (NULL == ptr) {
- doThrowIAE(env);
+#define ThrowIAE_IfNull(env, ptr) \
+ if (nullptr == ptr) { \
+ doThrowIAE(env); \
+ return 0; \
}
-}
static void Color_RGBToHSV(JNIEnv* env, jobject, jint red, jint green, jint blue, jfloatArray hsvArray)
{
@@ -76,186 +78,115 @@
}
sk_sp<SkShader> shader = image->makeShader(
(SkShader::TileMode)tileModeX, (SkShader::TileMode)tileModeY);
+ ThrowIAE_IfNull(env, shader.get());
if (matrix) {
shader = shader->makeWithLocalMatrix(*matrix);
}
- ThrowIAE_IfNull(env, shader.get());
return reinterpret_cast<jlong>(shader.release());
}
///////////////////////////////////////////////////////////////////////////////////////////////
-static jlong LinearGradient_create1(JNIEnv* env, jobject o, jlong matrixPtr,
- jfloat x0, jfloat y0, jfloat x1, jfloat y1,
- jintArray colorArray, jfloatArray posArray, jint tileMode) {
- const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
- SkPoint pts[2];
- pts[0].set(x0, y0);
- pts[1].set(x1, y1);
+static std::vector<SkColor4f> convertColorLongs(JNIEnv* env, jlongArray colorArray) {
+ const size_t count = env->GetArrayLength(colorArray);
+ const jlong* colorValues = env->GetLongArrayElements(colorArray, nullptr);
- size_t count = env->GetArrayLength(colorArray);
- const jint* colorValues = env->GetIntArrayElements(colorArray, NULL);
-
- AutoJavaFloatArray autoPos(env, posArray, count);
-#ifdef SK_SCALAR_IS_FLOAT
- SkScalar* pos = autoPos.ptr();
-#else
- #error Need to convert float array to SkScalar array before calling the following function.
-#endif
-
- sk_sp<SkShader> baseShader(SkGradientShader::MakeLinear(pts,
- reinterpret_cast<const SkColor*>(colorValues), pos, count,
- static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, NULL));
-
- SkShader* shader;
- if (matrix) {
- shader = baseShader->makeWithLocalMatrix(*matrix).release();
- } else {
- shader = baseShader.release();
+ std::vector<SkColor4f> colors(count);
+ for (size_t i = 0; i < count; ++i) {
+ colors[i] = GraphicsJNI::convertColorLong(colorValues[i]);
}
- env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT);
- ThrowIAE_IfNull(env, shader);
- return reinterpret_cast<jlong>(shader);
-}
-
-static jlong LinearGradient_create2(JNIEnv* env, jobject o, jlong matrixPtr,
- jfloat x0, jfloat y0, jfloat x1, jfloat y1, jint color0, jint color1, jint tileMode) {
- const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
-
- SkPoint pts[2];
- pts[0].set(x0, y0);
- pts[1].set(x1, y1);
-
- SkColor colors[2];
- colors[0] = color0;
- colors[1] = color1;
-
- sk_sp<SkShader> baseShader(SkGradientShader::MakeLinear(pts, colors, NULL, 2,
- static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, NULL));
-
- SkShader* s;
- if (matrix) {
- s = baseShader->makeWithLocalMatrix(*matrix).release();
- } else {
- s = baseShader.release();
- }
-
- ThrowIAE_IfNull(env, s);
- return reinterpret_cast<jlong>(s);
+ env->ReleaseLongArrayElements(colorArray, const_cast<jlong*>(colorValues), JNI_ABORT);
+ return colors;
}
///////////////////////////////////////////////////////////////////////////////////////////////
-static jlong RadialGradient_create1(JNIEnv* env, jobject, jlong matrixPtr, jfloat x, jfloat y,
- jfloat radius, jintArray colorArray, jfloatArray posArray, jint tileMode) {
- const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
- SkPoint center;
- center.set(x, y);
+static jlong LinearGradient_create(JNIEnv* env, jobject, jlong matrixPtr,
+ jfloat x0, jfloat y0, jfloat x1, jfloat y1, jlongArray colorArray,
+ jfloatArray posArray, jint tileMode, long colorSpaceHandle) {
+ SkPoint pts[2];
+ pts[0].set(x0, y0);
+ pts[1].set(x1, y1);
- size_t count = env->GetArrayLength(colorArray);
- const jint* colorValues = env->GetIntArrayElements(colorArray, NULL);
+ std::vector<SkColor4f> colors = convertColorLongs(env, colorArray);
- AutoJavaFloatArray autoPos(env, posArray, count);
+ AutoJavaFloatArray autoPos(env, posArray, colors.size());
#ifdef SK_SCALAR_IS_FLOAT
SkScalar* pos = autoPos.ptr();
#else
#error Need to convert float array to SkScalar array before calling the following function.
#endif
- sk_sp<SkShader> baseShader = SkGradientShader::MakeRadial(center, radius,
- reinterpret_cast<const SkColor*>(colorValues), pos, count,
- static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, NULL);
+ sk_sp<SkShader> shader(SkGradientShader::MakeLinear(pts, &colors[0],
+ GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(),
+ static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, nullptr));
+ ThrowIAE_IfNull(env, shader);
- SkShader* shader;
+ const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
if (matrix) {
- shader = baseShader->makeWithLocalMatrix(*matrix).release();
- } else {
- shader = baseShader.release();
+ shader = shader->makeWithLocalMatrix(*matrix);
}
- env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues),
- JNI_ABORT);
-
- ThrowIAE_IfNull(env, shader);
- return reinterpret_cast<jlong>(shader);
+ return reinterpret_cast<jlong>(shader.release());
}
-static jlong RadialGradient_create2(JNIEnv* env, jobject, jlong matrixPtr, jfloat x, jfloat y, jfloat radius,
- jint color0, jint color1, jint tileMode) {
- const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+static jlong RadialGradient_create(JNIEnv* env, jobject, jlong matrixPtr, jfloat x, jfloat y,
+ jfloat radius, jlongArray colorArray, jfloatArray posArray, jint tileMode,
+ jlong colorSpaceHandle) {
SkPoint center;
center.set(x, y);
- SkColor colors[2];
- colors[0] = color0;
- colors[1] = color1;
+ std::vector<SkColor4f> colors = convertColorLongs(env, colorArray);
- sk_sp<SkShader> baseShader = SkGradientShader::MakeRadial(center, radius, colors, NULL, 2,
- static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, NULL);
+ AutoJavaFloatArray autoPos(env, posArray, colors.size());
+#ifdef SK_SCALAR_IS_FLOAT
+ SkScalar* pos = autoPos.ptr();
+#else
+ #error Need to convert float array to SkScalar array before calling the following function.
+#endif
- SkShader* shader;
- if (matrix) {
- shader = baseShader->makeWithLocalMatrix(*matrix).release();
- } else {
- shader = baseShader.release();
- }
+ sk_sp<SkShader> shader = SkGradientShader::MakeRadial(center, radius, &colors[0],
+ GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(),
+ static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, nullptr);
ThrowIAE_IfNull(env, shader);
- return reinterpret_cast<jlong>(shader);
+
+ const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
+ if (matrix) {
+ shader = shader->makeWithLocalMatrix(*matrix);
+ }
+
+ return reinterpret_cast<jlong>(shader.release());
}
///////////////////////////////////////////////////////////////////////////////
-static jlong SweepGradient_create1(JNIEnv* env, jobject, jlong matrixPtr, jfloat x, jfloat y,
- jintArray jcolors, jfloatArray jpositions) {
- const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
- size_t count = env->GetArrayLength(jcolors);
- const jint* colors = env->GetIntArrayElements(jcolors, NULL);
+static jlong SweepGradient_create(JNIEnv* env, jobject, jlong matrixPtr, jfloat x, jfloat y,
+ jlongArray colorArray, jfloatArray jpositions, jlong colorSpaceHandle) {
+ std::vector<SkColor4f> colors = convertColorLongs(env, colorArray);
- AutoJavaFloatArray autoPos(env, jpositions, count);
+ AutoJavaFloatArray autoPos(env, jpositions, colors.size());
#ifdef SK_SCALAR_IS_FLOAT
SkScalar* pos = autoPos.ptr();
#else
#error Need to convert float array to SkScalar array before calling the following function.
#endif
- sk_sp<SkShader> baseShader = SkGradientShader::MakeSweep(x, y,
- reinterpret_cast<const SkColor*>(colors), pos, count,
- sGradientShaderFlags, NULL);
-
- SkShader* shader;
- if (matrix) {
- shader = baseShader->makeWithLocalMatrix(*matrix).release();
- } else {
- shader = baseShader.release();
- }
-
- env->ReleaseIntArrayElements(jcolors, const_cast<jint*>(colors),
- JNI_ABORT);
+ sk_sp<SkShader> shader = SkGradientShader::MakeSweep(x, y, &colors[0],
+ GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(),
+ sGradientShaderFlags, nullptr);
ThrowIAE_IfNull(env, shader);
- return reinterpret_cast<jlong>(shader);
-}
-static jlong SweepGradient_create2(JNIEnv* env, jobject, jlong matrixPtr, jfloat x, jfloat y,
- int color0, int color1) {
const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
- SkColor colors[2];
- colors[0] = color0;
- colors[1] = color1;
-
- sk_sp<SkShader> baseShader = SkGradientShader::MakeSweep(x, y, colors,
- NULL, 2, sGradientShaderFlags, NULL);
-
- SkShader* shader;
if (matrix) {
- shader = baseShader->makeWithLocalMatrix(*matrix).release();
- } else {
- shader = baseShader.release();
+ shader = shader->makeWithLocalMatrix(*matrix);
}
- ThrowIAE_IfNull(env, shader);
- return reinterpret_cast<jlong>(shader);
+
+ return reinterpret_cast<jlong>(shader.release());
}
///////////////////////////////////////////////////////////////////////////////////////////////
@@ -295,18 +226,15 @@
};
static const JNINativeMethod gLinearGradientMethods[] = {
- { "nativeCreate1", "(JFFFF[I[FI)J", (void*)LinearGradient_create1 },
- { "nativeCreate2", "(JFFFFIII)J", (void*)LinearGradient_create2 },
+ { "nativeCreate", "(JFFFF[J[FIJ)J", (void*)LinearGradient_create },
};
static const JNINativeMethod gRadialGradientMethods[] = {
- { "nativeCreate1", "(JFFF[I[FI)J", (void*)RadialGradient_create1 },
- { "nativeCreate2", "(JFFFIII)J", (void*)RadialGradient_create2 },
+ { "nativeCreate", "(JFFF[J[FIJ)J", (void*)RadialGradient_create },
};
static const JNINativeMethod gSweepGradientMethods[] = {
- { "nativeCreate1", "(JFF[I[F)J", (void*)SweepGradient_create1 },
- { "nativeCreate2", "(JFFII)J", (void*)SweepGradient_create2 },
+ { "nativeCreate", "(JFF[J[FJ)J", (void*)SweepGradient_create },
};
static const JNINativeMethod gComposeShaderMethods[] = {
diff --git a/graphics/java/android/graphics/LinearGradient.java b/graphics/java/android/graphics/LinearGradient.java
index 7e6fc35..ad33f79 100644
--- a/graphics/java/android/graphics/LinearGradient.java
+++ b/graphics/java/android/graphics/LinearGradient.java
@@ -17,21 +17,13 @@
package android.graphics;
import android.annotation.ColorInt;
+import android.annotation.ColorLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
+
public class LinearGradient extends Shader {
-
- private static final int TYPE_COLORS_AND_POSITIONS = 1;
- private static final int TYPE_COLOR_START_AND_COLOR_END = 2;
-
- /**
- * Type of the LinearGradient: can be either TYPE_COLORS_AND_POSITIONS or
- * TYPE_COLOR_START_AND_COLOR_END.
- */
- private int mType;
-
@UnsupportedAppUsage
private float mX0;
@UnsupportedAppUsage
@@ -41,16 +33,43 @@
@UnsupportedAppUsage
private float mY1;
@UnsupportedAppUsage
- private int[] mColors;
- @UnsupportedAppUsage
private float[] mPositions;
@UnsupportedAppUsage
+ private TileMode mTileMode;
+
+ // @ColorInts are replaced by @ColorLongs, but these remain due to @UnsupportedAppUsage.
+ @UnsupportedAppUsage
+ @ColorInt
+ private int[] mColors;
+ @UnsupportedAppUsage
+ @ColorInt
private int mColor0;
@UnsupportedAppUsage
+ @ColorInt
private int mColor1;
- @UnsupportedAppUsage
- private TileMode mTileMode;
+ @ColorLong
+ private final long[] mColorLongs;
+
+
+ /**
+ * Create a shader that draws a linear gradient along a line.
+ *
+ * @param x0 The x-coordinate for the start of the gradient line
+ * @param y0 The y-coordinate for the start of the gradient line
+ * @param x1 The x-coordinate for the end of the gradient line
+ * @param y1 The y-coordinate for the end of the gradient line
+ * @param colors The sRGB colors to be distributed along the gradient line
+ * @param positions May be null. The relative positions [0..1] of
+ * each corresponding color in the colors array. If this is null,
+ * the the colors are distributed evenly along the gradient line.
+ * @param tile The Shader tiling mode
+ */
+ public LinearGradient(float x0, float y0, float x1, float y1, @NonNull @ColorInt int[] colors,
+ @Nullable float[] positions, @NonNull TileMode tile) {
+ this(x0, y0, x1, y1, convertColors(colors), positions, tile,
+ ColorSpace.get(ColorSpace.Named.SRGB));
+ }
/**
* Create a shader that draws a linear gradient along a line.
@@ -64,21 +83,33 @@
* each corresponding color in the colors array. If this is null,
* the the colors are distributed evenly along the gradient line.
* @param tile The Shader tiling mode
- */
- public LinearGradient(float x0, float y0, float x1, float y1, @NonNull @ColorInt int colors[],
- @Nullable float positions[], @NonNull TileMode tile) {
- if (colors.length < 2) {
- throw new IllegalArgumentException("needs >= 2 number of colors");
- }
+ *
+ * @throws IllegalArgumentException if there are less than two colors, the colors do
+ * not share the same {@link ColorSpace} or do not use a valid one, or {@code positions}
+ * is not {@code null} and has a different length from {@code colors}.
+ */
+ public LinearGradient(float x0, float y0, float x1, float y1, @NonNull @ColorLong long[] colors,
+ @Nullable float[] positions, @NonNull TileMode tile) {
+ this(x0, y0, x1, y1, colors.clone(), positions, tile, detectColorSpace(colors));
+ }
+
+ /**
+ * Base constructor. Assumes @param colors is a copy that this object can hold onto,
+ * and all colors share @param colorSpace.
+ */
+ private LinearGradient(float x0, float y0, float x1, float y1,
+ @NonNull @ColorLong long[] colors, @Nullable float[] positions, @NonNull TileMode tile,
+ @NonNull ColorSpace colorSpace) {
+ super(colorSpace);
+
if (positions != null && colors.length != positions.length) {
throw new IllegalArgumentException("color and position arrays must be of equal length");
}
- mType = TYPE_COLORS_AND_POSITIONS;
mX0 = x0;
mY0 = y0;
mX1 = x1;
mY1 = y1;
- mColors = colors.clone();
+ mColorLongs = colors;
mPositions = positions != null ? positions.clone() : null;
mTileMode = tile;
}
@@ -90,34 +121,41 @@
* @param y0 The y-coordinate for the start of the gradient line
* @param x1 The x-coordinate for the end of the gradient line
* @param y1 The y-coordinate for the end of the gradient line
+ * @param color0 The sRGB color at the start of the gradient line.
+ * @param color1 The sRGB color at the end of the gradient line.
+ * @param tile The Shader tiling mode
+ */
+ public LinearGradient(float x0, float y0, float x1, float y1,
+ @ColorInt int color0, @ColorInt int color1,
+ @NonNull TileMode tile) {
+ this(x0, y0, x1, y1, Color.pack(color0), Color.pack(color1), tile);
+ }
+
+ /**
+ * Create a shader that draws a linear gradient along a line.
+ *
+ * @param x0 The x-coordinate for the start of the gradient line
+ * @param y0 The y-coordinate for the start of the gradient line
+ * @param x1 The x-coordinate for the end of the gradient line
+ * @param y1 The y-coordinate for the end of the gradient line
* @param color0 The color at the start of the gradient line.
* @param color1 The color at the end of the gradient line.
* @param tile The Shader tiling mode
- */
+ *
+ * @throws IllegalArgumentException if the colors do
+ * not share the same {@link ColorSpace} or do not use a valid one.
+ */
public LinearGradient(float x0, float y0, float x1, float y1,
- @ColorInt int color0, @ColorInt int color1,
+ @ColorLong long color0, @ColorLong long color1,
@NonNull TileMode tile) {
- mType = TYPE_COLOR_START_AND_COLOR_END;
- mX0 = x0;
- mY0 = y0;
- mX1 = x1;
- mY1 = y1;
- mColor0 = color0;
- mColor1 = color1;
- mColors = null;
- mPositions = null;
- mTileMode = tile;
+ this(x0, y0, x1, y1, new long[] {color0, color1}, null, tile);
}
@Override
long createNativeInstance(long nativeMatrix) {
- if (mType == TYPE_COLORS_AND_POSITIONS) {
- return nativeCreate1(nativeMatrix, mX0, mY0, mX1, mY1,
- mColors, mPositions, mTileMode.nativeInt);
- } else { // TYPE_COLOR_START_AND_COLOR_END
- return nativeCreate2(nativeMatrix, mX0, mY0, mX1, mY1,
- mColor0, mColor1, mTileMode.nativeInt);
- }
+ return nativeCreate(nativeMatrix, mX0, mY0, mX1, mY1,
+ mColorLongs, mPositions, mTileMode.nativeInt,
+ colorSpace().getNativeInstance());
}
/**
@@ -125,19 +163,12 @@
*/
@Override
protected Shader copy() {
- final LinearGradient copy;
- if (mType == TYPE_COLORS_AND_POSITIONS) {
- copy = new LinearGradient(mX0, mY0, mX1, mY1, mColors.clone(),
- mPositions != null ? mPositions.clone() : null, mTileMode);
- } else { // TYPE_COLOR_START_AND_COLOR_END
- copy = new LinearGradient(mX0, mY0, mX1, mY1, mColor0, mColor1, mTileMode);
- }
+ final LinearGradient copy = new LinearGradient(mX0, mY0, mX1, mY1, mColorLongs,
+ mPositions, mTileMode, colorSpace());
copyLocalMatrix(copy);
return copy;
}
- private native long nativeCreate1(long matrix, float x0, float y0, float x1, float y1,
- int colors[], float positions[], int tileMode);
- private native long nativeCreate2(long matrix, float x0, float y0, float x1, float y1,
- int color0, int color1, int tileMode);
+ private native long nativeCreate(long matrix, float x0, float y0, float x1, float y1,
+ long[] colors, float[] positions, int tileMode, long colorSpaceHandle);
}
diff --git a/graphics/java/android/graphics/RadialGradient.java b/graphics/java/android/graphics/RadialGradient.java
index 41d2628..5e16180 100644
--- a/graphics/java/android/graphics/RadialGradient.java
+++ b/graphics/java/android/graphics/RadialGradient.java
@@ -16,22 +16,13 @@
package android.graphics;
+import android.annotation.ColorInt;
+import android.annotation.ColorLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.ColorInt;
import android.annotation.UnsupportedAppUsage;
public class RadialGradient extends Shader {
-
- private static final int TYPE_COLORS_AND_POSITIONS = 1;
- private static final int TYPE_COLOR_CENTER_AND_COLOR_EDGE = 2;
-
- /**
- * Type of the RadialGradient: can be either TYPE_COLORS_AND_POSITIONS or
- * TYPE_COLOR_CENTER_AND_COLOR_EDGE.
- */
- private int mType;
-
@UnsupportedAppUsage
private float mX;
@UnsupportedAppUsage
@@ -39,16 +30,43 @@
@UnsupportedAppUsage
private float mRadius;
@UnsupportedAppUsage
- private int[] mColors;
- @UnsupportedAppUsage
private float[] mPositions;
@UnsupportedAppUsage
+ private TileMode mTileMode;
+
+ // @ColorInts are replaced by @ColorLongs, but these remain due to @UnsupportedAppUsage.
+ @UnsupportedAppUsage
+ @ColorInt
+ private int[] mColors;
+ @UnsupportedAppUsage
+ @ColorInt
private int mCenterColor;
@UnsupportedAppUsage
+ @ColorInt
private int mEdgeColor;
- @UnsupportedAppUsage
- private TileMode mTileMode;
+ @ColorLong
+ private final long[] mColorLongs;
+
+ /**
+ * Create a shader that draws a radial gradient given the center and radius.
+ *
+ * @param centerX The x-coordinate of the center of the radius
+ * @param centerY The y-coordinate of the center of the radius
+ * @param radius Must be positive. The radius of the circle for this gradient.
+ * @param colors The sRGB 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
+ */
+ 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));
+ }
/**
* Create a shader that draws a radial gradient given the center and radius.
@@ -62,24 +80,36 @@
* 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 there are less than two colors, the colors do
+ * not share the same {@link ColorSpace} or do not use a valid one, or {@code stops}
+ * is not {@code null} and has a different length from {@code colors}.
*/
public RadialGradient(float centerX, float centerY, float radius,
- @NonNull @ColorInt int colors[], @Nullable float stops[],
+ @NonNull @ColorLong long[] colors, @Nullable float[] stops,
@NonNull TileMode tileMode) {
+ this(centerX, centerY, radius, 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) {
+ super(colorSpace);
+
if (radius <= 0) {
throw new IllegalArgumentException("radius must be > 0");
}
- if (colors.length < 2) {
- throw new IllegalArgumentException("needs >= 2 number of colors");
- }
if (stops != null && colors.length != stops.length) {
throw new IllegalArgumentException("color and position arrays must be of equal length");
}
- mType = TYPE_COLORS_AND_POSITIONS;
mX = centerX;
mY = centerY;
mRadius = radius;
- mColors = colors.clone();
+ mColorLongs = colors;
mPositions = stops != null ? stops.clone() : null;
mTileMode = tileMode;
}
@@ -90,33 +120,38 @@
* @param centerX The x-coordinate of the center of the radius
* @param centerY The y-coordinate of the center of the radius
* @param radius Must be positive. The radius of the circle for this gradient
+ * @param centerColor The sRGB color at the center of the circle.
+ * @param edgeColor The sRGB color at the edge of the circle.
+ * @param tileMode The Shader tiling mode
+ */
+ public RadialGradient(float centerX, float centerY, float radius,
+ @ColorInt int centerColor, @ColorInt int edgeColor, @NonNull TileMode tileMode) {
+ this(centerX, centerY, radius, Color.pack(centerColor), Color.pack(edgeColor), tileMode);
+ }
+
+ /**
+ * Create a shader that draws a radial gradient given the center and radius.
+ *
+ * @param centerX The x-coordinate of the center of the radius
+ * @param centerY The y-coordinate of the center of the radius
+ * @param radius Must be positive. The radius of the circle for this gradient
* @param centerColor The color at the center of the circle.
* @param edgeColor The color at the edge of the circle.
* @param tileMode The Shader tiling mode
+ *
+ * @throws IllegalArgumentException if the colors do
+ * not share the same {@link ColorSpace} or do not use a valid one.
*/
public RadialGradient(float centerX, float centerY, float radius,
- @ColorInt int centerColor, @ColorInt int edgeColor, @NonNull TileMode tileMode) {
- if (radius <= 0) {
- throw new IllegalArgumentException("radius must be > 0");
- }
- mType = TYPE_COLOR_CENTER_AND_COLOR_EDGE;
- mX = centerX;
- mY = centerY;
- mRadius = radius;
- mCenterColor = centerColor;
- mEdgeColor = edgeColor;
- mTileMode = tileMode;
+ @ColorLong long centerColor, @ColorLong long edgeColor, @NonNull TileMode tileMode) {
+ this(centerX, centerY, radius, new long[] {centerColor, edgeColor}, null, tileMode);
}
@Override
long createNativeInstance(long nativeMatrix) {
- if (mType == TYPE_COLORS_AND_POSITIONS) {
- return nativeCreate1(nativeMatrix, mX, mY, mRadius,
- mColors, mPositions, mTileMode.nativeInt);
- } else { // TYPE_COLOR_CENTER_AND_COLOR_EDGE
- return nativeCreate2(nativeMatrix, mX, mY, mRadius,
- mCenterColor, mEdgeColor, mTileMode.nativeInt);
- }
+ return nativeCreate(nativeMatrix, mX, mY, mRadius,
+ mColorLongs, mPositions, mTileMode.nativeInt,
+ colorSpace().getNativeInstance());
}
/**
@@ -124,20 +159,13 @@
*/
@Override
protected Shader copy() {
- final RadialGradient copy;
- if (mType == TYPE_COLORS_AND_POSITIONS) {
- copy = new RadialGradient(mX, mY, mRadius, mColors.clone(),
- mPositions != null ? mPositions.clone() : null, mTileMode);
- } else { // TYPE_COLOR_CENTER_AND_COLOR_EDGE
- copy = new RadialGradient(mX, mY, mRadius, mCenterColor, mEdgeColor, mTileMode);
- }
+ final RadialGradient copy = new RadialGradient(mX, mY, mRadius, mColorLongs,
+ mPositions, mTileMode, colorSpace());
copyLocalMatrix(copy);
return copy;
}
- private static native long nativeCreate1(long matrix, float x, float y, float radius,
- int colors[], float positions[], int tileMode);
- private static native long nativeCreate2(long matrix, float x, float y, float radius,
- int color0, int color1, int tileMode);
+ private static native long nativeCreate(long matrix, float x, float y, float radius,
+ @ColorLong long[] colors, float[] positions, int tileMode, long colorSpaceHandle);
}
diff --git a/graphics/java/android/graphics/Shader.java b/graphics/java/android/graphics/Shader.java
index 40bcc9e..7f09786 100644
--- a/graphics/java/android/graphics/Shader.java
+++ b/graphics/java/android/graphics/Shader.java
@@ -16,6 +16,8 @@
package android.graphics;
+import android.annotation.ColorInt;
+import android.annotation.ColorLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
@@ -39,7 +41,32 @@
* @deprecated Use subclass constructors directly instead.
*/
@Deprecated
- public Shader() {}
+ public Shader() {
+ mColorSpace = null;
+ }
+
+ /**
+ * @hide
+ */
+ public Shader(ColorSpace colorSpace) {
+ mColorSpace = colorSpace;
+ if (colorSpace == null) {
+ throw new IllegalArgumentException(
+ "Use Shader() to create a Shader with no ColorSpace");
+ }
+
+ // This just ensures that if the ColorSpace is invalid, the Exception will be thrown now.
+ mColorSpace.getNativeInstance();
+ }
+
+ private final ColorSpace mColorSpace;
+
+ /**
+ * @hide
+ */
+ protected ColorSpace colorSpace() {
+ return mColorSpace;
+ }
/**
* Current native shader instance. Created and updated lazily when {@link #getNativeInstance()}
@@ -169,6 +196,43 @@
return mNativeInstance;
}
+ /**
+ * @hide
+ */
+ public static @ColorLong long[] convertColors(@NonNull @ColorInt int[] colors) {
+ if (colors.length < 2) {
+ throw new IllegalArgumentException("needs >= 2 number of colors");
+ }
+
+ long[] colorLongs = new long[colors.length];
+ for (int i = 0; i < colors.length; ++i) {
+ colorLongs[i] = Color.pack(colors[i]);
+ }
+
+ return colorLongs;
+ }
+
+ /**
+ * Detect the ColorSpace that the {@code colors} share.
+ *
+ * @throws IllegalArgumentException if the colors do not all share the same,
+ * valid ColorSpace, or if there are less than 2 colors.
+ *
+ * @hide
+ */
+ public static ColorSpace detectColorSpace(@NonNull @ColorLong long[] colors) {
+ if (colors.length < 2) {
+ throw new IllegalArgumentException("needs >= 2 number of colors");
+ }
+ final ColorSpace colorSpace = Color.colorSpace(colors[0]);
+ for (int i = 1; i < colors.length; ++i) {
+ if (Color.colorSpace(colors[i]) != colorSpace) {
+ throw new IllegalArgumentException("All colors must be in the same ColorSpace!");
+ }
+ }
+ return colorSpace;
+ }
+
private static native long nativeGetFinalizer();
}
diff --git a/graphics/java/android/graphics/SweepGradient.java b/graphics/java/android/graphics/SweepGradient.java
index f944d85..fc386d7 100644
--- a/graphics/java/android/graphics/SweepGradient.java
+++ b/graphics/java/android/graphics/SweepGradient.java
@@ -17,34 +17,52 @@
package android.graphics;
import android.annotation.ColorInt;
+import android.annotation.ColorLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
public class SweepGradient extends Shader {
-
- private static final int TYPE_COLORS_AND_POSITIONS = 1;
- private static final int TYPE_COLOR_START_AND_COLOR_END = 2;
-
- /**
- * Type of the LinearGradient: can be either TYPE_COLORS_AND_POSITIONS or
- * TYPE_COLOR_START_AND_COLOR_END.
- */
- private int mType;
-
@UnsupportedAppUsage
private float mCx;
@UnsupportedAppUsage
private float mCy;
@UnsupportedAppUsage
+ private float[] mPositions;
+
+ // @ColorInts are replaced by @ColorLongs, but these remain due to @UnsupportedAppUsage.
+ @UnsupportedAppUsage
+ @ColorInt
private int[] mColors;
@UnsupportedAppUsage
- private float[] mPositions;
- @UnsupportedAppUsage
+ @ColorInt
private int mColor0;
@UnsupportedAppUsage
+ @ColorInt
private int mColor1;
+ @ColorLong
+ private final long[] mColorLongs;
+
+ /**
+ * A Shader that draws a sweep gradient around a center point.
+ *
+ * @param cx The x-coordinate of the center
+ * @param cy The y-coordinate of the center
+ * @param colors The sRGB colors to be distributed between around the center.
+ * There must be at least 2 colors in the array.
+ * @param positions May be NULL. The relative position of
+ * each corresponding color in the colors array, beginning
+ * with 0 and ending with 1.0. If the values are not
+ * monotonic, the drawing may produce unexpected results.
+ * If positions is NULL, then the colors are automatically
+ * spaced evenly.
+ */
+ public SweepGradient(float cx, float cy, @NonNull @ColorInt int[] colors,
+ @Nullable float[] positions) {
+ this(cx, cy, convertColors(colors), positions, ColorSpace.get(ColorSpace.Named.SRGB));
+ }
+
/**
* A Shader that draws a sweep gradient around a center point.
*
@@ -58,20 +76,30 @@
* monotonic, the drawing may produce unexpected results.
* If positions is NULL, then the colors are automatically
* spaced evenly.
+ * @throws IllegalArgumentException if there are less than two colors, the colors do
+ * not share the same {@link ColorSpace} or do not use a valid one, or {@code positions}
+ * is not {@code null} and has a different length from {@code colors}.
*/
- public SweepGradient(float cx, float cy,
- @NonNull @ColorInt int colors[], @Nullable float positions[]) {
- if (colors.length < 2) {
- throw new IllegalArgumentException("needs >= 2 number of colors");
- }
+ public SweepGradient(float cx, float cy, @NonNull @ColorLong long[] colors,
+ @Nullable float[] positions) {
+ this(cx, cy, colors.clone(), positions, detectColorSpace(colors));
+ }
+
+ /**
+ * Base constructor. Assumes @param colors is a copy that this object can hold onto,
+ * and all colors share @param colorSpace.
+ */
+ private SweepGradient(float cx, float cy, @NonNull @ColorLong long[] colors,
+ @Nullable float[] positions, ColorSpace colorSpace) {
+ super(colorSpace);
+
if (positions != null && colors.length != positions.length) {
throw new IllegalArgumentException(
"color and position arrays must be of equal length");
}
- mType = TYPE_COLORS_AND_POSITIONS;
mCx = cx;
mCy = cy;
- mColors = colors.clone();
+ mColorLongs = colors;
mPositions = positions != null ? positions.clone() : null;
}
@@ -80,26 +108,32 @@
*
* @param cx The x-coordinate of the center
* @param cy The y-coordinate of the center
+ * @param color0 The sRGB color to use at the start of the sweep
+ * @param color1 The sRGB color to use at the end of the sweep
+ */
+ public SweepGradient(float cx, float cy, @ColorInt int color0, @ColorInt int color1) {
+ this(cx, cy, Color.pack(color0), Color.pack(color1));
+ }
+
+ /**
+ * A Shader that draws a sweep gradient around a center point.
+ *
+ * @param cx The x-coordinate of the center
+ * @param cy The y-coordinate of the center
* @param color0 The color to use at the start of the sweep
* @param color1 The color to use at the end of the sweep
+ *
+ * @throws IllegalArgumentException if the colors do
+ * not share the same {@link ColorSpace} or do not use a valid one.
*/
- public SweepGradient(float cx, float cy, @ColorInt int color0, @ColorInt int color1) {
- mType = TYPE_COLOR_START_AND_COLOR_END;
- mCx = cx;
- mCy = cy;
- mColor0 = color0;
- mColor1 = color1;
- mColors = null;
- mPositions = null;
+ public SweepGradient(float cx, float cy, @ColorLong long color0, @ColorLong long color1) {
+ this(cx, cy, new long[] {color0, color1}, null);
}
@Override
long createNativeInstance(long nativeMatrix) {
- if (mType == TYPE_COLORS_AND_POSITIONS) {
- return nativeCreate1(nativeMatrix, mCx, mCy, mColors, mPositions);
- } else { // TYPE_COLOR_START_AND_COLOR_END
- return nativeCreate2(nativeMatrix, mCx, mCy, mColor0, mColor1);
- }
+ return nativeCreate(nativeMatrix, mCx, mCy, mColorLongs, mPositions,
+ colorSpace().getNativeInstance());
}
/**
@@ -107,20 +141,13 @@
*/
@Override
protected Shader copy() {
- final SweepGradient copy;
- if (mType == TYPE_COLORS_AND_POSITIONS) {
- copy = new SweepGradient(mCx, mCy, mColors.clone(),
- mPositions != null ? mPositions.clone() : null);
- } else { // TYPE_COLOR_START_AND_COLOR_END
- copy = new SweepGradient(mCx, mCy, mColor0, mColor1);
- }
+ final SweepGradient copy = new SweepGradient(mCx, mCy, mColorLongs,
+ mPositions, colorSpace());
copyLocalMatrix(copy);
return copy;
}
- private static native long nativeCreate1(long matrix, float x, float y,
- int colors[], float positions[]);
- private static native long nativeCreate2(long matrix, float x, float y,
- int color0, int color1);
+ private static native long nativeCreate(long matrix, float x, float y,
+ long[] colors, float[] positions, long colorSpaceHandle);
}