summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/hwui/Extensions.cpp15
-rw-r--r--libs/hwui/Extensions.h2
-rw-r--r--libs/hwui/FloatColor.h12
-rw-r--r--libs/hwui/GlopBuilder.cpp8
-rw-r--r--libs/hwui/GradientCache.cpp28
-rw-r--r--libs/hwui/GradientCache.h8
-rw-r--r--libs/hwui/ProgramCache.cpp34
-rw-r--r--libs/hwui/SkiaShader.cpp4
-rw-r--r--libs/hwui/utils/Color.h18
9 files changed, 87 insertions, 42 deletions
diff --git a/libs/hwui/Extensions.cpp b/libs/hwui/Extensions.cpp
index 4dc7536d60bc..1d675791b851 100644
--- a/libs/hwui/Extensions.cpp
+++ b/libs/hwui/Extensions.cpp
@@ -45,13 +45,18 @@ Extensions::Extensions() {
mHas1BitStencil = extensions.has("GL_OES_stencil1");
mHas4BitStencil = extensions.has("GL_OES_stencil4");
mHasUnpackSubImage = extensions.has("GL_EXT_unpack_subimage");
- mHasSRGB = extensions.has("GL_EXT_sRGB");
- mHasSRGBWriteControl = extensions.has("GL_EXT_sRGB_write_control");
- // If linear blending is enabled, the device must have ES3.0 and GL_EXT_sRGB_write_control
#ifdef ANDROID_ENABLE_LINEAR_BLENDING
- assert(mVersionMajor >= 3 || mHasSRGB);
- assert(mHasSRGBWriteControl);
+ mHasSRGB = mVersionMajor >= 3 || extensions.has("GL_EXT_sRGB");
+ mHasSRGBWriteControl = extensions.has("GL_EXT_sRGB_write_control");
+
+ // If linear blending is enabled, the device must have (ES3.0 or EXT_sRGB)
+ // and EXT_sRGB_write_control
+ LOG_ALWAYS_FATAL_IF(!mHasSRGB, "Linear blending requires ES 3.0 or EXT_sRGB");
+ LOG_ALWAYS_FATAL_IF(!mHasSRGBWriteControl, "Linear blending requires EXT_sRGB_write_control");
+#else
+ mHasSRGB = false;
+ mHasSRGBWriteControl = false;
#endif
const char* version = (const char*) glGetString(GL_VERSION);
diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h
index cc73c2317720..2c38507bd79a 100644
--- a/libs/hwui/Extensions.h
+++ b/libs/hwui/Extensions.h
@@ -43,7 +43,7 @@ public:
inline bool hasPixelBufferObjects() const { return mVersionMajor >= 3; }
inline bool hasOcclusionQueries() const { return mVersionMajor >= 3; }
inline bool hasFloatTextures() const { return mVersionMajor >= 3; }
- inline bool hasSRGB() const { return mVersionMajor >= 3 || mHasSRGB; }
+ inline bool hasSRGB() const { return mHasSRGB; }
inline bool hasSRGBWriteControl() const { return hasSRGB() && mHasSRGBWriteControl; }
inline int getMajorGlVersion() const { return mVersionMajor; }
diff --git a/libs/hwui/FloatColor.h b/libs/hwui/FloatColor.h
index 6d19b7ccdd79..9df73387c36a 100644
--- a/libs/hwui/FloatColor.h
+++ b/libs/hwui/FloatColor.h
@@ -28,8 +28,20 @@ namespace uirenderer {
struct FloatColor {
// "color" is a gamma-encoded sRGB color
// After calling this method, the color is stored as a pre-multiplied linear color
+ // if linear blending is enabled. Otherwise, the color is stored as a pre-multiplied
+ // gamma-encoded sRGB color
void set(uint32_t color) {
a = ((color >> 24) & 0xff) / 255.0f;
+ r = a * EOCF(((color >> 16) & 0xff) / 255.0f);
+ g = a * EOCF(((color >> 8) & 0xff) / 255.0f);
+ b = a * EOCF(((color ) & 0xff) / 255.0f);
+ }
+
+ // "color" is a gamma-encoded sRGB color
+ // After calling this method, the color is stored as a pre-multiplied linear color
+ // if linear blending is enabled.
+ void setSRGB(uint32_t color) {
+ a = ((color >> 24) & 0xff) / 255.0f;
r = a * EOCF_sRGB(((color >> 16) & 0xff) / 255.0f);
g = a * EOCF_sRGB(((color >> 8) & 0xff) / 255.0f);
b = a * EOCF_sRGB(((color ) & 0xff) / 255.0f);
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
index ff88d020030c..65922f68bd09 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -289,10 +289,10 @@ void GlopBuilder::setFill(int color, float alphaScale,
// Skia uses the range [0..255] for the addition vector, but we need
// the [0..1] range to apply the vector in GLSL
float* colorVector = mOutGlop->fill.filter.matrix.vector;
- colorVector[0] = EOCF_sRGB(srcColorMatrix[4] / 255.0f);
- colorVector[1] = EOCF_sRGB(srcColorMatrix[9] / 255.0f);
- colorVector[2] = EOCF_sRGB(srcColorMatrix[14] / 255.0f);
- colorVector[3] = EOCF_sRGB(srcColorMatrix[19] / 255.0f);
+ colorVector[0] = EOCF(srcColorMatrix[4] / 255.0f);
+ colorVector[1] = EOCF(srcColorMatrix[9] / 255.0f);
+ colorVector[2] = EOCF(srcColorMatrix[14] / 255.0f);
+ colorVector[3] = srcColorMatrix[19] / 255.0f; // alpha is linear
} else {
LOG_ALWAYS_FATAL("unsupported ColorFilter");
}
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index 8573ab078aaf..cd3ccf9b8fc7 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -185,22 +185,28 @@ size_t GradientCache::sourceBytesPerPixel() const {
return 4 * (mUseFloatTexture ? sizeof(float) : sizeof(uint8_t));
}
-void GradientCache::mixBytes(FloatColor& start, FloatColor& end, float amount,
- uint8_t*& dst) const {
+void GradientCache::mixBytes(const FloatColor& start, const FloatColor& end,
+ float amount, uint8_t*& dst) const {
float oppAmount = 1.0f - amount;
- *dst++ = uint8_t(OECF_sRGB((start.r * oppAmount + end.r * amount) * 255.0f));
- *dst++ = uint8_t(OECF_sRGB((start.g * oppAmount + end.g * amount) * 255.0f));
- *dst++ = uint8_t(OECF_sRGB((start.b * oppAmount + end.b * amount) * 255.0f));
- *dst++ = uint8_t( (start.a * oppAmount + end.a * amount) * 255.0f);
+ *dst++ = uint8_t(OECF_sRGB(start.r * oppAmount + end.r * amount) * 255.0f);
+ *dst++ = uint8_t(OECF_sRGB(start.g * oppAmount + end.g * amount) * 255.0f);
+ *dst++ = uint8_t(OECF_sRGB(start.b * oppAmount + end.b * amount) * 255.0f);
+ *dst++ = uint8_t( (start.a * oppAmount + end.a * amount) * 255.0f);
}
-void GradientCache::mixFloats(FloatColor& start, FloatColor& end, float amount,
- uint8_t*& dst) const {
+void GradientCache::mixFloats(const FloatColor& start, const FloatColor& end,
+ float amount, uint8_t*& dst) const {
float oppAmount = 1.0f - amount;
float* d = (float*) dst;
+#if ANDROID_LINEAR_BLENDING_ENABLED
*d++ = start.r * oppAmount + end.r * amount;
*d++ = start.g * oppAmount + end.g * amount;
*d++ = start.b * oppAmount + end.b * amount;
+#else
+ *d++ = OECF_sRGB(start.r * oppAmount + end.r * amount);
+ *d++ = OECF_sRGB(start.g * oppAmount + end.g * amount);
+ *d++ = OECF_sRGB(start.b * oppAmount + end.b * amount);
+#endif
*d++ = start.a * oppAmount + end.a * amount;
dst += 4 * sizeof(float);
}
@@ -217,10 +223,10 @@ void GradientCache::generateTexture(uint32_t* colors, float* positions,
ChannelMixer mix = gMixers[mUseFloatTexture];
FloatColor start;
- start.set(colors[0]);
+ start.setSRGB(colors[0]);
FloatColor end;
- end.set(colors[1]);
+ end.setSRGB(colors[1]);
int currentPos = 1;
float startPos = positions[0];
@@ -235,7 +241,7 @@ void GradientCache::generateTexture(uint32_t* colors, float* positions,
currentPos++;
- end.set(colors[currentPos]);
+ end.setSRGB(colors[currentPos]);
distance = positions[currentPos] - startPos;
}
diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h
index 3fd8e80dd64b..5e35435ed64c 100644
--- a/libs/hwui/GradientCache.h
+++ b/libs/hwui/GradientCache.h
@@ -154,11 +154,13 @@ private:
size_t bytesPerPixel() const;
size_t sourceBytesPerPixel() const;
- typedef void (GradientCache::*ChannelMixer)(FloatColor& start, FloatColor& end,
+ typedef void (GradientCache::*ChannelMixer)(const FloatColor& start, const FloatColor& end,
float amount, uint8_t*& dst) const;
- void mixBytes(FloatColor& start, FloatColor& end, float amount, uint8_t*& dst) const;
- void mixFloats(FloatColor& start, FloatColor& end, float amount, uint8_t*& dst) const;
+ void mixBytes(const FloatColor& start, const FloatColor& end,
+ float amount, uint8_t*& dst) const;
+ void mixFloats(const FloatColor& start, const FloatColor& end,
+ float amount, uint8_t*& dst) const;
LruCache<GradientCacheEntry, Texture*> mCache;
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index 315c60eccb3e..4ef6b85de9ad 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -163,25 +163,31 @@ const char* gFS_Uniforms_HasRoundRectClip =
// When we are writing to an sRGB framebuffer, we must do the following:
// EOCF(OECF(color) + dither)
// We approximate the transfer functions with gamma 2.0 to avoid branches and pow()
-// The dithering pattern is generated with a triangle noise generator in the range [-0.5,1.5[
+// The dithering pattern is generated with a triangle noise generator in the range [-0.0,1.0]
// TODO: Handle linear fp16 render targets
-const char* gFS_Dither_Functions =
- "\nmediump float triangleNoise(const highp vec2 n) {\n"
+const char* gFS_Gradient_Functions =
+ "\nfloat triangleNoise(const highp vec2 n) {\n"
" highp vec2 p = fract(n * vec2(5.3987, 5.4421));\n"
" p += dot(p.yx, p.xy + vec2(21.5351, 14.3137));\n"
" highp float xy = p.x * p.y;\n"
- " return fract(xy * 95.4307) + fract(xy * 75.04961) - 0.5;\n"
+ " return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0;\n"
"}\n";
-const char* gFS_Dither_Preamble[2] = {
+const char* gFS_Gradient_Preamble[2] = {
// Linear framebuffer
"\nvec4 dither(const vec4 color) {\n"
" return vec4(color.rgb + (triangleNoise(gl_FragCoord.xy * screenSize.xy) / 255.0), color.a);"
+ "}\n"
+ "\nvec4 gammaMix(const vec4 a, const vec4 b, float v) {\n"
+ " return pow(mix(a, b, v), vec4(vec3(1.0 / 2.2), 1.0));"
"}\n",
// sRGB framebuffer
"\nvec4 dither(const vec4 color) {\n"
" vec3 dithered = sqrt(color.rgb) + (triangleNoise(gl_FragCoord.xy * screenSize.xy) / 255.0);\n"
" return vec4(dithered * dithered, color.a);\n"
"}\n"
+ "\nvec4 gammaMix(const vec4 a, const vec4 b, float v) {\n"
+ " return mix(a, b, v);"
+ "}\n"
};
// Uses luminance coefficients from Rec.709 to choose the appropriate gamma
@@ -239,7 +245,7 @@ const char* gFS_Fast_SingleGradient[2] = {
" gl_FragColor = dither(texture2D(gradientSampler, linear));\n"
"}\n\n",
"\nvoid main(void) {\n"
- " gl_FragColor = dither(mix(startColor, endColor, clamp(linear, 0.0, 1.0)));\n"
+ " gl_FragColor = dither(gammaMix(startColor, endColor, clamp(linear, 0.0, 1.0)));\n"
"}\n\n",
};
const char* gFS_Fast_SingleModulateGradient[2] = {
@@ -247,7 +253,7 @@ const char* gFS_Fast_SingleModulateGradient[2] = {
" gl_FragColor = dither(color.a * texture2D(gradientSampler, linear));\n"
"}\n\n",
"\nvoid main(void) {\n"
- " gl_FragColor = dither(color.a * mix(startColor, endColor, clamp(linear, 0.0, 1.0)));\n"
+ " gl_FragColor = dither(color.a * gammaMix(startColor, endColor, clamp(linear, 0.0, 1.0)));\n"
"}\n\n"
};
@@ -279,19 +285,19 @@ 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",
+ " vec4 gradientColor = gammaMix(startColor, endColor, clamp(linear, 0.0, 1.0));\n",
// Circular
" vec4 gradientColor = texture2D(gradientSampler, vec2(length(circular), 0.5));\n",
- " vec4 gradientColor = mix(startColor, endColor, clamp(length(circular), 0.0, 1.0));\n",
+ " vec4 gradientColor = gammaMix(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",
" 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"
+ " vec4 gradientColor = gammaMix(startColor, endColor, clamp(index - floor(index), 0.0, 1.0));\n"
};
const char* gFS_Main_FetchBitmap =
" vec4 bitmapColor = texture2D(bitmapSampler, outBitmapTexCoords);\n";
@@ -665,8 +671,8 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
}
fast = true;
} else if (singleGradient) {
- shader.append(gFS_Dither_Functions);
- shader.append(gFS_Dither_Preamble[mHasSRGB]);
+ shader.append(gFS_Gradient_Functions);
+ shader.append(gFS_Gradient_Preamble[mHasSRGB]);
if (!description.modulate) {
shader.append(gFS_Fast_SingleGradient[description.isSimpleGradient]);
} else {
@@ -705,8 +711,8 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
generateTextureWrap(shader, description.bitmapWrapS, description.bitmapWrapT);
}
if (description.hasGradient) {
- shader.append(gFS_Dither_Functions);
- shader.append(gFS_Dither_Preamble[mHasSRGB]);
+ shader.append(gFS_Gradient_Functions);
+ shader.append(gFS_Gradient_Preamble[mHasSRGB]);
}
// Begin the shader
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index 9838df2f6013..5d9e5c035283 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -173,8 +173,8 @@ bool tryStoreGradient(Caches& caches, const SkShader& shader, const Matrix4 mode
outData->gradientSampler = 0;
outData->gradientTexture = nullptr;
- outData->startColor.set(gradInfo.fColors[0]);
- outData->endColor.set(gradInfo.fColors[1]);
+ outData->startColor.setSRGB(gradInfo.fColors[0]);
+ outData->endColor.setSRGB(gradInfo.fColors[1]);
}
return true;
diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h
index c8f8c7071075..f9cc46d1cf30 100644
--- a/libs/hwui/utils/Color.h
+++ b/libs/hwui/utils/Color.h
@@ -85,10 +85,17 @@ namespace uirenderer {
// Opto-electronic conversion function for the sRGB color space
// Takes a gamma-encoded sRGB value and converts it to a linear sRGB value
static constexpr float OECF_sRGB(float linear) {
-#ifdef ANDROID_ENABLE_LINEAR_BLENDING
// IEC 61966-2-1:1999
return linear <= 0.0031308f ?
linear * 12.92f : (powf(linear, 1.0f / 2.4f) * 1.055f) - 0.055f;
+ }
+
+ // Opto-electronic conversion function for the sRGB color space
+ // Takes a gamma-encoded sRGB value and converts it to a linear sRGB value
+ // This function returns the input unmodified if linear blending is not enabled
+ static constexpr float OECF(float linear) {
+#ifdef ANDROID_ENABLE_LINEAR_BLENDING
+ return OECF_sRGB(linear);
#else
return linear;
#endif
@@ -97,9 +104,16 @@ namespace uirenderer {
// Electro-optical conversion function for the sRGB color space
// Takes a linear sRGB value and converts it to a gamma-encoded sRGB value
static constexpr float EOCF_sRGB(float srgb) {
-#ifdef ANDROID_ENABLE_LINEAR_BLENDING
// IEC 61966-2-1:1999
return srgb <= 0.04045f ? srgb / 12.92f : powf((srgb + 0.055f) / 1.055f, 2.4f);
+ }
+
+ // Electro-optical conversion function for the sRGB color space
+ // Takes a linear sRGB value and converts it to a gamma-encoded sRGB value
+ // This function returns the input unmodified if linear blending is not enabled
+ static constexpr float EOCF(float srgb) {
+#ifdef ANDROID_ENABLE_LINEAR_BLENDING
+ return EOCF_sRGB(srgb);
#else
return srgb;
#endif