Add support for SweepGradient in the GL renderer.
Change-Id: I7aa397ed4e34655ead9ba1f5b4ce087665e0f022
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index 9957370..97f4cb4 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -116,7 +116,7 @@
mCache.clear();
}
-Texture* GradientCache::addLinearGradient(SkShader* shader, float* bounds, uint32_t* colors,
+Texture* GradientCache::addLinearGradient(SkShader* shader, uint32_t* colors,
float* positions, int count, SkShader::TileMode tileMode) {
SkBitmap bitmap;
bitmap.setConfig(SkBitmap::kARGB_8888_Config, 1024, 1);
diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h
index 51a8c01..c829fd4 100644
--- a/libs/hwui/GradientCache.h
+++ b/libs/hwui/GradientCache.h
@@ -46,8 +46,8 @@
* Adds a new linear gradient to the cache. The generated texture is
* returned.
*/
- Texture* addLinearGradient(SkShader* shader, float* bounds, uint32_t* colors,
- float* positions, int count, SkShader::TileMode tileMode);
+ Texture* addLinearGradient(SkShader* shader, uint32_t* colors, float* positions,
+ int count, SkShader::TileMode tileMode = SkShader::kClamp_TileMode);
/**
* Returns the texture associated with the specified shader.
*/
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index becbc22..bcc1edf 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -33,11 +33,18 @@
"attribute vec2 texCoords;\n";
const char* gVS_Header_Uniforms =
"uniform mat4 transform;\n";
-const char* gVS_Header_Uniforms_HasGradient =
+const char* gVS_Header_Uniforms_HasGradient[3] = {
+ // Linear
"uniform float gradientLength;\n"
"uniform vec2 gradient;\n"
"uniform vec2 gradientStart;\n"
- "uniform mat4 screenSpace;\n";
+ "uniform mat4 screenSpace;\n",
+ // Circular
+ "",
+ // Sweep
+ "uniform vec2 gradientStart;\n"
+ "uniform mat4 screenSpace;\n"
+};
const char* gVS_Header_Uniforms_HasBitmap =
"uniform mat4 textureTransform;\n"
"uniform vec2 textureDimension;\n";
@@ -45,15 +52,28 @@
"varying vec2 outTexCoords;\n";
const char* gVS_Header_Varyings_HasBitmap =
"varying vec2 outBitmapTexCoords;\n";
-const char* gVS_Header_Varyings_HasGradient =
- "varying float index;\n";
+const char* gVS_Header_Varyings_HasGradient[3] = {
+ // Linear
+ "varying float index;\n",
+ // Circular
+ "",
+ // Sweep
+ "varying vec2 sweep;\n"
+};
const char* gVS_Main =
"\nvoid main(void) {\n";
const char* gVS_Main_OutTexCoords =
" outTexCoords = texCoords;\n";
-const char* gVS_Main_OutGradientIndex =
+const char* gVS_Main_OutGradient[3] = {
+ // Linear
" vec4 location = screenSpace * position;\n"
- " index = dot(location.xy - gradientStart, gradient) * gradientLength;\n";
+ " index = dot(location.xy - gradientStart, gradient) * gradientLength;\n",
+ // Circular
+ "",
+ // Sweep
+ " vec4 location = screenSpace * position;\n"
+ " sweep = location.xy - gradientStart;\n"
+};
const char* gVS_Main_OutBitmapTexCoords =
" vec4 bitmapCoords = textureTransform * position;\n"
" outBitmapTexCoords = bitmapCoords.xy * textureDimension;\n";
@@ -74,8 +94,14 @@
"uniform vec4 color;\n";
const char* gFS_Uniforms_TextureSampler =
"uniform sampler2D sampler;\n";
-const char* gFS_Uniforms_GradientSampler =
- "uniform sampler2D gradientSampler;\n";
+const char* gFS_Uniforms_GradientSampler[3] = {
+ // Linear
+ "uniform sampler2D gradientSampler;\n",
+ // Circular
+ "uniform sampler2D gradientSampler;\n",
+ // Sweep
+ "uniform sampler2D gradientSampler;\n"
+};
const char* gFS_Uniforms_BitmapSampler =
"uniform sampler2D bitmapSampler;\n";
const char* gFS_Uniforms_ColorOp[4] = {
@@ -99,8 +125,15 @@
" fragColor = color * texture2D(sampler, outTexCoords);\n";
const char* gFS_Main_FetchA8Texture =
" fragColor = color * texture2D(sampler, outTexCoords).a;\n";
-const char* gFS_Main_FetchGradient =
- " vec4 gradientColor = texture2D(gradientSampler, vec2(index, 0.5));\n";
+const char* gFS_Main_FetchGradient[3] = {
+ // Linear
+ " vec4 gradientColor = texture2D(gradientSampler, vec2(index, 0.5));\n",
+ // Circular
+ "",
+ // 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"
+};
const char* gFS_Main_FetchBitmap =
" vec4 bitmapColor = texture2D(bitmapSampler, outBitmapTexCoords);\n";
const char* gFS_Main_FetchBitmapNpot =
@@ -217,7 +250,7 @@
ssize_t index = mCache.indexOfKey(key);
Program* program = NULL;
if (index < 0) {
- PROGRAM_LOGD("Could not find program with key 0x%x", key);
+ description.log("Could not find program");
program = generateProgram(description, key);
mCache.add(key, program);
} else {
@@ -247,7 +280,7 @@
// Uniforms
shader.append(gVS_Header_Uniforms);
if (description.hasGradient) {
- shader.append(gVS_Header_Uniforms_HasGradient);
+ shader.append(gVS_Header_Uniforms_HasGradient[description.gradientType]);
}
if (description.hasBitmap) {
shader.append(gVS_Header_Uniforms_HasBitmap);
@@ -257,7 +290,7 @@
shader.append(gVS_Header_Varyings_HasTexture);
}
if (description.hasGradient) {
- shader.append(gVS_Header_Varyings_HasGradient);
+ shader.append(gVS_Header_Varyings_HasGradient[description.gradientType]);
}
if (description.hasBitmap) {
shader.append(gVS_Header_Varyings_HasBitmap);
@@ -269,7 +302,7 @@
shader.append(gVS_Main_OutTexCoords);
}
if (description.hasGradient) {
- shader.append(gVS_Main_OutGradientIndex);
+ shader.append(gVS_Main_OutGradient[description.gradientType]);
}
if (description.hasBitmap) {
shader.append(gVS_Main_OutBitmapTexCoords);
@@ -301,7 +334,7 @@
shader.append(gVS_Header_Varyings_HasTexture);
}
if (description.hasGradient) {
- shader.append(gVS_Header_Varyings_HasGradient);
+ shader.append(gVS_Header_Varyings_HasGradient[description.gradientType]);
}
if (description.hasBitmap) {
shader.append(gVS_Header_Varyings_HasBitmap);
@@ -314,7 +347,7 @@
shader.append(gFS_Uniforms_TextureSampler);
}
if (description.hasGradient) {
- shader.append(gFS_Uniforms_GradientSampler);
+ shader.append(gFS_Uniforms_GradientSampler[description.gradientType]);
}
if (description.hasBitmap) {
shader.append(gFS_Uniforms_BitmapSampler);
@@ -348,7 +381,7 @@
shader.append(gFS_Main_FetchColor);
}
if (description.hasGradient) {
- shader.append(gFS_Main_FetchGradient);
+ shader.append(gFS_Main_FetchGradient[description.gradientType]);
}
if (description.hasBitmap) {
if (!description.isBitmapNpot) {
diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h
index 0a17052..4fa8011 100644
--- a/libs/hwui/ProgramCache.h
+++ b/libs/hwui/ProgramCache.h
@@ -44,9 +44,6 @@
#define PROGRAM_LOGD(...)
#endif
-/*
- * IMPORTANT: All 32 bits are used, switch to a long.
- */
#define PROGRAM_KEY_TEXTURE 0x1
#define PROGRAM_KEY_A8_TEXTURE 0x2
#define PROGRAM_KEY_BITMAP 0x4
@@ -70,14 +67,13 @@
#define PROGRAM_BITMAP_WRAPS_SHIFT 9
#define PROGRAM_BITMAP_WRAPT_SHIFT 11
+#define PROGRAM_GRADIENT_TYPE_SHIFT 33
+
///////////////////////////////////////////////////////////////////////////////
// Types
///////////////////////////////////////////////////////////////////////////////
-/*
- * IMPORTANT: All 32 bits are used, switch to a long.
- */
-typedef uint32_t programid;
+typedef uint64_t programid;
///////////////////////////////////////////////////////////////////////////////
// Cache
@@ -96,9 +92,16 @@
kColorBlend
};
+ enum Gradient {
+ kGradientLinear,
+ kGradientCircular,
+ kGradientSweep
+ };
+
ProgramDescription():
hasTexture(false), hasAlpha8Texture(false),
hasBitmap(false), isBitmapNpot(false), hasGradient(false),
+ gradientType(kGradientLinear),
shadersMode(SkXfermode::kClear_Mode), isBitmapFirst(false),
bitmapWrapS(GL_CLAMP_TO_EDGE), bitmapWrapT(GL_CLAMP_TO_EDGE),
colorOp(kColorNone), colorMode(SkXfermode::kClear_Mode),
@@ -112,8 +115,12 @@
// Shaders
bool hasBitmap;
bool isBitmapNpot;
+
bool hasGradient;
+ Gradient gradientType;
+
SkXfermode::Mode shadersMode;
+
bool isBitmapFirst;
GLenum bitmapWrapS;
GLenum bitmapWrapT;
@@ -152,7 +159,8 @@
}
}
if (hasGradient) key |= PROGRAM_KEY_GRADIENT;
- if (isBitmapFirst) key |= PROGRAM_KEY_BITMAP_FIRST;
+ key |= programid(gradientType) << PROGRAM_GRADIENT_TYPE_SHIFT;
+ if (isBitmapFirst) key |= PROGRAM_KEY_BITMAP_FIRST;
if (hasBitmap && hasGradient) {
key |= (shadersMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_SHADER_SHIFT;
}
@@ -174,6 +182,12 @@
if (swapSrcDst) key |= PROGRAM_KEY_SWAP_SRC_DST;
return key;
}
+
+ void log(const char* message) const {
+ programid k = key();
+ PROGRAM_LOGD("%s (key = 0x%.8x%.8x)", message, uint32_t(k >> 32),
+ uint32_t(k & 0xffffffff));
+ }
}; // struct ProgramDescription
/**
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index 946cc4b..c5d9767 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -164,6 +164,7 @@
void SkiaLinearGradientShader::describe(ProgramDescription& description,
const Extensions& extensions) {
description.hasGradient = true;
+ description.gradientType = ProgramDescription::kGradientLinear;
}
void SkiaLinearGradientShader::setupProgram(Program* program, const mat4& modelView,
@@ -173,8 +174,7 @@
Texture* texture = mGradientCache->get(mKey);
if (!texture) {
- texture = mGradientCache->addLinearGradient(mKey, mBounds, mColors, mPositions,
- mCount, mTileX);
+ texture = mGradientCache->addLinearGradient(mKey, mColors, mPositions, mCount, mTileX);
}
Rect start(mBounds[0], mBounds[1], mBounds[2], mBounds[3]);
@@ -209,6 +209,64 @@
}
///////////////////////////////////////////////////////////////////////////////
+// Sweep gradient shader
+///////////////////////////////////////////////////////////////////////////////
+
+SkiaSweepGradientShader::SkiaSweepGradientShader(float x, float y, uint32_t* colors,
+ float* positions, int count, SkShader* key, SkMatrix* matrix, bool blend):
+ SkiaShader(kSweepGradient, key, SkShader::kClamp_TileMode,
+ SkShader::kClamp_TileMode, matrix, blend),
+ mX(x), mY(y), mColors(colors), mPositions(positions), mCount(count) {
+}
+
+SkiaSweepGradientShader::~SkiaSweepGradientShader() {
+ delete[] mColors;
+ delete[] mPositions;
+}
+
+void SkiaSweepGradientShader::describe(ProgramDescription& description,
+ const Extensions& extensions) {
+ description.hasGradient = true;
+ description.gradientType = ProgramDescription::kGradientSweep;
+}
+
+void SkiaSweepGradientShader::setupProgram(Program* program, const mat4& modelView,
+ const Snapshot& snapshot, GLuint* textureUnit) {
+ GLuint textureSlot = (*textureUnit)++;
+ glActiveTexture(gTextureUnitsMap[textureSlot]);
+
+ Texture* texture = mGradientCache->get(mKey);
+ if (!texture) {
+ texture = mGradientCache->addLinearGradient(mKey, mColors, mPositions, mCount);
+ }
+
+ float left = mX;
+ float top = mY;
+
+ if (mMatrix) {
+ mat4 shaderMatrix(*mMatrix);
+ shaderMatrix.mapPoint(left, top);
+ }
+ snapshot.transform->mapPoint(left, top);
+
+ mat4 screenSpace(*snapshot.transform);
+ screenSpace.multiply(modelView);
+
+ // Uniforms
+ bindTexture(texture->id, gTileModes[mTileX], gTileModes[mTileY], textureSlot);
+ glUniform1i(program->getUniform("gradientSampler"), textureSlot);
+ glUniform2f(program->getUniform("gradientStart"), left, top);
+ glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
+}
+
+void SkiaSweepGradientShader::updateTransforms(Program* program, const mat4& modelView,
+ const Snapshot& snapshot) {
+ mat4 screenSpace(*snapshot.transform);
+ screenSpace.multiply(modelView);
+ glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
+}
+
+///////////////////////////////////////////////////////////////////////////////
// Compose shader
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h
index cc94ae6..b67bfee 100644
--- a/libs/hwui/SkiaShader.h
+++ b/libs/hwui/SkiaShader.h
@@ -145,6 +145,26 @@
}; // struct SkiaLinearGradientShader
/**
+ * A shader that draws a sweep gradient.
+ */
+struct SkiaSweepGradientShader: public SkiaShader {
+ SkiaSweepGradientShader(float x, float y, uint32_t* colors, float* positions, int count,
+ SkShader* key, SkMatrix* matrix, bool blend);
+ ~SkiaSweepGradientShader();
+
+ void describe(ProgramDescription& description, const Extensions& extensions);
+ void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
+ GLuint* textureUnit);
+ void updateTransforms(Program* program, const mat4& modelView, const Snapshot& snapshot);
+
+private:
+ float mX, mY;
+ uint32_t* mColors;
+ float* mPositions;
+ int mCount;
+}; // struct SkiaSweepGradientShader
+
+/**
* A shader that draws two shaders, composited with an xfermode.
*/
struct SkiaComposeShader: public SkiaShader {