summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Romain Guy <romainguy@google.com> 2010-09-20 19:04:33 -0700
committer Romain Guy <romainguy@google.com> 2010-09-21 16:48:50 -0700
commitddb80bebb0776e6d852aab6e8bba5d5591847a55 (patch)
tree460e39e115bf75648a3b4dbc9788c911751bd848
parentee916f14cbd1fe1422c063ce2ef7b185e2bc5c6f (diff)
Add support for circular gradients to the GL renderer.
This change also adds full support for local transformation matrices on sweep and radial gradients. Change-Id: Id8773bc0766575190e3f3d51984fc5e57b266c3f
-rw-r--r--core/jni/android/graphics/Shader.cpp91
-rw-r--r--graphics/java/android/graphics/RadialGradient.java13
-rw-r--r--libs/hwui/Matrix.cpp6
-rw-r--r--libs/hwui/Matrix.h2
-rw-r--r--libs/hwui/ProgramCache.cpp16
-rw-r--r--libs/hwui/SkiaShader.cpp39
-rw-r--r--libs/hwui/SkiaShader.h24
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/AdvancedGradientsActivity.java40
8 files changed, 189 insertions, 42 deletions
diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp
index 344669c40da8..ee4474703c24 100644
--- a/core/jni/android/graphics/Shader.cpp
+++ b/core/jni/android/graphics/Shader.cpp
@@ -226,10 +226,8 @@ static SkShader* LinearGradient_create2(JNIEnv* env, jobject o,
///////////////////////////////////////////////////////////////////////////////////////////////
-static SkShader* RadialGradient_create1(JNIEnv* env, jobject,
- float x, float y, float radius,
- jintArray colorArray, jfloatArray posArray, int tileMode)
-{
+static SkShader* RadialGradient_create1(JNIEnv* env, jobject, float x, float y, float radius,
+ jintArray colorArray, jfloatArray posArray, int tileMode) {
SkPoint center;
center.set(SkFloatToScalar(x), SkFloatToScalar(y));
@@ -259,10 +257,8 @@ static SkShader* RadialGradient_create1(JNIEnv* env, jobject,
return shader;
}
-static SkShader* RadialGradient_create2(JNIEnv* env, jobject,
- float x, float y, float radius,
- int color0, int color1, int tileMode)
-{
+static SkShader* RadialGradient_create2(JNIEnv* env, jobject, float x, float y, float radius,
+ int color0, int color1, int tileMode) {
SkPoint center;
center.set(SkFloatToScalar(x), SkFloatToScalar(y));
@@ -276,11 +272,65 @@ static SkShader* RadialGradient_create2(JNIEnv* env, jobject,
return s;
}
+static SkiaShader* RadialGradient_postCreate1(JNIEnv* env, jobject o, SkShader* shader,
+ float x, float y, float radius, jintArray colorArray, jfloatArray posArray, int tileMode) {
+#ifdef USE_OPENGL_RENDERER
+ size_t count = env->GetArrayLength(colorArray);
+ const jint* colorValues = env->GetIntArrayElements(colorArray, NULL);
+
+ jfloat* storedPositions = new jfloat[count];
+ uint32_t* storedColors = new uint32_t[count];
+ for (size_t i = 0; i < count; i++) {
+ storedColors[i] = static_cast<uint32_t>(colorValues[i]);
+ }
+
+ if (posArray) {
+ AutoJavaFloatArray autoPos(env, posArray, count);
+ const float* posValues = autoPos.ptr();
+ for (size_t i = 0; i < count; i++) {
+ storedPositions[i] = posValues[i];
+ }
+ } else {
+ storedPositions[0] = 0.0f;
+ storedPositions[1] = 1.0f;
+ }
+
+ SkiaShader* skiaShader = new SkiaCircularGradientShader(x, y, radius, storedColors,
+ storedPositions, count, shader, (SkShader::TileMode) tileMode, NULL,
+ (shader->getFlags() & SkShader::kOpaqueAlpha_Flag) == 0);
+
+ env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT);
+ return skiaShader;
+#else
+ return NULL;
+#endif
+}
+
+static SkiaShader* RadialGradient_postCreate2(JNIEnv* env, jobject o, SkShader* shader,
+ float x, float y, float radius, int color0, int color1, int tileMode) {
+#ifdef USE_OPENGL_RENDERER
+ float* storedPositions = new float[2];
+ storedPositions[0] = 0.0f;
+ storedPositions[1] = 1.0f;
+
+ uint32_t* storedColors = new uint32_t[2];
+ storedColors[0] = static_cast<uint32_t>(color0);
+ storedColors[1] = static_cast<uint32_t>(color1);
+
+ SkiaShader* skiaShader = new SkiaCircularGradientShader(x, y, radius, storedColors,
+ storedPositions, 2, shader, (SkShader::TileMode) tileMode, NULL,
+ (shader->getFlags() & SkShader::kOpaqueAlpha_Flag) == 0);
+
+ return skiaShader;
+#else
+ return NULL;
+#endif
+}
+
///////////////////////////////////////////////////////////////////////////////
static SkShader* SweepGradient_create1(JNIEnv* env, jobject, float x, float y,
- jintArray jcolors, jfloatArray jpositions)
-{
+ jintArray jcolors, jfloatArray jpositions) {
size_t count = env->GetArrayLength(jcolors);
const jint* colors = env->GetIntArrayElements(jcolors, NULL);
@@ -307,8 +357,7 @@ static SkShader* SweepGradient_create1(JNIEnv* env, jobject, float x, float y,
}
static SkShader* SweepGradient_create2(JNIEnv* env, jobject, float x, float y,
- int color0, int color1)
-{
+ int color0, int color1) {
SkColor colors[2];
colors[0] = color0;
colors[1] = color1;
@@ -437,22 +486,24 @@ static JNINativeMethod gLinearGradientMethods[] = {
};
static JNINativeMethod gRadialGradientMethods[] = {
- {"nativeCreate1", "(FFF[I[FI)I", (void*)RadialGradient_create1 },
- {"nativeCreate2", "(FFFIII)I", (void*)RadialGradient_create2 }
+ { "nativeCreate1", "(FFF[I[FI)I", (void*)RadialGradient_create1 },
+ { "nativeCreate2", "(FFFIII)I", (void*)RadialGradient_create2 },
+ { "nativePostCreate1", "(IFFF[I[FI)I", (void*)RadialGradient_postCreate1 },
+ { "nativePostCreate2", "(IFFFIII)I", (void*)RadialGradient_postCreate2 }
};
static JNINativeMethod gSweepGradientMethods[] = {
- {"nativeCreate1", "(FF[I[F)I", (void*)SweepGradient_create1 },
- {"nativeCreate2", "(FFII)I", (void*)SweepGradient_create2 },
+ { "nativeCreate1", "(FF[I[F)I", (void*)SweepGradient_create1 },
+ { "nativeCreate2", "(FFII)I", (void*)SweepGradient_create2 },
{ "nativePostCreate1", "(IFF[I[F)I", (void*)SweepGradient_postCreate1 },
{ "nativePostCreate2", "(IFFII)I", (void*)SweepGradient_postCreate2 }
};
static JNINativeMethod gComposeShaderMethods[] = {
- {"nativeCreate1", "(III)I", (void*)ComposeShader_create1 },
- {"nativeCreate2", "(III)I", (void*)ComposeShader_create2 },
- {"nativePostCreate1", "(IIII)I", (void*)ComposeShader_postCreate1 },
- {"nativePostCreate2", "(IIII)I", (void*)ComposeShader_postCreate2 }
+ { "nativeCreate1", "(III)I", (void*)ComposeShader_create1 },
+ { "nativeCreate2", "(III)I", (void*)ComposeShader_create2 },
+ { "nativePostCreate1", "(IIII)I", (void*)ComposeShader_postCreate1 },
+ { "nativePostCreate2", "(IIII)I", (void*)ComposeShader_postCreate2 }
};
#include <android_runtime/AndroidRuntime.h>
diff --git a/graphics/java/android/graphics/RadialGradient.java b/graphics/java/android/graphics/RadialGradient.java
index b4e902dc6ec7..897762cba761 100644
--- a/graphics/java/android/graphics/RadialGradient.java
+++ b/graphics/java/android/graphics/RadialGradient.java
@@ -40,6 +40,8 @@ public class RadialGradient extends Shader {
throw new IllegalArgumentException("color and position arrays must be of equal length");
}
native_instance = nativeCreate1(x, y, radius, colors, positions, tile.nativeInt);
+ native_shader = nativePostCreate1(native_instance, x, y, radius, colors, positions,
+ tile.nativeInt);
}
/** Create a shader that draws a radial gradient given the center and radius.
@@ -56,11 +58,18 @@ public class RadialGradient extends Shader {
throw new IllegalArgumentException("radius must be > 0");
}
native_instance = nativeCreate2(x, y, radius, color0, color1, tile.nativeInt);
+ native_shader = nativePostCreate2(native_instance, x, y, radius, color0, color1,
+ tile.nativeInt);
}
private static native int nativeCreate1(float x, float y, float radius,
- int colors[], float positions[], int tileMode);
+ int colors[], float positions[], int tileMode);
private static native int nativeCreate2(float x, float y, float radius,
- int color0, int color1, int tileMode);
+ int color0, int color1, int tileMode);
+
+ private static native int nativePostCreate1(int native_shader, float x, float y, float radius,
+ int colors[], float positions[], int tileMode);
+ private static native int nativePostCreate2(int native_shader, float x, float y, float radius,
+ int color0, int color1, int tileMode);
}
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index 264ad3d28077..c698b5abb9c5 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -144,6 +144,12 @@ float Matrix4::getTranslateY() {
return data[kTranslateY];
}
+void Matrix4::multiply(float v) {
+ for (int i = 0; i < 16; i++) {
+ data[i] *= v;
+ }
+}
+
void Matrix4::loadTranslate(float x, float y, float z) {
loadIdentity();
data[kTranslateX] = x;
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index c247a670998b..0608efe988fe 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -83,6 +83,8 @@ public:
load(u);
}
+ void multiply(float v);
+
void translate(float x, float y, float z) {
Matrix4 u;
u.loadTranslate(x, y, z);
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index bcc1edfbb13c..3e9412cf0c78 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -40,9 +40,12 @@ const char* gVS_Header_Uniforms_HasGradient[3] = {
"uniform vec2 gradientStart;\n"
"uniform mat4 screenSpace;\n",
// Circular
- "",
+ "uniform vec2 gradientStart;\n"
+ "uniform mat4 gradientMatrix;\n"
+ "uniform mat4 screenSpace;\n",
// Sweep
"uniform vec2 gradientStart;\n"
+ "uniform mat4 gradientMatrix;\n"
"uniform mat4 screenSpace;\n"
};
const char* gVS_Header_Uniforms_HasBitmap =
@@ -56,7 +59,7 @@ const char* gVS_Header_Varyings_HasGradient[3] = {
// Linear
"varying float index;\n",
// Circular
- "",
+ "varying vec2 circular;\n",
// Sweep
"varying vec2 sweep;\n"
};
@@ -69,10 +72,11 @@ const char* gVS_Main_OutGradient[3] = {
" vec4 location = screenSpace * position;\n"
" index = dot(location.xy - gradientStart, gradient) * gradientLength;\n",
// Circular
- "",
+ " vec4 location = screenSpace * position;\n"
+ " circular = (gradientMatrix * vec4(location.xy - gradientStart, 0.0, 0.0)).xy;\n",
// Sweep
" vec4 location = screenSpace * position;\n"
- " sweep = location.xy - gradientStart;\n"
+ " sweep = (gradientMatrix * vec4(location.xy - gradientStart, 0.0, 0.0)).xy;\n"
};
const char* gVS_Main_OutBitmapTexCoords =
" vec4 bitmapCoords = textureTransform * position;\n"
@@ -98,6 +102,7 @@ const char* gFS_Uniforms_GradientSampler[3] = {
// Linear
"uniform sampler2D gradientSampler;\n",
// Circular
+ "uniform float gradientRadius;\n"
"uniform sampler2D gradientSampler;\n",
// Sweep
"uniform sampler2D gradientSampler;\n"
@@ -129,7 +134,8 @@ const char* gFS_Main_FetchGradient[3] = {
// Linear
" vec4 gradientColor = texture2D(gradientSampler, vec2(index, 0.5));\n",
// Circular
- "",
+ " float index = length(circular) * gradientRadius;\n"
+ " vec4 gradientColor = texture2D(gradientSampler, vec2(index, 0.5));\n",
// Sweep
" float index = atan(sweep.y, sweep.x) * 0.15915494309; // inv(2 * PI)\n"
" vec4 gradientColor = texture2D(gradientSampler, vec2(index - floor(index), 0.5));\n"
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index c5d97674ad7a..9e1f6c2e862b 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -209,6 +209,30 @@ void SkiaLinearGradientShader::updateTransforms(Program* program, const mat4& mo
}
///////////////////////////////////////////////////////////////////////////////
+// Circular gradient shader
+///////////////////////////////////////////////////////////////////////////////
+
+SkiaCircularGradientShader::SkiaCircularGradientShader(float x, float y, float radius,
+ uint32_t* colors, float* positions, int count, SkShader* key, SkShader::TileMode tileMode,
+ SkMatrix* matrix, bool blend):
+ SkiaSweepGradientShader(kCircularGradient, x, y, colors, positions, count, key,
+ tileMode, matrix, blend),
+ mRadius(radius) {
+}
+
+void SkiaCircularGradientShader::describe(ProgramDescription& description,
+ const Extensions& extensions) {
+ description.hasGradient = true;
+ description.gradientType = ProgramDescription::kGradientCircular;
+}
+
+void SkiaCircularGradientShader::setupProgram(Program* program, const mat4& modelView,
+ const Snapshot& snapshot, GLuint* textureUnit) {
+ SkiaSweepGradientShader::setupProgram(program, modelView, snapshot, textureUnit);
+ glUniform1f(program->getUniform("gradientRadius"), 1.0f / mRadius);
+}
+
+///////////////////////////////////////////////////////////////////////////////
// Sweep gradient shader
///////////////////////////////////////////////////////////////////////////////
@@ -219,6 +243,13 @@ SkiaSweepGradientShader::SkiaSweepGradientShader(float x, float y, uint32_t* col
mX(x), mY(y), mColors(colors), mPositions(positions), mCount(count) {
}
+SkiaSweepGradientShader::SkiaSweepGradientShader(Type type, float x, float y, uint32_t* colors,
+ float* positions, int count, SkShader* key, SkShader::TileMode tileMode,
+ SkMatrix* matrix, bool blend):
+ SkiaShader(type, key, tileMode, tileMode, matrix, blend),
+ mX(x), mY(y), mColors(colors), mPositions(positions), mCount(count) {
+}
+
SkiaSweepGradientShader::~SkiaSweepGradientShader() {
delete[] mColors;
delete[] mPositions;
@@ -243,10 +274,15 @@ void SkiaSweepGradientShader::setupProgram(Program* program, const mat4& modelVi
float left = mX;
float top = mY;
+ mat4 shaderMatrix;
if (mMatrix) {
- mat4 shaderMatrix(*mMatrix);
+ shaderMatrix.load(*mMatrix);
shaderMatrix.mapPoint(left, top);
}
+
+ mat4 copy(shaderMatrix);
+ shaderMatrix.loadInverse(copy);
+
snapshot.transform->mapPoint(left, top);
mat4 screenSpace(*snapshot.transform);
@@ -255,6 +291,7 @@ void SkiaSweepGradientShader::setupProgram(Program* program, const mat4& modelVi
// Uniforms
bindTexture(texture->id, gTileModes[mTileX], gTileModes[mTileY], textureSlot);
glUniform1i(program->getUniform("gradientSampler"), textureSlot);
+ glUniformMatrix4fv(program->getUniform("gradientMatrix"), 1, GL_FALSE, &shaderMatrix.data[0]);
glUniform2f(program->getUniform("gradientStart"), left, top);
glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
}
diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h
index b67bfee66919..0023c46465af 100644
--- a/libs/hwui/SkiaShader.h
+++ b/libs/hwui/SkiaShader.h
@@ -152,12 +152,15 @@ struct SkiaSweepGradientShader: public SkiaShader {
SkShader* key, SkMatrix* matrix, bool blend);
~SkiaSweepGradientShader();
- void describe(ProgramDescription& description, const Extensions& extensions);
- void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
+ virtual void describe(ProgramDescription& description, const Extensions& extensions);
+ virtual void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
GLuint* textureUnit);
void updateTransforms(Program* program, const mat4& modelView, const Snapshot& snapshot);
-private:
+protected:
+ SkiaSweepGradientShader(Type type, float x, float y, uint32_t* colors, float* positions,
+ int count, SkShader* key, SkShader::TileMode tileMode, SkMatrix* matrix, bool blend);
+
float mX, mY;
uint32_t* mColors;
float* mPositions;
@@ -165,6 +168,21 @@ private:
}; // struct SkiaSweepGradientShader
/**
+ * A shader that draws a circular gradient.
+ */
+struct SkiaCircularGradientShader: public SkiaSweepGradientShader {
+ SkiaCircularGradientShader(float x, float y, float radius, uint32_t* colors, float* positions,
+ int count, SkShader* key,SkShader::TileMode tileMode, SkMatrix* matrix, bool blend);
+
+ void describe(ProgramDescription& description, const Extensions& extensions);
+ void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
+ GLuint* textureUnit);
+
+private:
+ float mRadius;
+}; // struct SkiaCircularGradientShader
+
+/**
* A shader that draws two shaders, composited with an xfermode.
*/
struct SkiaComposeShader: public SkiaShader {
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/AdvancedGradientsActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/AdvancedGradientsActivity.java
index e2320216dd37..27974e77e676 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/AdvancedGradientsActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/AdvancedGradientsActivity.java
@@ -42,19 +42,25 @@ public class AdvancedGradientsActivity extends Activity {
private final RadialGradient mRadialGradient;
private final Matrix mMatrix;
private final Matrix mMatrix2;
+ private final Matrix mMatrix3;
GradientsView(Context c) {
super(c);
- mSweepGradient = new SweepGradient(100.0f, 100.0f, 0xff000000, 0xffffffff);
- mRadialGradient = new RadialGradient(100.0f, 100.0f, 100.0f, 0xff000000, 0xffffffff,
+ mSweepGradient = new SweepGradient(0.0f, 0.0f, 0xff000000, 0xffffffff);
+ mRadialGradient = new RadialGradient(0.0f, 0.0f, 100.0f, 0xff000000, 0xffffffff,
Shader.TileMode.MIRROR);
mMatrix = new Matrix();
- mMatrix.setTranslate(50.0f, 50.0f);
+ mMatrix.setRotate(-45, 0.0f, 0.0f);
+ mMatrix.postTranslate(100.0f, 100.0f);
mMatrix2 = new Matrix();
- mMatrix2.setScale(2.0f, 2.0f);
+ mMatrix2.setScale(1.0f, 2.0f);
+ mMatrix2.postRotate(-45, 0.0f, 0.0f);
+
+ mMatrix3 = new Matrix();
+ mMatrix3.setTranslate(100.0f, 100.0f);
mPaint = new Paint();
}
@@ -64,29 +70,41 @@ public class AdvancedGradientsActivity extends Activity {
super.onDraw(canvas);
canvas.drawRGB(255, 255, 255);
- // Bitmap shaders
canvas.save();
canvas.translate(130.0f, 100.0f);
- mSweepGradient.setLocalMatrix(null);
+ mSweepGradient.setLocalMatrix(mMatrix3);
mPaint.setShader(mSweepGradient);
canvas.drawRect(0.0f, 0.0f, 200.0f, 200.0f, mPaint);
canvas.translate(400.0f, 000.0f);
- mPaint.setShader(mRadialGradient);
- canvas.drawRect(0.0f, 0.0f, 200.0f, 200.0f, mPaint);
+ mSweepGradient.setLocalMatrix(mMatrix);
+ mPaint.setShader(mSweepGradient);
+ canvas.drawRect(0.0f, 0.0f, 200.0f, 200.0f, mPaint);
canvas.translate(400.0f, 000.0f);
- mSweepGradient.setLocalMatrix(mMatrix);
+ mSweepGradient.setLocalMatrix(mMatrix2);
mPaint.setShader(mSweepGradient);
canvas.drawRect(0.0f, 0.0f, 200.0f, 200.0f, mPaint);
canvas.translate(-800.0f, 300.0f);
+
+ mRadialGradient.setLocalMatrix(null);
+ mPaint.setShader(mRadialGradient);
+ canvas.drawRect(0.0f, 0.0f, 200.0f, 200.0f, mPaint);
- mSweepGradient.setLocalMatrix(mMatrix2);
- mPaint.setShader(mSweepGradient);
+ canvas.translate(400.0f, 000.0f);
+
+ mRadialGradient.setLocalMatrix(mMatrix);
+ mPaint.setShader(mRadialGradient);
+ canvas.drawRect(0.0f, 0.0f, 200.0f, 200.0f, mPaint);
+
+ canvas.translate(400.0f, 000.0f);
+
+ mRadialGradient.setLocalMatrix(mMatrix2);
+ mPaint.setShader(mRadialGradient);
canvas.drawRect(0.0f, 0.0f, 200.0f, 200.0f, mPaint);