Skip to content

Commit 413ab3a

Browse files
Romain GuyAndroid (Google) Code Review
authored andcommitted
Merge "Improve gradients" into jb-mr1-dev
2 parents 3805e8c + 42e1e0d commit 413ab3a

File tree

10 files changed

+393
-118
lines changed

10 files changed

+393
-118
lines changed

core/jni/android/graphics/Shader.cpp

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -150,19 +150,35 @@ static SkiaShader* LinearGradient_postCreate1(JNIEnv* env, jobject o, SkShader*
150150
jfloat* storedBounds = new jfloat[4];
151151
storedBounds[0] = x0; storedBounds[1] = y0;
152152
storedBounds[2] = x1; storedBounds[3] = y1;
153-
jfloat* storedPositions = new jfloat[count];
154-
uint32_t* storedColors = new uint32_t[count];
155-
for (size_t i = 0; i < count; i++) {
156-
storedColors[i] = static_cast<uint32_t>(colorValues[i]);
157-
}
153+
154+
bool missFirst = false;
155+
bool missLast = false;
156+
size_t stopCount = count;
158157

158+
jfloat* storedPositions = NULL;
159159
if (posArray) {
160160
AutoJavaFloatArray autoPos(env, posArray, count);
161161
const float* posValues = autoPos.ptr();
162-
for (size_t i = 0; i < count; i++) {
163-
storedPositions[i] = posValues[i];
162+
163+
missFirst = posValues[0] != 0.0f;
164+
missLast = posValues[count - 1] != 1.0f;
165+
166+
stopCount += missFirst + missLast;
167+
storedPositions = new jfloat[stopCount];
168+
169+
if (missFirst) {
170+
storedPositions[0] = 0.0f;
171+
}
172+
173+
for (size_t i = missFirst; i < count + missFirst; i++) {
174+
storedPositions[i] = posValues[i - missFirst];
175+
}
176+
177+
if (missLast) {
178+
storedPositions[stopCount - 1] = 1.0f;
164179
}
165180
} else {
181+
storedPositions = new jfloat[count];
166182
storedPositions[0] = 0.0f;
167183
const jfloat step = 1.0f / (count - 1);
168184
for (size_t i = 1; i < count - 1; i++) {
@@ -171,8 +187,22 @@ static SkiaShader* LinearGradient_postCreate1(JNIEnv* env, jobject o, SkShader*
171187
storedPositions[count - 1] = 1.0f;
172188
}
173189

190+
uint32_t* storedColors = new uint32_t[stopCount];
191+
192+
if (missFirst) {
193+
storedColors[0] = static_cast<uint32_t>(colorValues[0]);
194+
}
195+
196+
for (size_t i = missFirst; i < count + missFirst; i++) {
197+
storedColors[i] = static_cast<uint32_t>(colorValues[i - missFirst]);
198+
}
199+
200+
if (missLast) {
201+
storedColors[stopCount - 1] = static_cast<uint32_t>(colorValues[count - 1]);
202+
}
203+
174204
SkiaShader* skiaShader = new SkiaLinearGradientShader(storedBounds, storedColors,
175-
storedPositions, count, shader, static_cast<SkShader::TileMode>(tileMode), NULL,
205+
storedPositions, stopCount, shader, static_cast<SkShader::TileMode>(tileMode), NULL,
176206
(shader->getFlags() & SkShader::kOpaqueAlpha_Flag) == 0);
177207

178208
env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT);

libs/hwui/GradientCache.cpp

Lines changed: 98 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,6 @@
1616

1717
#define LOG_TAG "OpenGLRenderer"
1818

19-
#include <SkCanvas.h>
20-
#include <SkGradientShader.h>
21-
2219
#include <utils/threads.h>
2320

2421
#include "Debug.h"
@@ -28,6 +25,22 @@
2825
namespace android {
2926
namespace uirenderer {
3027

28+
///////////////////////////////////////////////////////////////////////////////
29+
// Defines
30+
///////////////////////////////////////////////////////////////////////////////
31+
32+
#define GRADIENT_TEXTURE_HEIGHT 2
33+
#define GRADIENT_BYTES_PER_PIXEL 4
34+
35+
///////////////////////////////////////////////////////////////////////////////
36+
// Functions
37+
///////////////////////////////////////////////////////////////////////////////
38+
39+
template<typename T>
40+
static inline T min(T a, T b) {
41+
return a < b ? a : b;
42+
}
43+
3144
///////////////////////////////////////////////////////////////////////////////
3245
// Constructors/destructor
3346
///////////////////////////////////////////////////////////////////////////////
@@ -83,7 +96,7 @@ void GradientCache::setMaxSize(uint32_t maxSize) {
8396

8497
void GradientCache::operator()(GradientCacheEntry& shader, Texture*& texture) {
8598
if (texture) {
86-
const uint32_t size = texture->width * texture->height * 4;
99+
const uint32_t size = texture->width * texture->height * GRADIENT_BYTES_PER_PIXEL;
87100
mSize -= size;
88101
}
89102

@@ -97,14 +110,13 @@ void GradientCache::operator()(GradientCacheEntry& shader, Texture*& texture) {
97110
// Caching
98111
///////////////////////////////////////////////////////////////////////////////
99112

100-
Texture* GradientCache::get(uint32_t* colors, float* positions,
101-
int count, SkShader::TileMode tileMode) {
113+
Texture* GradientCache::get(uint32_t* colors, float* positions, int count) {
102114

103-
GradientCacheEntry gradient(colors, positions, count, tileMode);
115+
GradientCacheEntry gradient(colors, positions, count);
104116
Texture* texture = mCache.get(gradient);
105117

106118
if (!texture) {
107-
texture = addLinearGradient(gradient, colors, positions, count, tileMode);
119+
texture = addLinearGradient(gradient, colors, positions, count);
108120
}
109121

110122
return texture;
@@ -114,65 +126,109 @@ void GradientCache::clear() {
114126
mCache.clear();
115127
}
116128

117-
Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient,
118-
uint32_t* colors, float* positions, int count, SkShader::TileMode tileMode) {
119-
int width = 256 * (count - 1);
120-
width = width < mMaxTextureSize ? width : mMaxTextureSize;
121-
122-
SkBitmap bitmap;
123-
bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, 4);
124-
bitmap.allocPixels();
125-
bitmap.eraseColor(0);
129+
void GradientCache::getGradientInfo(const uint32_t* colors, const int count,
130+
GradientInfo& info) {
131+
uint32_t width = 1 << (31 - __builtin_clz(256 * (count - 1)));
132+
bool hasAlpha = false;
126133

127-
SkCanvas canvas(bitmap);
134+
for (int i = 0; i < count; i++) {
135+
if (((colors[i] >> 24) & 0xff) < 255) {
136+
hasAlpha = true;
137+
break;
138+
}
139+
}
128140

129-
SkPoint points[2];
130-
points[0].set(0.0f, 0.0f);
131-
points[1].set(bitmap.width(), 0.0f);
141+
info.width = min(width, uint32_t(mMaxTextureSize));
142+
info.hasAlpha = hasAlpha;
143+
}
132144

133-
SkShader* localShader = SkGradientShader::CreateLinear(points,
134-
reinterpret_cast<const SkColor*>(colors), positions, count, tileMode);
145+
Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient,
146+
uint32_t* colors, float* positions, int count) {
135147

136-
SkPaint p;
137-
p.setStyle(SkPaint::kStrokeAndFill_Style);
138-
p.setShader(localShader)->unref();
148+
GradientInfo info;
149+
getGradientInfo(colors, count, info);
139150

140-
canvas.drawRectCoords(0.0f, 0.0f, bitmap.width(), 4.0f, p);
151+
Texture* texture = new Texture;
152+
texture->width = info.width;
153+
texture->height = GRADIENT_TEXTURE_HEIGHT;
154+
texture->blend = info.hasAlpha;
155+
texture->generation = 1;
141156

142157
// Asume the cache is always big enough
143-
const uint32_t size = bitmap.rowBytes() * bitmap.height();
158+
const uint32_t size = texture->width * texture->height * GRADIENT_BYTES_PER_PIXEL;
144159
while (mSize + size > mMaxSize) {
145160
mCache.removeOldest();
146161
}
147162

148-
Texture* texture = new Texture;
149-
generateTexture(&bitmap, texture);
163+
generateTexture(colors, positions, count, texture);
150164

151165
mSize += size;
152166
mCache.put(gradient, texture);
153167

154168
return texture;
155169
}
156170

157-
void GradientCache::generateTexture(SkBitmap* bitmap, Texture* texture) {
158-
SkAutoLockPixels autoLock(*bitmap);
159-
if (!bitmap->readyToDraw()) {
160-
ALOGE("Cannot generate texture from shader");
161-
return;
171+
void GradientCache::generateTexture(uint32_t* colors, float* positions,
172+
int count, Texture* texture) {
173+
174+
const uint32_t width = texture->width;
175+
const GLsizei rowBytes = width * GRADIENT_BYTES_PER_PIXEL;
176+
uint32_t pixels[width * texture->height];
177+
178+
int currentPos = 1;
179+
180+
float startA = (colors[0] >> 24) & 0xff;
181+
float startR = (colors[0] >> 16) & 0xff;
182+
float startG = (colors[0] >> 8) & 0xff;
183+
float startB = (colors[0] >> 0) & 0xff;
184+
185+
float endA = (colors[1] >> 24) & 0xff;
186+
float endR = (colors[1] >> 16) & 0xff;
187+
float endG = (colors[1] >> 8) & 0xff;
188+
float endB = (colors[1] >> 0) & 0xff;
189+
190+
float start = positions[0];
191+
float distance = positions[1] - start;
192+
193+
uint8_t* p = (uint8_t*) pixels;
194+
for (uint32_t x = 0; x < width; x++) {
195+
float pos = x / float(width - 1);
196+
if (pos > positions[currentPos]) {
197+
startA = endA;
198+
startR = endR;
199+
startG = endG;
200+
startB = endB;
201+
start = positions[currentPos];
202+
203+
currentPos++;
204+
205+
endA = (colors[currentPos] >> 24) & 0xff;
206+
endR = (colors[currentPos] >> 16) & 0xff;
207+
endG = (colors[currentPos] >> 8) & 0xff;
208+
endB = (colors[currentPos] >> 0) & 0xff;
209+
distance = positions[currentPos] - start;
210+
}
211+
212+
float amount = (pos - start) / distance;
213+
float oppAmount = 1.0f - amount;
214+
215+
*p++ = uint8_t(startR * oppAmount + endR * amount);
216+
*p++ = uint8_t(startG * oppAmount + endG * amount);
217+
*p++ = uint8_t(startB * oppAmount + endB * amount);
218+
*p++ = uint8_t(startA * oppAmount + endA * amount);
162219
}
163220

164-
texture->generation = bitmap->getGenerationID();
165-
texture->width = bitmap->width();
166-
texture->height = bitmap->height();
221+
for (int i = 1; i < GRADIENT_TEXTURE_HEIGHT; i++) {
222+
memcpy(pixels + width * i, pixels, rowBytes);
223+
}
167224

168225
glGenTextures(1, &texture->id);
169226

170227
glBindTexture(GL_TEXTURE_2D, texture->id);
171-
glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
228+
glPixelStorei(GL_UNPACK_ALIGNMENT, GRADIENT_BYTES_PER_PIXEL);
172229

173-
texture->blend = !bitmap->isOpaque();
174-
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap->rowBytesAsPixels(), texture->height, 0,
175-
GL_RGBA, GL_UNSIGNED_BYTE, bitmap->getPixels());
230+
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, texture->height, 0,
231+
GL_RGBA, GL_UNSIGNED_BYTE, pixels);
176232

177233
texture->setFilter(GL_LINEAR);
178234
texture->setWrap(GL_CLAMP_TO_EDGE);

libs/hwui/GradientCache.h

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,14 @@ struct GradientCacheEntry {
3636
count = 0;
3737
colors = NULL;
3838
positions = NULL;
39-
tileMode = SkShader::kClamp_TileMode;
4039
}
4140

42-
GradientCacheEntry(uint32_t* colors, float* positions, int count,
43-
SkShader::TileMode tileMode) {
44-
copy(colors, positions, count, tileMode);
41+
GradientCacheEntry(uint32_t* colors, float* positions, int count) {
42+
copy(colors, positions, count);
4543
}
4644

4745
GradientCacheEntry(const GradientCacheEntry& entry) {
48-
copy(entry.colors, entry.positions, entry.count, entry.tileMode);
46+
copy(entry.colors, entry.positions, entry.count);
4947
}
5048

5149
~GradientCacheEntry() {
@@ -58,7 +56,7 @@ struct GradientCacheEntry {
5856
delete[] colors;
5957
delete[] positions;
6058

61-
copy(entry.colors, entry.positions, entry.count, entry.tileMode);
59+
copy(entry.colors, entry.positions, entry.count);
6260
}
6361

6462
return *this;
@@ -67,13 +65,11 @@ struct GradientCacheEntry {
6765
bool operator<(const GradientCacheEntry& r) const {
6866
const GradientCacheEntry& rhs = (const GradientCacheEntry&) r;
6967
LTE_INT(count) {
70-
LTE_INT(tileMode) {
71-
int result = memcmp(colors, rhs.colors, count * sizeof(uint32_t));
72-
if (result< 0) return true;
73-
else if (result == 0) {
74-
result = memcmp(positions, rhs.positions, count * sizeof(float));
75-
if (result < 0) return true;
76-
}
68+
int result = memcmp(colors, rhs.colors, count * sizeof(uint32_t));
69+
if (result< 0) return true;
70+
else if (result == 0) {
71+
result = memcmp(positions, rhs.positions, count * sizeof(float));
72+
if (result < 0) return true;
7773
}
7874
}
7975
return false;
@@ -86,11 +82,10 @@ struct GradientCacheEntry {
8682

8783
private:
8884

89-
void copy(uint32_t* colors, float* positions, int count, SkShader::TileMode tileMode) {
85+
void copy(uint32_t* colors, float* positions, int count) {
9086
this->count = count;
9187
this->colors = new uint32_t[count];
9288
this->positions = new float[count];
93-
this->tileMode = tileMode;
9489

9590
memcpy(this->colors, colors, count * sizeof(uint32_t));
9691
memcpy(this->positions, positions, count * sizeof(float));
@@ -118,8 +113,8 @@ class GradientCache: public OnEntryRemoved<GradientCacheEntry, Texture*> {
118113
/**
119114
* Returns the texture associated with the specified shader.
120115
*/
121-
Texture* get(uint32_t* colors, float* positions,
122-
int count, SkShader::TileMode tileMode = SkShader::kClamp_TileMode);
116+
Texture* get(uint32_t* colors, float* positions, int count);
117+
123118
/**
124119
* Clears the cache. This causes all textures to be deleted.
125120
*/
@@ -144,10 +139,16 @@ class GradientCache: public OnEntryRemoved<GradientCacheEntry, Texture*> {
144139
* returned.
145140
*/
146141
Texture* addLinearGradient(GradientCacheEntry& gradient,
147-
uint32_t* colors, float* positions, int count,
148-
SkShader::TileMode tileMode = SkShader::kClamp_TileMode);
142+
uint32_t* colors, float* positions, int count);
143+
144+
void generateTexture(uint32_t* colors, float* positions, int count, Texture* texture);
145+
146+
struct GradientInfo {
147+
uint32_t width;
148+
bool hasAlpha;
149+
};
149150

150-
void generateTexture(SkBitmap* bitmap, Texture* texture);
151+
void getGradientInfo(const uint32_t* colors, const int count, GradientInfo& info);
151152

152153
GenerationCache<GradientCacheEntry, Texture*> mCache;
153154

0 commit comments

Comments
 (0)