Merge "Improve gradients" into jb-mr1-dev

This commit is contained in:
Romain Guy
2012-07-31 18:55:43 -07:00
committed by Android (Google) Code Review
10 changed files with 395 additions and 120 deletions

View File

@@ -150,19 +150,35 @@ static SkiaShader* LinearGradient_postCreate1(JNIEnv* env, jobject o, SkShader*
jfloat* storedBounds = new jfloat[4]; jfloat* storedBounds = new jfloat[4];
storedBounds[0] = x0; storedBounds[1] = y0; storedBounds[0] = x0; storedBounds[1] = y0;
storedBounds[2] = x1; storedBounds[3] = y1; storedBounds[2] = x1; storedBounds[3] = y1;
jfloat* storedPositions = new jfloat[count];
uint32_t* storedColors = new uint32_t[count]; bool missFirst = false;
for (size_t i = 0; i < count; i++) { bool missLast = false;
storedColors[i] = static_cast<uint32_t>(colorValues[i]); size_t stopCount = count;
}
jfloat* storedPositions = NULL;
if (posArray) { if (posArray) {
AutoJavaFloatArray autoPos(env, posArray, count); AutoJavaFloatArray autoPos(env, posArray, count);
const float* posValues = autoPos.ptr(); const float* posValues = autoPos.ptr();
for (size_t i = 0; i < count; i++) {
storedPositions[i] = posValues[i]; missFirst = posValues[0] != 0.0f;
missLast = posValues[count - 1] != 1.0f;
stopCount += missFirst + missLast;
storedPositions = new jfloat[stopCount];
if (missFirst) {
storedPositions[0] = 0.0f;
}
for (size_t i = missFirst; i < count + missFirst; i++) {
storedPositions[i] = posValues[i - missFirst];
}
if (missLast) {
storedPositions[stopCount - 1] = 1.0f;
} }
} else { } else {
storedPositions = new jfloat[count];
storedPositions[0] = 0.0f; storedPositions[0] = 0.0f;
const jfloat step = 1.0f / (count - 1); const jfloat step = 1.0f / (count - 1);
for (size_t i = 1; i < count - 1; i++) { for (size_t i = 1; i < count - 1; i++) {
@@ -171,8 +187,22 @@ static SkiaShader* LinearGradient_postCreate1(JNIEnv* env, jobject o, SkShader*
storedPositions[count - 1] = 1.0f; storedPositions[count - 1] = 1.0f;
} }
uint32_t* storedColors = new uint32_t[stopCount];
if (missFirst) {
storedColors[0] = static_cast<uint32_t>(colorValues[0]);
}
for (size_t i = missFirst; i < count + missFirst; i++) {
storedColors[i] = static_cast<uint32_t>(colorValues[i - missFirst]);
}
if (missLast) {
storedColors[stopCount - 1] = static_cast<uint32_t>(colorValues[count - 1]);
}
SkiaShader* skiaShader = new SkiaLinearGradientShader(storedBounds, storedColors, SkiaShader* skiaShader = new SkiaLinearGradientShader(storedBounds, storedColors,
storedPositions, count, shader, static_cast<SkShader::TileMode>(tileMode), NULL, storedPositions, stopCount, shader, static_cast<SkShader::TileMode>(tileMode), NULL,
(shader->getFlags() & SkShader::kOpaqueAlpha_Flag) == 0); (shader->getFlags() & SkShader::kOpaqueAlpha_Flag) == 0);
env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT); env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT);

View File

@@ -16,9 +16,6 @@
#define LOG_TAG "OpenGLRenderer" #define LOG_TAG "OpenGLRenderer"
#include <SkCanvas.h>
#include <SkGradientShader.h>
#include <utils/threads.h> #include <utils/threads.h>
#include "Debug.h" #include "Debug.h"
@@ -28,6 +25,22 @@
namespace android { namespace android {
namespace uirenderer { namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
// Defines
///////////////////////////////////////////////////////////////////////////////
#define GRADIENT_TEXTURE_HEIGHT 2
#define GRADIENT_BYTES_PER_PIXEL 4
///////////////////////////////////////////////////////////////////////////////
// Functions
///////////////////////////////////////////////////////////////////////////////
template<typename T>
static inline T min(T a, T b) {
return a < b ? a : b;
}
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Constructors/destructor // Constructors/destructor
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@@ -83,7 +96,7 @@ void GradientCache::setMaxSize(uint32_t maxSize) {
void GradientCache::operator()(GradientCacheEntry& shader, Texture*& texture) { void GradientCache::operator()(GradientCacheEntry& shader, Texture*& texture) {
if (texture) { if (texture) {
const uint32_t size = texture->width * texture->height * 4; const uint32_t size = texture->width * texture->height * GRADIENT_BYTES_PER_PIXEL;
mSize -= size; mSize -= size;
} }
@@ -97,14 +110,13 @@ void GradientCache::operator()(GradientCacheEntry& shader, Texture*& texture) {
// Caching // Caching
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
Texture* GradientCache::get(uint32_t* colors, float* positions, Texture* GradientCache::get(uint32_t* colors, float* positions, int count) {
int count, SkShader::TileMode tileMode) {
GradientCacheEntry gradient(colors, positions, count, tileMode); GradientCacheEntry gradient(colors, positions, count);
Texture* texture = mCache.get(gradient); Texture* texture = mCache.get(gradient);
if (!texture) { if (!texture) {
texture = addLinearGradient(gradient, colors, positions, count, tileMode); texture = addLinearGradient(gradient, colors, positions, count);
} }
return texture; return texture;
@@ -114,39 +126,41 @@ void GradientCache::clear() {
mCache.clear(); mCache.clear();
} }
void GradientCache::getGradientInfo(const uint32_t* colors, const int count,
GradientInfo& info) {
uint32_t width = 1 << (31 - __builtin_clz(256 * (count - 1)));
bool hasAlpha = false;
for (int i = 0; i < count; i++) {
if (((colors[i] >> 24) & 0xff) < 255) {
hasAlpha = true;
break;
}
}
info.width = min(width, uint32_t(mMaxTextureSize));
info.hasAlpha = hasAlpha;
}
Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient, Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient,
uint32_t* colors, float* positions, int count, SkShader::TileMode tileMode) { uint32_t* colors, float* positions, int count) {
int width = 256 * (count - 1);
width = width < mMaxTextureSize ? width : mMaxTextureSize;
SkBitmap bitmap; GradientInfo info;
bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, 4); getGradientInfo(colors, count, info);
bitmap.allocPixels();
bitmap.eraseColor(0);
SkCanvas canvas(bitmap); Texture* texture = new Texture;
texture->width = info.width;
SkPoint points[2]; texture->height = GRADIENT_TEXTURE_HEIGHT;
points[0].set(0.0f, 0.0f); texture->blend = info.hasAlpha;
points[1].set(bitmap.width(), 0.0f); texture->generation = 1;
SkShader* localShader = SkGradientShader::CreateLinear(points,
reinterpret_cast<const SkColor*>(colors), positions, count, tileMode);
SkPaint p;
p.setStyle(SkPaint::kStrokeAndFill_Style);
p.setShader(localShader)->unref();
canvas.drawRectCoords(0.0f, 0.0f, bitmap.width(), 4.0f, p);
// Asume the cache is always big enough // Asume the cache is always big enough
const uint32_t size = bitmap.rowBytes() * bitmap.height(); const uint32_t size = texture->width * texture->height * GRADIENT_BYTES_PER_PIXEL;
while (mSize + size > mMaxSize) { while (mSize + size > mMaxSize) {
mCache.removeOldest(); mCache.removeOldest();
} }
Texture* texture = new Texture; generateTexture(colors, positions, count, texture);
generateTexture(&bitmap, texture);
mSize += size; mSize += size;
mCache.put(gradient, texture); mCache.put(gradient, texture);
@@ -154,25 +168,67 @@ Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient,
return texture; return texture;
} }
void GradientCache::generateTexture(SkBitmap* bitmap, Texture* texture) { void GradientCache::generateTexture(uint32_t* colors, float* positions,
SkAutoLockPixels autoLock(*bitmap); int count, Texture* texture) {
if (!bitmap->readyToDraw()) {
ALOGE("Cannot generate texture from shader"); const uint32_t width = texture->width;
return; const GLsizei rowBytes = width * GRADIENT_BYTES_PER_PIXEL;
uint32_t pixels[width * texture->height];
int currentPos = 1;
float startA = (colors[0] >> 24) & 0xff;
float startR = (colors[0] >> 16) & 0xff;
float startG = (colors[0] >> 8) & 0xff;
float startB = (colors[0] >> 0) & 0xff;
float endA = (colors[1] >> 24) & 0xff;
float endR = (colors[1] >> 16) & 0xff;
float endG = (colors[1] >> 8) & 0xff;
float endB = (colors[1] >> 0) & 0xff;
float start = positions[0];
float distance = positions[1] - start;
uint8_t* p = (uint8_t*) pixels;
for (uint32_t x = 0; x < width; x++) {
float pos = x / float(width - 1);
if (pos > positions[currentPos]) {
startA = endA;
startR = endR;
startG = endG;
startB = endB;
start = positions[currentPos];
currentPos++;
endA = (colors[currentPos] >> 24) & 0xff;
endR = (colors[currentPos] >> 16) & 0xff;
endG = (colors[currentPos] >> 8) & 0xff;
endB = (colors[currentPos] >> 0) & 0xff;
distance = positions[currentPos] - start;
}
float amount = (pos - start) / distance;
float oppAmount = 1.0f - amount;
*p++ = uint8_t(startR * oppAmount + endR * amount);
*p++ = uint8_t(startG * oppAmount + endG * amount);
*p++ = uint8_t(startB * oppAmount + endB * amount);
*p++ = uint8_t(startA * oppAmount + endA * amount);
} }
texture->generation = bitmap->getGenerationID(); for (int i = 1; i < GRADIENT_TEXTURE_HEIGHT; i++) {
texture->width = bitmap->width(); memcpy(pixels + width * i, pixels, rowBytes);
texture->height = bitmap->height(); }
glGenTextures(1, &texture->id); glGenTextures(1, &texture->id);
glBindTexture(GL_TEXTURE_2D, texture->id); glBindTexture(GL_TEXTURE_2D, texture->id);
glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel()); glPixelStorei(GL_UNPACK_ALIGNMENT, GRADIENT_BYTES_PER_PIXEL);
texture->blend = !bitmap->isOpaque(); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, texture->height, 0,
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap->rowBytesAsPixels(), texture->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
GL_RGBA, GL_UNSIGNED_BYTE, bitmap->getPixels());
texture->setFilter(GL_LINEAR); texture->setFilter(GL_LINEAR);
texture->setWrap(GL_CLAMP_TO_EDGE); texture->setWrap(GL_CLAMP_TO_EDGE);

View File

@@ -36,16 +36,14 @@ struct GradientCacheEntry {
count = 0; count = 0;
colors = NULL; colors = NULL;
positions = NULL; positions = NULL;
tileMode = SkShader::kClamp_TileMode;
} }
GradientCacheEntry(uint32_t* colors, float* positions, int count, GradientCacheEntry(uint32_t* colors, float* positions, int count) {
SkShader::TileMode tileMode) { copy(colors, positions, count);
copy(colors, positions, count, tileMode);
} }
GradientCacheEntry(const GradientCacheEntry& entry) { GradientCacheEntry(const GradientCacheEntry& entry) {
copy(entry.colors, entry.positions, entry.count, entry.tileMode); copy(entry.colors, entry.positions, entry.count);
} }
~GradientCacheEntry() { ~GradientCacheEntry() {
@@ -58,7 +56,7 @@ struct GradientCacheEntry {
delete[] colors; delete[] colors;
delete[] positions; delete[] positions;
copy(entry.colors, entry.positions, entry.count, entry.tileMode); copy(entry.colors, entry.positions, entry.count);
} }
return *this; return *this;
@@ -67,13 +65,11 @@ struct GradientCacheEntry {
bool operator<(const GradientCacheEntry& r) const { bool operator<(const GradientCacheEntry& r) const {
const GradientCacheEntry& rhs = (const GradientCacheEntry&) r; const GradientCacheEntry& rhs = (const GradientCacheEntry&) r;
LTE_INT(count) { LTE_INT(count) {
LTE_INT(tileMode) { int result = memcmp(colors, rhs.colors, count * sizeof(uint32_t));
int result = memcmp(colors, rhs.colors, count * sizeof(uint32_t)); if (result< 0) return true;
if (result< 0) return true; else if (result == 0) {
else if (result == 0) { result = memcmp(positions, rhs.positions, count * sizeof(float));
result = memcmp(positions, rhs.positions, count * sizeof(float)); if (result < 0) return true;
if (result < 0) return true;
}
} }
} }
return false; return false;
@@ -86,11 +82,10 @@ struct GradientCacheEntry {
private: private:
void copy(uint32_t* colors, float* positions, int count, SkShader::TileMode tileMode) { void copy(uint32_t* colors, float* positions, int count) {
this->count = count; this->count = count;
this->colors = new uint32_t[count]; this->colors = new uint32_t[count];
this->positions = new float[count]; this->positions = new float[count];
this->tileMode = tileMode;
memcpy(this->colors, colors, count * sizeof(uint32_t)); memcpy(this->colors, colors, count * sizeof(uint32_t));
memcpy(this->positions, positions, count * sizeof(float)); memcpy(this->positions, positions, count * sizeof(float));
@@ -118,8 +113,8 @@ public:
/** /**
* Returns the texture associated with the specified shader. * Returns the texture associated with the specified shader.
*/ */
Texture* get(uint32_t* colors, float* positions, Texture* get(uint32_t* colors, float* positions, int count);
int count, SkShader::TileMode tileMode = SkShader::kClamp_TileMode);
/** /**
* Clears the cache. This causes all textures to be deleted. * Clears the cache. This causes all textures to be deleted.
*/ */
@@ -144,10 +139,16 @@ private:
* returned. * returned.
*/ */
Texture* addLinearGradient(GradientCacheEntry& gradient, Texture* addLinearGradient(GradientCacheEntry& gradient,
uint32_t* colors, float* positions, int count, uint32_t* colors, float* positions, int count);
SkShader::TileMode tileMode = SkShader::kClamp_TileMode);
void generateTexture(SkBitmap* bitmap, Texture* texture); void generateTexture(uint32_t* colors, float* positions, int count, Texture* texture);
struct GradientInfo {
uint32_t width;
bool hasAlpha;
};
void getGradientInfo(const uint32_t* colors, const int count, GradientInfo& info);
GenerationCache<GradientCacheEntry, Texture*> mCache; GenerationCache<GradientCacheEntry, Texture*> mCache;

View File

@@ -79,6 +79,8 @@ namespace uirenderer {
#define PROGRAM_HAS_GAMMA_CORRECTION 40 #define PROGRAM_HAS_GAMMA_CORRECTION 40
#define PROGRAM_IS_SIMPLE_GRADIENT 41
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Types // Types
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@@ -96,14 +98,14 @@ typedef uint64_t programid;
*/ */
struct ProgramDescription { struct ProgramDescription {
enum ColorModifier { enum ColorModifier {
kColorNone, kColorNone = 0,
kColorMatrix, kColorMatrix,
kColorLighting, kColorLighting,
kColorBlend kColorBlend
}; };
enum Gradient { enum Gradient {
kGradientLinear, kGradientLinear = 0,
kGradientCircular, kGradientCircular,
kGradientSweep kGradientSweep
}; };
@@ -129,6 +131,7 @@ struct ProgramDescription {
bool hasGradient; bool hasGradient;
Gradient gradientType; Gradient gradientType;
bool isSimpleGradient;
SkXfermode::Mode shadersMode; SkXfermode::Mode shadersMode;
@@ -170,6 +173,7 @@ struct ProgramDescription {
hasGradient = false; hasGradient = false;
gradientType = kGradientLinear; gradientType = kGradientLinear;
isSimpleGradient = false;
shadersMode = SkXfermode::kClear_Mode; shadersMode = SkXfermode::kClear_Mode;
@@ -255,6 +259,7 @@ struct ProgramDescription {
if (hasExternalTexture) key |= programid(0x1) << PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT; if (hasExternalTexture) key |= programid(0x1) << PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT;
if (hasTextureTransform) key |= programid(0x1) << PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT; if (hasTextureTransform) key |= programid(0x1) << PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT;
if (hasGammaCorrection) key |= programid(0x1) << PROGRAM_HAS_GAMMA_CORRECTION; if (hasGammaCorrection) key |= programid(0x1) << PROGRAM_HAS_GAMMA_CORRECTION;
if (isSimpleGradient) key |= programid(0x1) << PROGRAM_IS_SIMPLE_GRADIENT;
return key; return key;
} }

View File

@@ -71,13 +71,16 @@ const char* gVS_Header_Varyings_PointHasBitmap =
"varying highp vec2 outPointBitmapTexCoords;\n"; "varying highp vec2 outPointBitmapTexCoords;\n";
// TODO: These values are used to sample from textures, // TODO: These values are used to sample from textures,
// they may need to be highp // they may need to be highp
const char* gVS_Header_Varyings_HasGradient[3] = { const char* gVS_Header_Varyings_HasGradient[6] = {
// Linear // Linear
"varying highp vec2 linear;\n", "varying highp vec2 linear;\n",
"varying highp float linear;\n",
// Circular // Circular
"varying highp vec2 circular;\n", "varying highp vec2 circular;\n",
"varying highp vec2 circular;\n",
// Sweep // Sweep
"varying highp vec2 sweep;\n" "varying highp vec2 sweep;\n",
"varying highp vec2 sweep;\n",
}; };
const char* gVS_Main = const char* gVS_Main =
"\nvoid main(void) {\n"; "\nvoid main(void) {\n";
@@ -85,13 +88,16 @@ const char* gVS_Main_OutTexCoords =
" outTexCoords = texCoords;\n"; " outTexCoords = texCoords;\n";
const char* gVS_Main_OutTransformedTexCoords = const char* gVS_Main_OutTransformedTexCoords =
" outTexCoords = (mainTextureTransform * vec4(texCoords, 0.0, 1.0)).xy;\n"; " outTexCoords = (mainTextureTransform * vec4(texCoords, 0.0, 1.0)).xy;\n";
const char* gVS_Main_OutGradient[3] = { const char* gVS_Main_OutGradient[6] = {
// Linear // Linear
" linear = vec2((screenSpace * position).x, 0.5);\n", " linear = vec2((screenSpace * position).x, 0.5);\n",
" linear = (screenSpace * position).x;\n",
// Circular // Circular
" circular = (screenSpace * position).xy;\n", " circular = (screenSpace * position).xy;\n",
" circular = (screenSpace * position).xy;\n",
// Sweep // Sweep
" sweep = (screenSpace * position).xy;\n" " sweep = (screenSpace * position).xy;\n",
" sweep = (screenSpace * position).xy;\n",
}; };
const char* gVS_Main_OutBitmapTexCoords = const char* gVS_Main_OutBitmapTexCoords =
" outBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n"; " outBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n";
@@ -131,13 +137,19 @@ const char* gFS_Uniforms_TextureSampler =
"uniform sampler2D sampler;\n"; "uniform sampler2D sampler;\n";
const char* gFS_Uniforms_ExternalTextureSampler = const char* gFS_Uniforms_ExternalTextureSampler =
"uniform samplerExternalOES sampler;\n"; "uniform samplerExternalOES sampler;\n";
const char* gFS_Uniforms_GradientSampler[3] = { const char* gFS_Uniforms_GradientSampler[6] = {
// Linear // Linear
"uniform sampler2D gradientSampler;\n", "uniform sampler2D gradientSampler;\n",
"uniform vec4 startColor;\n"
"uniform vec4 endColor;\n",
// Circular // Circular
"uniform sampler2D gradientSampler;\n", "uniform sampler2D gradientSampler;\n",
"uniform vec4 startColor;\n"
"uniform vec4 endColor;\n",
// Sweep // Sweep
"uniform sampler2D gradientSampler;\n" "uniform sampler2D gradientSampler;\n",
"uniform vec4 startColor;\n"
"uniform vec4 endColor;\n",
}; };
const char* gFS_Uniforms_BitmapSampler = const char* gFS_Uniforms_BitmapSampler =
"uniform sampler2D bitmapSampler;\n"; "uniform sampler2D bitmapSampler;\n";
@@ -193,14 +205,22 @@ const char* gFS_Fast_SingleModulateA8Texture_ApplyGamma =
"\nvoid main(void) {\n" "\nvoid main(void) {\n"
" gl_FragColor = color * pow(texture2D(sampler, outTexCoords).a, gamma);\n" " gl_FragColor = color * pow(texture2D(sampler, outTexCoords).a, gamma);\n"
"}\n\n"; "}\n\n";
const char* gFS_Fast_SingleGradient = const char* gFS_Fast_SingleGradient[2] = {
"\nvoid main(void) {\n" "\nvoid main(void) {\n"
" gl_FragColor = texture2D(gradientSampler, linear);\n" " gl_FragColor = texture2D(gradientSampler, linear);\n"
"}\n\n"; "}\n\n",
const char* gFS_Fast_SingleModulateGradient = "\nvoid main(void) {\n"
" gl_FragColor = mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n"
"}\n\n"
};
const char* gFS_Fast_SingleModulateGradient[2] = {
"\nvoid main(void) {\n" "\nvoid main(void) {\n"
" gl_FragColor = color.a * texture2D(gradientSampler, linear);\n" " gl_FragColor = color.a * texture2D(gradientSampler, linear);\n"
"}\n\n"; "}\n\n",
"\nvoid main(void) {\n"
" gl_FragColor = color.a * mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n"
"}\n\n"
};
// General case // General case
const char* gFS_Main_FetchColor = const char* gFS_Main_FetchColor =
@@ -232,15 +252,18 @@ const char* gFS_Main_FetchA8Texture[2] = {
// Modulate // Modulate
" fragColor = color * texture2D(sampler, outTexCoords).a;\n" " fragColor = color * texture2D(sampler, outTexCoords).a;\n"
}; };
const char* gFS_Main_FetchGradient[3] = { const char* gFS_Main_FetchGradient[6] = {
// Linear // Linear
" vec4 gradientColor = texture2D(gradientSampler, linear);\n", " vec4 gradientColor = texture2D(gradientSampler, linear);\n",
" vec4 gradientColor = mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n",
// Circular // Circular
" highp float index = length(circular);\n" " vec4 gradientColor = texture2D(gradientSampler, vec2(length(circular), 0.5));\n",
" vec4 gradientColor = texture2D(gradientSampler, vec2(index, 0.5));\n", " vec4 gradientColor = mix(startColor, endColor, clamp(length(circular), 0.0, 1.0));\n",
// Sweep // Sweep
" highp float index = atan(sweep.y, sweep.x) * 0.15915494309; // inv(2 * PI)\n" " highp float index = atan(sweep.y, sweep.x) * 0.15915494309; // inv(2 * PI)\n"
" vec4 gradientColor = texture2D(gradientSampler, vec2(index - floor(index), 0.5));\n" " vec4 gradientColor = texture2D(gradientSampler, vec2(index - floor(index), 0.5));\n",
" highp float index = atan(sweep.y, sweep.x) * 0.15915494309; // inv(2 * PI)\n"
" vec4 gradientColor = mix(startColor, endColor, clamp(index - floor(index), 0.0, 1.0));\n"
}; };
const char* gFS_Main_FetchBitmap = const char* gFS_Main_FetchBitmap =
" vec4 bitmapColor = texture2D(bitmapSampler, outBitmapTexCoords);\n"; " vec4 bitmapColor = texture2D(bitmapSampler, outBitmapTexCoords);\n";
@@ -395,8 +418,11 @@ Program* ProgramCache::generateProgram(const ProgramDescription& description, pr
String8 vertexShader = generateVertexShader(description); String8 vertexShader = generateVertexShader(description);
String8 fragmentShader = generateFragmentShader(description); String8 fragmentShader = generateFragmentShader(description);
Program* program = new Program(description, vertexShader.string(), fragmentShader.string()); return new Program(description, vertexShader.string(), fragmentShader.string());
return program; }
static inline size_t gradientIndex(const ProgramDescription& description) {
return description.gradientType * 2 + description.isSimpleGradient;
} }
String8 ProgramCache::generateVertexShader(const ProgramDescription& description) { String8 ProgramCache::generateVertexShader(const ProgramDescription& description) {
@@ -430,7 +456,7 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description
shader.append(gVS_Header_Varyings_IsAA); shader.append(gVS_Header_Varyings_IsAA);
} }
if (description.hasGradient) { if (description.hasGradient) {
shader.append(gVS_Header_Varyings_HasGradient[description.gradientType]); shader.append(gVS_Header_Varyings_HasGradient[gradientIndex(description)]);
} }
if (description.hasBitmap) { if (description.hasBitmap) {
shader.append(description.isPoint ? shader.append(description.isPoint ?
@@ -449,7 +475,7 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description
shader.append(gVS_Main_AA); shader.append(gVS_Main_AA);
} }
if (description.hasGradient) { if (description.hasGradient) {
shader.append(gVS_Main_OutGradient[description.gradientType]); shader.append(gVS_Main_OutGradient[gradientIndex(description)]);
} }
if (description.hasBitmap) { if (description.hasBitmap) {
shader.append(description.isPoint ? shader.append(description.isPoint ?
@@ -491,7 +517,7 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
shader.append(gVS_Header_Varyings_IsAA); shader.append(gVS_Header_Varyings_IsAA);
} }
if (description.hasGradient) { if (description.hasGradient) {
shader.append(gVS_Header_Varyings_HasGradient[description.gradientType]); shader.append(gVS_Header_Varyings_HasGradient[gradientIndex(description)]);
} }
if (description.hasBitmap) { if (description.hasBitmap) {
shader.append(description.isPoint ? shader.append(description.isPoint ?
@@ -517,7 +543,7 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
shader.append(gFS_Uniforms_AA); shader.append(gFS_Uniforms_AA);
} }
if (description.hasGradient) { if (description.hasGradient) {
shader.append(gFS_Uniforms_GradientSampler[description.gradientType]); shader.append(gFS_Uniforms_GradientSampler[gradientIndex(description)]);
} }
if (description.hasBitmap && description.isPoint) { if (description.hasBitmap && description.isPoint) {
shader.append(gFS_Header_Uniforms_PointHasBitmap); shader.append(gFS_Header_Uniforms_PointHasBitmap);
@@ -567,9 +593,9 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
fast = true; fast = true;
} else if (singleGradient) { } else if (singleGradient) {
if (!description.modulate) { if (!description.modulate) {
shader.append(gFS_Fast_SingleGradient); shader.append(gFS_Fast_SingleGradient[description.isSimpleGradient]);
} else { } else {
shader.append(gFS_Fast_SingleModulateGradient); shader.append(gFS_Fast_SingleModulateGradient[description.isSimpleGradient]);
} }
fast = true; fast = true;
} }
@@ -624,7 +650,7 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
shader.append(gFS_Main_AccountForAA); shader.append(gFS_Main_AccountForAA);
} }
if (description.hasGradient) { if (description.hasGradient) {
shader.append(gFS_Main_FetchGradient[description.gradientType]); shader.append(gFS_Main_FetchGradient[gradientIndex(description)]);
} }
if (description.hasBitmap) { if (description.hasBitmap) {
if (description.isPoint) { if (description.isPoint) {

View File

@@ -38,6 +38,21 @@ static const GLint gTileModes[] = {
GL_MIRRORED_REPEAT // == SkShader::kMirror_TileMode GL_MIRRORED_REPEAT // == SkShader::kMirror_TileMode
}; };
/**
* This function does not work for n == 0.
*/
static inline bool isPowerOfTwo(unsigned int n) {
return !(n & (n - 1));
}
static inline void bindUniformColor(int slot, uint32_t color) {
glUniform4f(slot,
((color >> 16) & 0xff) / 255.0f,
((color >> 8) & 0xff) / 255.0f,
((color ) & 0xff) / 255.0f,
((color >> 24) & 0xff) / 255.0f);
}
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Base shader // Base shader
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@@ -188,6 +203,8 @@ SkiaLinearGradientShader::SkiaLinearGradientShader(float* bounds, uint32_t* colo
mUnitMatrix.load(unitMatrix); mUnitMatrix.load(unitMatrix);
updateLocalMatrix(matrix); updateLocalMatrix(matrix);
mIsSimple = count == 2 && tileMode == SkShader::kClamp_TileMode;
} }
SkiaLinearGradientShader::~SkiaLinearGradientShader() { SkiaLinearGradientShader::~SkiaLinearGradientShader() {
@@ -206,6 +223,7 @@ SkiaShader* SkiaLinearGradientShader::copy() {
copy->mPositions = new float[mCount]; copy->mPositions = new float[mCount];
memcpy(copy->mPositions, mPositions, sizeof(float) * mCount); memcpy(copy->mPositions, mPositions, sizeof(float) * mCount);
copy->mCount = mCount; copy->mCount = mCount;
copy->mIsSimple = mIsSimple;
return copy; return copy;
} }
@@ -213,21 +231,27 @@ void SkiaLinearGradientShader::describe(ProgramDescription& description,
const Extensions& extensions) { const Extensions& extensions) {
description.hasGradient = true; description.hasGradient = true;
description.gradientType = ProgramDescription::kGradientLinear; description.gradientType = ProgramDescription::kGradientLinear;
description.isSimpleGradient = mIsSimple;
} }
void SkiaLinearGradientShader::setupProgram(Program* program, const mat4& modelView, void SkiaLinearGradientShader::setupProgram(Program* program, const mat4& modelView,
const Snapshot& snapshot, GLuint* textureUnit) { const Snapshot& snapshot, GLuint* textureUnit) {
GLuint textureSlot = (*textureUnit)++; if (CC_UNLIKELY(!mIsSimple)) {
Caches::getInstance().activeTexture(textureSlot); GLuint textureSlot = (*textureUnit)++;
Caches::getInstance().activeTexture(textureSlot);
Texture* texture = mGradientCache->get(mColors, mPositions, mCount, mTileX); Texture* texture = mGradientCache->get(mColors, mPositions, mCount);
// Uniforms
bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]);
glUniform1i(program->getUniform("gradientSampler"), textureSlot);
} else {
bindUniformColor(program->getUniform("startColor"), mColors[0]);
bindUniformColor(program->getUniform("endColor"), mColors[1]);
}
mat4 screenSpace; mat4 screenSpace;
computeScreenSpaceMatrix(screenSpace, modelView); computeScreenSpaceMatrix(screenSpace, modelView);
// Uniforms
bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]);
glUniform1i(program->getUniform("gradientSampler"), textureSlot);
glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]); glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
} }
@@ -269,6 +293,7 @@ SkiaShader* SkiaCircularGradientShader::copy() {
copy->mPositions = new float[mCount]; copy->mPositions = new float[mCount];
memcpy(copy->mPositions, mPositions, sizeof(float) * mCount); memcpy(copy->mPositions, mPositions, sizeof(float) * mCount);
copy->mCount = mCount; copy->mCount = mCount;
copy->mIsSimple = mIsSimple;
return copy; return copy;
} }
@@ -276,6 +301,7 @@ void SkiaCircularGradientShader::describe(ProgramDescription& description,
const Extensions& extensions) { const Extensions& extensions) {
description.hasGradient = true; description.hasGradient = true;
description.gradientType = ProgramDescription::kGradientCircular; description.gradientType = ProgramDescription::kGradientCircular;
description.isSimpleGradient = mIsSimple;
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@@ -296,6 +322,8 @@ SkiaSweepGradientShader::SkiaSweepGradientShader(float x, float y, uint32_t* col
mUnitMatrix.load(unitMatrix); mUnitMatrix.load(unitMatrix);
updateLocalMatrix(matrix); updateLocalMatrix(matrix);
mIsSimple = count == 2;
} }
SkiaSweepGradientShader::SkiaSweepGradientShader(Type type, float x, float y, uint32_t* colors, SkiaSweepGradientShader::SkiaSweepGradientShader(Type type, float x, float y, uint32_t* colors,
@@ -303,6 +331,8 @@ SkiaSweepGradientShader::SkiaSweepGradientShader(Type type, float x, float y, ui
SkMatrix* matrix, bool blend): SkMatrix* matrix, bool blend):
SkiaShader(type, key, tileMode, tileMode, matrix, blend), SkiaShader(type, key, tileMode, tileMode, matrix, blend),
mColors(colors), mPositions(positions), mCount(count) { mColors(colors), mPositions(positions), mCount(count) {
mIsSimple = count == 2 && tileMode == SkShader::kClamp_TileMode;
} }
SkiaSweepGradientShader::~SkiaSweepGradientShader() { SkiaSweepGradientShader::~SkiaSweepGradientShader() {
@@ -318,6 +348,7 @@ SkiaShader* SkiaSweepGradientShader::copy() {
copy->mPositions = new float[mCount]; copy->mPositions = new float[mCount];
memcpy(copy->mPositions, mPositions, sizeof(float) * mCount); memcpy(copy->mPositions, mPositions, sizeof(float) * mCount);
copy->mCount = mCount; copy->mCount = mCount;
copy->mIsSimple = mIsSimple;
return copy; return copy;
} }
@@ -325,21 +356,27 @@ void SkiaSweepGradientShader::describe(ProgramDescription& description,
const Extensions& extensions) { const Extensions& extensions) {
description.hasGradient = true; description.hasGradient = true;
description.gradientType = ProgramDescription::kGradientSweep; description.gradientType = ProgramDescription::kGradientSweep;
description.isSimpleGradient = mIsSimple;
} }
void SkiaSweepGradientShader::setupProgram(Program* program, const mat4& modelView, void SkiaSweepGradientShader::setupProgram(Program* program, const mat4& modelView,
const Snapshot& snapshot, GLuint* textureUnit) { const Snapshot& snapshot, GLuint* textureUnit) {
GLuint textureSlot = (*textureUnit)++; if (CC_UNLIKELY(!mIsSimple)) {
Caches::getInstance().activeTexture(textureSlot); GLuint textureSlot = (*textureUnit)++;
Caches::getInstance().activeTexture(textureSlot);
Texture* texture = mGradientCache->get(mColors, mPositions, mCount); Texture* texture = mGradientCache->get(mColors, mPositions, mCount);
// Uniforms
bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]);
glUniform1i(program->getUniform("gradientSampler"), textureSlot);
} else {
bindUniformColor(program->getUniform("startColor"), mColors[0]);
bindUniformColor(program->getUniform("endColor"), mColors[1]);
}
mat4 screenSpace; mat4 screenSpace;
computeScreenSpaceMatrix(screenSpace, modelView); computeScreenSpaceMatrix(screenSpace, modelView);
// Uniforms
bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]);
glUniform1i(program->getUniform("gradientSampler"), textureSlot);
glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]); glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
} }

View File

@@ -154,13 +154,6 @@ private:
SkiaBitmapShader() { SkiaBitmapShader() {
} }
/**
* This method does not work for n == 0.
*/
inline bool isPowerOfTwo(unsigned int n) {
return !(n & (n - 1));
}
SkBitmap* mBitmap; SkBitmap* mBitmap;
Texture* mTexture; Texture* mTexture;
GLenum mWrapS; GLenum mWrapS;
@@ -185,6 +178,7 @@ private:
SkiaLinearGradientShader() { SkiaLinearGradientShader() {
} }
bool mIsSimple;
float* mBounds; float* mBounds;
uint32_t* mColors; uint32_t* mColors;
float* mPositions; float* mPositions;
@@ -211,6 +205,7 @@ protected:
SkiaSweepGradientShader() { SkiaSweepGradientShader() {
} }
bool mIsSimple;
uint32_t* mColors; uint32_t* mColors;
float* mPositions; float* mPositions;
int mCount; int mCount;

View File

@@ -337,8 +337,8 @@ public class Matrix {
final float y = 2.0f * (near * r_height); final float y = 2.0f * (near * r_height);
final float A = (right + left) * r_width; final float A = (right + left) * r_width;
final float B = (top + bottom) * r_height; final float B = (top + bottom) * r_height;
final float C = -(far + near) * r_depth; final float C = (far + near) * r_depth;
final float D = -2.0f * (far * near * r_depth); final float D = 2.0f * (far * near * r_depth);
m[offset + 0] = x; m[offset + 0] = x;
m[offset + 5] = y; m[offset + 5] = y;
m[offset + 8] = A; m[offset + 8] = A;

View File

@@ -32,6 +32,15 @@
<meta-data android:name="android.graphics.renderThread" android:value="true" /> <meta-data android:name="android.graphics.renderThread" android:value="true" />
<activity
android:name="GradientStopsActivity"
android:label="_GradientStops">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity <activity
android:name="PaintDrawFilterActivity" android:name="PaintDrawFilterActivity"
android:label="_DrawFilter"> android:label="_DrawFilter">

View File

@@ -0,0 +1,116 @@
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.test.hwui;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Shader;
import android.os.Bundle;
import android.view.View;
@SuppressWarnings("UnusedDeclaration")
public class GradientStopsActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new GradientView(this));
}
private class GradientView extends View {
public GradientView(Context context) {
super(context);
}
@Override
protected void onDraw(Canvas canvas) {
int[] colors = new int[] { 0xffff0000, 0xff0000ff };
float[] positions = new float[] { 0.3f, 0.6f };
LinearGradient gradient = new LinearGradient(0.0f, 0.0f, 256.0f, 0.0f,
colors, positions, Shader.TileMode.CLAMP);
Paint paint = new Paint();
paint.setShader(gradient);
canvas.drawRect(0.0f, 0.0f, 256.0f, 50.0f, paint);
colors = new int[] { 0xffff0000, 0xff0000ff, 0xff00ff00 };
positions = new float[] { 0.3f, 0.6f, 1.0f };
gradient = new LinearGradient(0.0f, 0.0f, 256.0f, 0.0f,
colors, positions, Shader.TileMode.CLAMP);
paint.setShader(gradient);
canvas.translate(0.0f, 75.0f);
canvas.drawRect(0.0f, 0.0f, 256.0f, 50.0f, paint);
colors = new int[] { 0xffff0000, 0xff0000ff, 0xff00ff00 };
positions = new float[] { 0.0f, 0.3f, 0.6f };
gradient = new LinearGradient(0.0f, 0.0f, 256.0f, 0.0f,
colors, positions, Shader.TileMode.CLAMP);
paint.setShader(gradient);
canvas.translate(0.0f, 75.0f);
canvas.drawRect(0.0f, 0.0f, 256.0f, 50.0f, paint);
colors = new int[] { 0xff000000, 0xffffffff };
gradient = new LinearGradient(0.0f, 0.0f, 256.0f, 0.0f,
colors, null, Shader.TileMode.CLAMP);
paint.setShader(gradient);
canvas.translate(0.0f, 75.0f);
canvas.drawRect(0.0f, 0.0f, 256.0f, 50.0f, paint);
gradient = new LinearGradient(0.0f, 0.0f, 256.0f, 0.0f,
colors, null, Shader.TileMode.REPEAT);
paint.setShader(gradient);
canvas.translate(0.0f, 75.0f);
canvas.drawRect(0.0f, 0.0f, 768.0f, 50.0f, paint);
gradient = new LinearGradient(0.0f, 0.0f, 256.0f, 0.0f,
colors, null, Shader.TileMode.MIRROR);
paint.setShader(gradient);
canvas.translate(0.0f, 75.0f);
canvas.drawRect(0.0f, 0.0f, 768.0f, 50.0f, paint);
gradient = new LinearGradient(0.0f, 0.0f, 256.0f, 0.0f,
colors, null, Shader.TileMode.CLAMP);
paint.setShader(gradient);
canvas.translate(0.0f, 75.0f);
canvas.drawRect(0.0f, 0.0f, 768.0f, 50.0f, paint);
gradient = new LinearGradient(0.0f, 0.0f, 768.0f, 0.0f,
colors, null, Shader.TileMode.CLAMP);
paint.setShader(gradient);
canvas.translate(0.0f, 75.0f);
canvas.drawRect(0.0f, 0.0f, 768.0f, 50.0f, paint);
}
}
}