diff options
Diffstat (limited to 'libs/hwui/GradientCache.cpp')
-rw-r--r-- | libs/hwui/GradientCache.cpp | 148 |
1 files changed, 107 insertions, 41 deletions
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp index 3678788..2e4e349 100644 --- a/libs/hwui/GradientCache.cpp +++ b/libs/hwui/GradientCache.cpp @@ -16,13 +16,9 @@ #define LOG_TAG "OpenGLRenderer" -#include <GLES2/gl2.h> - -#include <SkCanvas.h> -#include <SkGradientShader.h> - #include <utils/threads.h> +#include "Caches.h" #include "Debug.h" #include "GradientCache.h" #include "Properties.h" @@ -31,6 +27,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 /////////////////////////////////////////////////////////////////////////////// @@ -45,6 +57,8 @@ GradientCache::GradientCache(): INIT_LOGD(" Using default gradient cache size of %.2fMB", DEFAULT_GRADIENT_CACHE_SIZE); } + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); + mCache.setOnEntryRemovedListener(this); } @@ -83,7 +97,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; } @@ -97,14 +111,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; @@ -114,36 +127,45 @@ void GradientCache::clear() { mCache.clear(); } -Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient, - uint32_t* colors, float* positions, int count, SkShader::TileMode tileMode) { - SkBitmap bitmap; - bitmap.setConfig(SkBitmap::kARGB_8888_Config, 1024, 1); - bitmap.allocPixels(); - bitmap.eraseColor(0); +void GradientCache::getGradientInfo(const uint32_t* colors, const int count, + GradientInfo& info) { + uint32_t width = 256 * (count - 1); - SkCanvas canvas(bitmap); + if (!Caches::getInstance().extensions.hasNPot()) { + width = 1 << (31 - __builtin_clz(width)); + } + + bool hasAlpha = false; + 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(), 1.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); @@ -151,25 +173,69 @@ 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; + } + + float amount = (pos - start) / distance; + float oppAmount = 1.0f - amount; + + const float alpha = startA * oppAmount + endA * amount; + const float a = alpha / 255.0f; + *p++ = uint8_t(a * (startR * oppAmount + endR * amount)); + *p++ = uint8_t(a * (startG * oppAmount + endG * amount)); + *p++ = uint8_t(a * (startB * oppAmount + endB * amount)); + *p++ = uint8_t(alpha); } - texture->generation = bitmap->getGenerationID(); - texture->width = bitmap->width(); - texture->height = bitmap->height(); + 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); |