Loading core/jni/android/graphics/Shader.cpp +38 −8 Original line number Diff line number Diff line Loading @@ -150,19 +150,35 @@ static SkiaShader* LinearGradient_postCreate1(JNIEnv* env, jobject o, SkShader* jfloat* storedBounds = new jfloat[4]; storedBounds[0] = x0; storedBounds[1] = y0; storedBounds[2] = x1; storedBounds[3] = y1; 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]); } bool missFirst = false; bool missLast = false; size_t stopCount = count; jfloat* storedPositions = NULL; if (posArray) { AutoJavaFloatArray autoPos(env, posArray, count); 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 { storedPositions = new jfloat[count]; storedPositions[0] = 0.0f; const jfloat step = 1.0f / (count - 1); for (size_t i = 1; i < count - 1; i++) { Loading @@ -171,8 +187,22 @@ static SkiaShader* LinearGradient_postCreate1(JNIEnv* env, jobject o, SkShader* 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, storedPositions, count, shader, static_cast<SkShader::TileMode>(tileMode), NULL, storedPositions, stopCount, shader, static_cast<SkShader::TileMode>(tileMode), NULL, (shader->getFlags() & SkShader::kOpaqueAlpha_Flag) == 0); env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT); Loading libs/hwui/GradientCache.cpp +98 −42 Original line number Diff line number Diff line Loading @@ -16,9 +16,6 @@ #define LOG_TAG "OpenGLRenderer" #include <SkCanvas.h> #include <SkGradientShader.h> #include <utils/threads.h> #include "Debug.h" Loading @@ -28,6 +25,22 @@ namespace android { 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 /////////////////////////////////////////////////////////////////////////////// Loading Loading @@ -83,7 +96,7 @@ void GradientCache::setMaxSize(uint32_t maxSize) { void GradientCache::operator()(GradientCacheEntry& shader, Texture*& 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; } Loading @@ -97,14 +110,13 @@ void GradientCache::operator()(GradientCacheEntry& shader, Texture*& texture) { // Caching /////////////////////////////////////////////////////////////////////////////// Texture* GradientCache::get(uint32_t* colors, float* positions, int count, SkShader::TileMode tileMode) { Texture* GradientCache::get(uint32_t* colors, float* positions, int count) { GradientCacheEntry gradient(colors, positions, count, tileMode); GradientCacheEntry gradient(colors, positions, count); Texture* texture = mCache.get(gradient); if (!texture) { texture = addLinearGradient(gradient, colors, positions, count, tileMode); texture = addLinearGradient(gradient, colors, positions, count); } return texture; Loading @@ -114,39 +126,41 @@ void GradientCache::clear() { mCache.clear(); } Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient, uint32_t* colors, float* positions, int count, SkShader::TileMode tileMode) { int width = 256 * (count - 1); width = width < mMaxTextureSize ? width : mMaxTextureSize; SkBitmap bitmap; bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, 4); bitmap.allocPixels(); bitmap.eraseColor(0); 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; SkCanvas canvas(bitmap); for (int i = 0; i < count; i++) { if (((colors[i] >> 24) & 0xff) < 255) { hasAlpha = true; break; } } SkPoint points[2]; points[0].set(0.0f, 0.0f); points[1].set(bitmap.width(), 0.0f); info.width = min(width, uint32_t(mMaxTextureSize)); info.hasAlpha = hasAlpha; } SkShader* localShader = SkGradientShader::CreateLinear(points, reinterpret_cast<const SkColor*>(colors), positions, count, tileMode); Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient, uint32_t* colors, float* positions, int count) { SkPaint p; p.setStyle(SkPaint::kStrokeAndFill_Style); p.setShader(localShader)->unref(); GradientInfo info; getGradientInfo(colors, count, info); canvas.drawRectCoords(0.0f, 0.0f, bitmap.width(), 4.0f, p); Texture* texture = new Texture; texture->width = info.width; texture->height = GRADIENT_TEXTURE_HEIGHT; texture->blend = info.hasAlpha; texture->generation = 1; // 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) { mCache.removeOldest(); } Texture* texture = new Texture; generateTexture(&bitmap, texture); generateTexture(colors, positions, count, texture); mSize += size; mCache.put(gradient, texture); Loading @@ -154,25 +168,67 @@ Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient, return texture; } void GradientCache::generateTexture(SkBitmap* bitmap, Texture* texture) { SkAutoLockPixels autoLock(*bitmap); if (!bitmap->readyToDraw()) { ALOGE("Cannot generate texture from shader"); return; void GradientCache::generateTexture(uint32_t* colors, float* positions, int count, Texture* texture) { const uint32_t width = texture->width; 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; } texture->generation = bitmap->getGenerationID(); texture->width = bitmap->width(); texture->height = bitmap->height(); 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); } for (int i = 1; i < GRADIENT_TEXTURE_HEIGHT; i++) { memcpy(pixels + width * i, pixels, rowBytes); } glGenTextures(1, &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, bitmap->rowBytesAsPixels(), texture->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, bitmap->getPixels()); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, texture->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); texture->setFilter(GL_LINEAR); texture->setWrap(GL_CLAMP_TO_EDGE); Loading libs/hwui/GradientCache.h +21 −20 Original line number Diff line number Diff line Loading @@ -36,16 +36,14 @@ struct GradientCacheEntry { count = 0; colors = NULL; positions = NULL; tileMode = SkShader::kClamp_TileMode; } GradientCacheEntry(uint32_t* colors, float* positions, int count, SkShader::TileMode tileMode) { copy(colors, positions, count, tileMode); GradientCacheEntry(uint32_t* colors, float* positions, int count) { copy(colors, positions, count); } GradientCacheEntry(const GradientCacheEntry& entry) { copy(entry.colors, entry.positions, entry.count, entry.tileMode); copy(entry.colors, entry.positions, entry.count); } ~GradientCacheEntry() { Loading @@ -58,7 +56,7 @@ struct GradientCacheEntry { delete[] colors; delete[] positions; copy(entry.colors, entry.positions, entry.count, entry.tileMode); copy(entry.colors, entry.positions, entry.count); } return *this; Loading @@ -67,7 +65,6 @@ struct GradientCacheEntry { bool operator<(const GradientCacheEntry& r) const { const GradientCacheEntry& rhs = (const GradientCacheEntry&) r; LTE_INT(count) { LTE_INT(tileMode) { int result = memcmp(colors, rhs.colors, count * sizeof(uint32_t)); if (result< 0) return true; else if (result == 0) { Loading @@ -75,7 +72,6 @@ struct GradientCacheEntry { if (result < 0) return true; } } } return false; } Loading @@ -86,11 +82,10 @@ struct GradientCacheEntry { 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->colors = new uint32_t[count]; this->positions = new float[count]; this->tileMode = tileMode; memcpy(this->colors, colors, count * sizeof(uint32_t)); memcpy(this->positions, positions, count * sizeof(float)); Loading Loading @@ -118,8 +113,8 @@ public: /** * Returns the texture associated with the specified shader. */ Texture* get(uint32_t* colors, float* positions, int count, SkShader::TileMode tileMode = SkShader::kClamp_TileMode); Texture* get(uint32_t* colors, float* positions, int count); /** * Clears the cache. This causes all textures to be deleted. */ Loading @@ -144,10 +139,16 @@ private: * returned. */ Texture* addLinearGradient(GradientCacheEntry& gradient, uint32_t* colors, float* positions, int count, SkShader::TileMode tileMode = SkShader::kClamp_TileMode); uint32_t* colors, float* positions, int count); void generateTexture(uint32_t* colors, float* positions, int count, Texture* texture); struct GradientInfo { uint32_t width; bool hasAlpha; }; void generateTexture(SkBitmap* bitmap, Texture* texture); void getGradientInfo(const uint32_t* colors, const int count, GradientInfo& info); GenerationCache<GradientCacheEntry, Texture*> mCache; Loading libs/hwui/Program.h +7 −2 Original line number Diff line number Diff line Loading @@ -79,6 +79,8 @@ namespace uirenderer { #define PROGRAM_HAS_GAMMA_CORRECTION 40 #define PROGRAM_IS_SIMPLE_GRADIENT 41 /////////////////////////////////////////////////////////////////////////////// // Types /////////////////////////////////////////////////////////////////////////////// Loading @@ -96,14 +98,14 @@ typedef uint64_t programid; */ struct ProgramDescription { enum ColorModifier { kColorNone, kColorNone = 0, kColorMatrix, kColorLighting, kColorBlend }; enum Gradient { kGradientLinear, kGradientLinear = 0, kGradientCircular, kGradientSweep }; Loading @@ -129,6 +131,7 @@ struct ProgramDescription { bool hasGradient; Gradient gradientType; bool isSimpleGradient; SkXfermode::Mode shadersMode; Loading Loading @@ -170,6 +173,7 @@ struct ProgramDescription { hasGradient = false; gradientType = kGradientLinear; isSimpleGradient = false; shadersMode = SkXfermode::kClear_Mode; Loading Loading @@ -255,6 +259,7 @@ struct ProgramDescription { if (hasExternalTexture) key |= programid(0x1) << PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT; if (hasTextureTransform) key |= programid(0x1) << PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT; if (hasGammaCorrection) key |= programid(0x1) << PROGRAM_HAS_GAMMA_CORRECTION; if (isSimpleGradient) key |= programid(0x1) << PROGRAM_IS_SIMPLE_GRADIENT; return key; } Loading libs/hwui/ProgramCache.cpp +49 −23 Original line number Diff line number Diff line Loading @@ -71,13 +71,16 @@ const char* gVS_Header_Varyings_PointHasBitmap = "varying highp vec2 outPointBitmapTexCoords;\n"; // TODO: These values are used to sample from textures, // they may need to be highp const char* gVS_Header_Varyings_HasGradient[3] = { const char* gVS_Header_Varyings_HasGradient[6] = { // Linear "varying highp vec2 linear;\n", "varying highp float linear;\n", // Circular "varying highp vec2 circular;\n", "varying highp vec2 circular;\n", // Sweep "varying highp vec2 sweep;\n" "varying highp vec2 sweep;\n", "varying highp vec2 sweep;\n", }; const char* gVS_Main = "\nvoid main(void) {\n"; Loading @@ -85,13 +88,16 @@ const char* gVS_Main_OutTexCoords = " outTexCoords = texCoords;\n"; const char* gVS_Main_OutTransformedTexCoords = " outTexCoords = (mainTextureTransform * vec4(texCoords, 0.0, 1.0)).xy;\n"; const char* gVS_Main_OutGradient[3] = { const char* gVS_Main_OutGradient[6] = { // Linear " linear = vec2((screenSpace * position).x, 0.5);\n", " linear = (screenSpace * position).x;\n", // Circular " circular = (screenSpace * position).xy;\n", " circular = (screenSpace * position).xy;\n", // Sweep " sweep = (screenSpace * position).xy;\n" " sweep = (screenSpace * position).xy;\n", " sweep = (screenSpace * position).xy;\n", }; const char* gVS_Main_OutBitmapTexCoords = " outBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n"; Loading Loading @@ -131,13 +137,19 @@ const char* gFS_Uniforms_TextureSampler = "uniform sampler2D sampler;\n"; const char* gFS_Uniforms_ExternalTextureSampler = "uniform samplerExternalOES sampler;\n"; const char* gFS_Uniforms_GradientSampler[3] = { const char* gFS_Uniforms_GradientSampler[6] = { // Linear "uniform sampler2D gradientSampler;\n", "uniform vec4 startColor;\n" "uniform vec4 endColor;\n", // Circular "uniform sampler2D gradientSampler;\n", "uniform vec4 startColor;\n" "uniform vec4 endColor;\n", // Sweep "uniform sampler2D gradientSampler;\n" "uniform sampler2D gradientSampler;\n", "uniform vec4 startColor;\n" "uniform vec4 endColor;\n", }; const char* gFS_Uniforms_BitmapSampler = "uniform sampler2D bitmapSampler;\n"; Loading Loading @@ -193,14 +205,22 @@ const char* gFS_Fast_SingleModulateA8Texture_ApplyGamma = "\nvoid main(void) {\n" " gl_FragColor = color * pow(texture2D(sampler, outTexCoords).a, gamma);\n" "}\n\n"; const char* gFS_Fast_SingleGradient = const char* gFS_Fast_SingleGradient[2] = { "\nvoid main(void) {\n" " gl_FragColor = texture2D(gradientSampler, linear);\n" "}\n\n"; const char* gFS_Fast_SingleModulateGradient = "}\n\n", "\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" " 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 const char* gFS_Main_FetchColor = Loading Loading @@ -232,15 +252,18 @@ const char* gFS_Main_FetchA8Texture[2] = { // Modulate " fragColor = color * texture2D(sampler, outTexCoords).a;\n" }; const char* gFS_Main_FetchGradient[3] = { const char* gFS_Main_FetchGradient[6] = { // Linear " vec4 gradientColor = texture2D(gradientSampler, linear);\n", " vec4 gradientColor = mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n", // Circular " highp float index = length(circular);\n" " vec4 gradientColor = texture2D(gradientSampler, vec2(index, 0.5));\n", " vec4 gradientColor = texture2D(gradientSampler, vec2(length(circular), 0.5));\n", " vec4 gradientColor = mix(startColor, endColor, clamp(length(circular), 0.0, 1.0));\n", // Sweep " 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 = " vec4 bitmapColor = texture2D(bitmapSampler, outBitmapTexCoords);\n"; Loading Loading @@ -395,8 +418,11 @@ Program* ProgramCache::generateProgram(const ProgramDescription& description, pr String8 vertexShader = generateVertexShader(description); String8 fragmentShader = generateFragmentShader(description); Program* program = new Program(description, vertexShader.string(), fragmentShader.string()); return program; return new Program(description, vertexShader.string(), fragmentShader.string()); } static inline size_t gradientIndex(const ProgramDescription& description) { return description.gradientType * 2 + description.isSimpleGradient; } String8 ProgramCache::generateVertexShader(const ProgramDescription& description) { Loading Loading @@ -430,7 +456,7 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description shader.append(gVS_Header_Varyings_IsAA); } if (description.hasGradient) { shader.append(gVS_Header_Varyings_HasGradient[description.gradientType]); shader.append(gVS_Header_Varyings_HasGradient[gradientIndex(description)]); } if (description.hasBitmap) { shader.append(description.isPoint ? Loading @@ -449,7 +475,7 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description shader.append(gVS_Main_AA); } if (description.hasGradient) { shader.append(gVS_Main_OutGradient[description.gradientType]); shader.append(gVS_Main_OutGradient[gradientIndex(description)]); } if (description.hasBitmap) { shader.append(description.isPoint ? Loading Loading @@ -491,7 +517,7 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti shader.append(gVS_Header_Varyings_IsAA); } if (description.hasGradient) { shader.append(gVS_Header_Varyings_HasGradient[description.gradientType]); shader.append(gVS_Header_Varyings_HasGradient[gradientIndex(description)]); } if (description.hasBitmap) { shader.append(description.isPoint ? Loading @@ -517,7 +543,7 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti shader.append(gFS_Uniforms_AA); } if (description.hasGradient) { shader.append(gFS_Uniforms_GradientSampler[description.gradientType]); shader.append(gFS_Uniforms_GradientSampler[gradientIndex(description)]); } if (description.hasBitmap && description.isPoint) { shader.append(gFS_Header_Uniforms_PointHasBitmap); Loading Loading @@ -567,9 +593,9 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti fast = true; } else if (singleGradient) { if (!description.modulate) { shader.append(gFS_Fast_SingleGradient); shader.append(gFS_Fast_SingleGradient[description.isSimpleGradient]); } else { shader.append(gFS_Fast_SingleModulateGradient); shader.append(gFS_Fast_SingleModulateGradient[description.isSimpleGradient]); } fast = true; } Loading Loading @@ -624,7 +650,7 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti shader.append(gFS_Main_AccountForAA); } if (description.hasGradient) { shader.append(gFS_Main_FetchGradient[description.gradientType]); shader.append(gFS_Main_FetchGradient[gradientIndex(description)]); } if (description.hasBitmap) { if (description.isPoint) { Loading Loading
core/jni/android/graphics/Shader.cpp +38 −8 Original line number Diff line number Diff line Loading @@ -150,19 +150,35 @@ static SkiaShader* LinearGradient_postCreate1(JNIEnv* env, jobject o, SkShader* jfloat* storedBounds = new jfloat[4]; storedBounds[0] = x0; storedBounds[1] = y0; storedBounds[2] = x1; storedBounds[3] = y1; 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]); } bool missFirst = false; bool missLast = false; size_t stopCount = count; jfloat* storedPositions = NULL; if (posArray) { AutoJavaFloatArray autoPos(env, posArray, count); 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 { storedPositions = new jfloat[count]; storedPositions[0] = 0.0f; const jfloat step = 1.0f / (count - 1); for (size_t i = 1; i < count - 1; i++) { Loading @@ -171,8 +187,22 @@ static SkiaShader* LinearGradient_postCreate1(JNIEnv* env, jobject o, SkShader* 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, storedPositions, count, shader, static_cast<SkShader::TileMode>(tileMode), NULL, storedPositions, stopCount, shader, static_cast<SkShader::TileMode>(tileMode), NULL, (shader->getFlags() & SkShader::kOpaqueAlpha_Flag) == 0); env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT); Loading
libs/hwui/GradientCache.cpp +98 −42 Original line number Diff line number Diff line Loading @@ -16,9 +16,6 @@ #define LOG_TAG "OpenGLRenderer" #include <SkCanvas.h> #include <SkGradientShader.h> #include <utils/threads.h> #include "Debug.h" Loading @@ -28,6 +25,22 @@ namespace android { 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 /////////////////////////////////////////////////////////////////////////////// Loading Loading @@ -83,7 +96,7 @@ void GradientCache::setMaxSize(uint32_t maxSize) { void GradientCache::operator()(GradientCacheEntry& shader, Texture*& 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; } Loading @@ -97,14 +110,13 @@ void GradientCache::operator()(GradientCacheEntry& shader, Texture*& texture) { // Caching /////////////////////////////////////////////////////////////////////////////// Texture* GradientCache::get(uint32_t* colors, float* positions, int count, SkShader::TileMode tileMode) { Texture* GradientCache::get(uint32_t* colors, float* positions, int count) { GradientCacheEntry gradient(colors, positions, count, tileMode); GradientCacheEntry gradient(colors, positions, count); Texture* texture = mCache.get(gradient); if (!texture) { texture = addLinearGradient(gradient, colors, positions, count, tileMode); texture = addLinearGradient(gradient, colors, positions, count); } return texture; Loading @@ -114,39 +126,41 @@ void GradientCache::clear() { mCache.clear(); } Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient, uint32_t* colors, float* positions, int count, SkShader::TileMode tileMode) { int width = 256 * (count - 1); width = width < mMaxTextureSize ? width : mMaxTextureSize; SkBitmap bitmap; bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, 4); bitmap.allocPixels(); bitmap.eraseColor(0); 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; SkCanvas canvas(bitmap); for (int i = 0; i < count; i++) { if (((colors[i] >> 24) & 0xff) < 255) { hasAlpha = true; break; } } SkPoint points[2]; points[0].set(0.0f, 0.0f); points[1].set(bitmap.width(), 0.0f); info.width = min(width, uint32_t(mMaxTextureSize)); info.hasAlpha = hasAlpha; } SkShader* localShader = SkGradientShader::CreateLinear(points, reinterpret_cast<const SkColor*>(colors), positions, count, tileMode); Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient, uint32_t* colors, float* positions, int count) { SkPaint p; p.setStyle(SkPaint::kStrokeAndFill_Style); p.setShader(localShader)->unref(); GradientInfo info; getGradientInfo(colors, count, info); canvas.drawRectCoords(0.0f, 0.0f, bitmap.width(), 4.0f, p); Texture* texture = new Texture; texture->width = info.width; texture->height = GRADIENT_TEXTURE_HEIGHT; texture->blend = info.hasAlpha; texture->generation = 1; // 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) { mCache.removeOldest(); } Texture* texture = new Texture; generateTexture(&bitmap, texture); generateTexture(colors, positions, count, texture); mSize += size; mCache.put(gradient, texture); Loading @@ -154,25 +168,67 @@ Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient, return texture; } void GradientCache::generateTexture(SkBitmap* bitmap, Texture* texture) { SkAutoLockPixels autoLock(*bitmap); if (!bitmap->readyToDraw()) { ALOGE("Cannot generate texture from shader"); return; void GradientCache::generateTexture(uint32_t* colors, float* positions, int count, Texture* texture) { const uint32_t width = texture->width; 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; } texture->generation = bitmap->getGenerationID(); texture->width = bitmap->width(); texture->height = bitmap->height(); 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); } for (int i = 1; i < GRADIENT_TEXTURE_HEIGHT; i++) { memcpy(pixels + width * i, pixels, rowBytes); } glGenTextures(1, &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, bitmap->rowBytesAsPixels(), texture->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, bitmap->getPixels()); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, texture->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); texture->setFilter(GL_LINEAR); texture->setWrap(GL_CLAMP_TO_EDGE); Loading
libs/hwui/GradientCache.h +21 −20 Original line number Diff line number Diff line Loading @@ -36,16 +36,14 @@ struct GradientCacheEntry { count = 0; colors = NULL; positions = NULL; tileMode = SkShader::kClamp_TileMode; } GradientCacheEntry(uint32_t* colors, float* positions, int count, SkShader::TileMode tileMode) { copy(colors, positions, count, tileMode); GradientCacheEntry(uint32_t* colors, float* positions, int count) { copy(colors, positions, count); } GradientCacheEntry(const GradientCacheEntry& entry) { copy(entry.colors, entry.positions, entry.count, entry.tileMode); copy(entry.colors, entry.positions, entry.count); } ~GradientCacheEntry() { Loading @@ -58,7 +56,7 @@ struct GradientCacheEntry { delete[] colors; delete[] positions; copy(entry.colors, entry.positions, entry.count, entry.tileMode); copy(entry.colors, entry.positions, entry.count); } return *this; Loading @@ -67,7 +65,6 @@ struct GradientCacheEntry { bool operator<(const GradientCacheEntry& r) const { const GradientCacheEntry& rhs = (const GradientCacheEntry&) r; LTE_INT(count) { LTE_INT(tileMode) { int result = memcmp(colors, rhs.colors, count * sizeof(uint32_t)); if (result< 0) return true; else if (result == 0) { Loading @@ -75,7 +72,6 @@ struct GradientCacheEntry { if (result < 0) return true; } } } return false; } Loading @@ -86,11 +82,10 @@ struct GradientCacheEntry { 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->colors = new uint32_t[count]; this->positions = new float[count]; this->tileMode = tileMode; memcpy(this->colors, colors, count * sizeof(uint32_t)); memcpy(this->positions, positions, count * sizeof(float)); Loading Loading @@ -118,8 +113,8 @@ public: /** * Returns the texture associated with the specified shader. */ Texture* get(uint32_t* colors, float* positions, int count, SkShader::TileMode tileMode = SkShader::kClamp_TileMode); Texture* get(uint32_t* colors, float* positions, int count); /** * Clears the cache. This causes all textures to be deleted. */ Loading @@ -144,10 +139,16 @@ private: * returned. */ Texture* addLinearGradient(GradientCacheEntry& gradient, uint32_t* colors, float* positions, int count, SkShader::TileMode tileMode = SkShader::kClamp_TileMode); uint32_t* colors, float* positions, int count); void generateTexture(uint32_t* colors, float* positions, int count, Texture* texture); struct GradientInfo { uint32_t width; bool hasAlpha; }; void generateTexture(SkBitmap* bitmap, Texture* texture); void getGradientInfo(const uint32_t* colors, const int count, GradientInfo& info); GenerationCache<GradientCacheEntry, Texture*> mCache; Loading
libs/hwui/Program.h +7 −2 Original line number Diff line number Diff line Loading @@ -79,6 +79,8 @@ namespace uirenderer { #define PROGRAM_HAS_GAMMA_CORRECTION 40 #define PROGRAM_IS_SIMPLE_GRADIENT 41 /////////////////////////////////////////////////////////////////////////////// // Types /////////////////////////////////////////////////////////////////////////////// Loading @@ -96,14 +98,14 @@ typedef uint64_t programid; */ struct ProgramDescription { enum ColorModifier { kColorNone, kColorNone = 0, kColorMatrix, kColorLighting, kColorBlend }; enum Gradient { kGradientLinear, kGradientLinear = 0, kGradientCircular, kGradientSweep }; Loading @@ -129,6 +131,7 @@ struct ProgramDescription { bool hasGradient; Gradient gradientType; bool isSimpleGradient; SkXfermode::Mode shadersMode; Loading Loading @@ -170,6 +173,7 @@ struct ProgramDescription { hasGradient = false; gradientType = kGradientLinear; isSimpleGradient = false; shadersMode = SkXfermode::kClear_Mode; Loading Loading @@ -255,6 +259,7 @@ struct ProgramDescription { if (hasExternalTexture) key |= programid(0x1) << PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT; if (hasTextureTransform) key |= programid(0x1) << PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT; if (hasGammaCorrection) key |= programid(0x1) << PROGRAM_HAS_GAMMA_CORRECTION; if (isSimpleGradient) key |= programid(0x1) << PROGRAM_IS_SIMPLE_GRADIENT; return key; } Loading
libs/hwui/ProgramCache.cpp +49 −23 Original line number Diff line number Diff line Loading @@ -71,13 +71,16 @@ const char* gVS_Header_Varyings_PointHasBitmap = "varying highp vec2 outPointBitmapTexCoords;\n"; // TODO: These values are used to sample from textures, // they may need to be highp const char* gVS_Header_Varyings_HasGradient[3] = { const char* gVS_Header_Varyings_HasGradient[6] = { // Linear "varying highp vec2 linear;\n", "varying highp float linear;\n", // Circular "varying highp vec2 circular;\n", "varying highp vec2 circular;\n", // Sweep "varying highp vec2 sweep;\n" "varying highp vec2 sweep;\n", "varying highp vec2 sweep;\n", }; const char* gVS_Main = "\nvoid main(void) {\n"; Loading @@ -85,13 +88,16 @@ const char* gVS_Main_OutTexCoords = " outTexCoords = texCoords;\n"; const char* gVS_Main_OutTransformedTexCoords = " outTexCoords = (mainTextureTransform * vec4(texCoords, 0.0, 1.0)).xy;\n"; const char* gVS_Main_OutGradient[3] = { const char* gVS_Main_OutGradient[6] = { // Linear " linear = vec2((screenSpace * position).x, 0.5);\n", " linear = (screenSpace * position).x;\n", // Circular " circular = (screenSpace * position).xy;\n", " circular = (screenSpace * position).xy;\n", // Sweep " sweep = (screenSpace * position).xy;\n" " sweep = (screenSpace * position).xy;\n", " sweep = (screenSpace * position).xy;\n", }; const char* gVS_Main_OutBitmapTexCoords = " outBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n"; Loading Loading @@ -131,13 +137,19 @@ const char* gFS_Uniforms_TextureSampler = "uniform sampler2D sampler;\n"; const char* gFS_Uniforms_ExternalTextureSampler = "uniform samplerExternalOES sampler;\n"; const char* gFS_Uniforms_GradientSampler[3] = { const char* gFS_Uniforms_GradientSampler[6] = { // Linear "uniform sampler2D gradientSampler;\n", "uniform vec4 startColor;\n" "uniform vec4 endColor;\n", // Circular "uniform sampler2D gradientSampler;\n", "uniform vec4 startColor;\n" "uniform vec4 endColor;\n", // Sweep "uniform sampler2D gradientSampler;\n" "uniform sampler2D gradientSampler;\n", "uniform vec4 startColor;\n" "uniform vec4 endColor;\n", }; const char* gFS_Uniforms_BitmapSampler = "uniform sampler2D bitmapSampler;\n"; Loading Loading @@ -193,14 +205,22 @@ const char* gFS_Fast_SingleModulateA8Texture_ApplyGamma = "\nvoid main(void) {\n" " gl_FragColor = color * pow(texture2D(sampler, outTexCoords).a, gamma);\n" "}\n\n"; const char* gFS_Fast_SingleGradient = const char* gFS_Fast_SingleGradient[2] = { "\nvoid main(void) {\n" " gl_FragColor = texture2D(gradientSampler, linear);\n" "}\n\n"; const char* gFS_Fast_SingleModulateGradient = "}\n\n", "\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" " 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 const char* gFS_Main_FetchColor = Loading Loading @@ -232,15 +252,18 @@ const char* gFS_Main_FetchA8Texture[2] = { // Modulate " fragColor = color * texture2D(sampler, outTexCoords).a;\n" }; const char* gFS_Main_FetchGradient[3] = { const char* gFS_Main_FetchGradient[6] = { // Linear " vec4 gradientColor = texture2D(gradientSampler, linear);\n", " vec4 gradientColor = mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n", // Circular " highp float index = length(circular);\n" " vec4 gradientColor = texture2D(gradientSampler, vec2(index, 0.5));\n", " vec4 gradientColor = texture2D(gradientSampler, vec2(length(circular), 0.5));\n", " vec4 gradientColor = mix(startColor, endColor, clamp(length(circular), 0.0, 1.0));\n", // Sweep " 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 = " vec4 bitmapColor = texture2D(bitmapSampler, outBitmapTexCoords);\n"; Loading Loading @@ -395,8 +418,11 @@ Program* ProgramCache::generateProgram(const ProgramDescription& description, pr String8 vertexShader = generateVertexShader(description); String8 fragmentShader = generateFragmentShader(description); Program* program = new Program(description, vertexShader.string(), fragmentShader.string()); return program; return new Program(description, vertexShader.string(), fragmentShader.string()); } static inline size_t gradientIndex(const ProgramDescription& description) { return description.gradientType * 2 + description.isSimpleGradient; } String8 ProgramCache::generateVertexShader(const ProgramDescription& description) { Loading Loading @@ -430,7 +456,7 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description shader.append(gVS_Header_Varyings_IsAA); } if (description.hasGradient) { shader.append(gVS_Header_Varyings_HasGradient[description.gradientType]); shader.append(gVS_Header_Varyings_HasGradient[gradientIndex(description)]); } if (description.hasBitmap) { shader.append(description.isPoint ? Loading @@ -449,7 +475,7 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description shader.append(gVS_Main_AA); } if (description.hasGradient) { shader.append(gVS_Main_OutGradient[description.gradientType]); shader.append(gVS_Main_OutGradient[gradientIndex(description)]); } if (description.hasBitmap) { shader.append(description.isPoint ? Loading Loading @@ -491,7 +517,7 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti shader.append(gVS_Header_Varyings_IsAA); } if (description.hasGradient) { shader.append(gVS_Header_Varyings_HasGradient[description.gradientType]); shader.append(gVS_Header_Varyings_HasGradient[gradientIndex(description)]); } if (description.hasBitmap) { shader.append(description.isPoint ? Loading @@ -517,7 +543,7 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti shader.append(gFS_Uniforms_AA); } if (description.hasGradient) { shader.append(gFS_Uniforms_GradientSampler[description.gradientType]); shader.append(gFS_Uniforms_GradientSampler[gradientIndex(description)]); } if (description.hasBitmap && description.isPoint) { shader.append(gFS_Header_Uniforms_PointHasBitmap); Loading Loading @@ -567,9 +593,9 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti fast = true; } else if (singleGradient) { if (!description.modulate) { shader.append(gFS_Fast_SingleGradient); shader.append(gFS_Fast_SingleGradient[description.isSimpleGradient]); } else { shader.append(gFS_Fast_SingleModulateGradient); shader.append(gFS_Fast_SingleModulateGradient[description.isSimpleGradient]); } fast = true; } Loading Loading @@ -624,7 +650,7 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti shader.append(gFS_Main_AccountForAA); } if (description.hasGradient) { shader.append(gFS_Main_FetchGradient[description.gradientType]); shader.append(gFS_Main_FetchGradient[gradientIndex(description)]); } if (description.hasBitmap) { if (description.isPoint) { Loading