Improve gradients
Avoid using textures for common gradients (two stops from 0.0 to 1.0)
Change-Id: Iff55d21b126c8cfc4cfb701669f2339c8f6b131a
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index 7026eead..b1c4dfe 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -16,9 +16,6 @@
#define LOG_TAG "OpenGLRenderer"
-#include <SkCanvas.h>
-#include <SkGradientShader.h>
-
#include <utils/threads.h>
#include "Debug.h"
@@ -29,6 +26,22 @@
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
///////////////////////////////////////////////////////////////////////////////
@@ -83,7 +96,7 @@
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 +110,13 @@
// 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,39 +126,41 @@
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,
- uint32_t* colors, float* positions, int count, SkShader::TileMode tileMode) {
- int width = 256 * (count - 1);
- width = width < mMaxTextureSize ? width : mMaxTextureSize;
+ uint32_t* colors, float* positions, int count) {
- SkBitmap bitmap;
- bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, 4);
- bitmap.allocPixels();
- bitmap.eraseColor(0);
+ GradientInfo info;
+ getGradientInfo(colors, count, info);
- SkCanvas canvas(bitmap);
-
- SkPoint points[2];
- points[0].set(0.0f, 0.0f);
- points[1].set(bitmap.width(), 0.0f);
-
- 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);
+ 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);
@@ -154,25 +168,67 @@
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;
+
+ *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();
- 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);