diff options
author | 2016-12-12 18:21:32 -0800 | |
---|---|---|
committer | 2016-12-12 18:50:07 -0800 | |
commit | a0ed6f03f6f06eb41cbcc15c0a99b4a78fd91bef (patch) | |
tree | f6eeb56420b457fe9955f21548ca12e6cf7ec68e | |
parent | 84cac20dfdff35932901e978e6b6d3da843a2fa7 (diff) |
Pre-multiply gradient colors the right way
Alpha pre-multiplication must be done after applying the
opto-electronic transfer function when linear blending is
disabled. The correct way would be to pre-multiply before
gamma encoding but this leads to improper blending which
cannot be corrected without using sRGB frame buffers and
texture sampling.
Bug: 33010587
Test: cts-tradefed run singleCommand cts-dev --module CtsUiRenderingTestCases --test android.uirendering.cts.testclasses.GradientTests
Change-Id: I5f04bda4cb9f63674537aef5931621c14d601884
-rw-r--r-- | libs/hwui/FloatColor.h | 12 | ||||
-rw-r--r-- | libs/hwui/GradientCache.cpp | 37 | ||||
-rw-r--r-- | libs/hwui/ProgramCache.cpp | 8 | ||||
-rw-r--r-- | libs/hwui/SkiaShader.cpp | 8 |
4 files changed, 36 insertions, 29 deletions
diff --git a/libs/hwui/FloatColor.h b/libs/hwui/FloatColor.h index 9df73387c36a..d8afa35d32bf 100644 --- a/libs/hwui/FloatColor.h +++ b/libs/hwui/FloatColor.h @@ -38,13 +38,13 @@ 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. - void setSRGB(uint32_t color) { + // After calling this method, the color is stored as a linear color. The color + // is not pre-multiplied. + void setUnPreMultipliedSRGB(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); + r = EOCF_sRGB(((color >> 16) & 0xff) / 255.0f); + g = EOCF_sRGB(((color >> 8) & 0xff) / 255.0f); + b = EOCF_sRGB(((color ) & 0xff) / 255.0f); } bool isNotBlack() { diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp index 0972ac1cafc2..1dad58fd64b9 100644 --- a/libs/hwui/GradientCache.cpp +++ b/libs/hwui/GradientCache.cpp @@ -188,26 +188,28 @@ size_t GradientCache::sourceBytesPerPixel() 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); + float a = start.a * oppAmount + end.a * amount; + *dst++ = uint8_t(a * OECF_sRGB((start.r * oppAmount + end.r * amount)) * 255.0f); + *dst++ = uint8_t(a * OECF_sRGB((start.g * oppAmount + end.g * amount)) * 255.0f); + *dst++ = uint8_t(a * OECF_sRGB((start.b * oppAmount + end.b * amount)) * 255.0f); + *dst++ = uint8_t(a * 255.0f); } void GradientCache::mixFloats(const FloatColor& start, const FloatColor& end, float amount, uint8_t*& dst) const { float oppAmount = 1.0f - amount; + float a = start.a * oppAmount + end.a * amount; float* d = (float*) dst; #ifdef ANDROID_ENABLE_LINEAR_BLENDING - *d++ = start.r * oppAmount + end.r * amount; - *d++ = start.g * oppAmount + end.g * amount; - *d++ = start.b * oppAmount + end.b * amount; + *d++ = a * (start.r * oppAmount + end.r * amount); + *d++ = a * (start.g * oppAmount + end.g * amount); + *d++ = a * (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); + *d++ = a * OECF_sRGB(start.r * oppAmount + end.r * amount); + *d++ = a * OECF_sRGB(start.g * oppAmount + end.g * amount); + *d++ = a * OECF_sRGB(start.b * oppAmount + end.b * amount); #endif - *d++ = start.a * oppAmount + end.a * amount; + *d++ = a; dst += 4 * sizeof(float); } @@ -217,16 +219,19 @@ void GradientCache::generateTexture(uint32_t* colors, float* positions, uint8_t pixels[rowBytes * height]; static ChannelMixer gMixers[] = { - &android::uirenderer::GradientCache::mixBytes, // colors are stored gamma-encoded - &android::uirenderer::GradientCache::mixFloats, // colors are stored in linear + // colors are stored gamma-encoded + &android::uirenderer::GradientCache::mixBytes, + // colors are stored in linear (linear blending on) + // or gamma-encoded (linear blending off) + &android::uirenderer::GradientCache::mixFloats, }; ChannelMixer mix = gMixers[mUseFloatTexture]; FloatColor start; - start.setSRGB(colors[0]); + start.setUnPreMultipliedSRGB(colors[0]); FloatColor end; - end.setSRGB(colors[1]); + end.setUnPreMultipliedSRGB(colors[1]); int currentPos = 1; float startPos = positions[0]; @@ -241,7 +246,7 @@ void GradientCache::generateTexture(uint32_t* colors, float* positions, currentPos++; - end.setSRGB(colors[currentPos]); + end.setUnPreMultipliedSRGB(colors[currentPos]); distance = positions[currentPos] - startPos; } diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp index 0c2309faf4ea..2688ba484212 100644 --- a/libs/hwui/ProgramCache.cpp +++ b/libs/hwui/ProgramCache.cpp @@ -175,10 +175,11 @@ const char* gFS_Gradient_Functions = 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);" + " return vec4(color.rgb + (triangleNoise(gl_FragCoord.xy * screenSize.xy) / 255.0), color.a);\n" "}\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));" + " vec4 c = pow(mix(a, b, v), vec4(vec3(1.0 / 2.2), 1.0));\n" + " return vec4(c.rgb * c.a, c.a);\n" "}\n", // sRGB framebuffer "\nvec4 dither(const vec4 color) {\n" @@ -186,7 +187,8 @@ const char* gFS_Gradient_Preamble[2] = { " return vec4(dithered * dithered, color.a);\n" "}\n" "\nvec4 gammaMix(const vec4 a, const vec4 b, float v) {\n" - " return mix(a, b, v);" + " vec4 c = mix(a, b, v);\n" + " return vec4(c.rgb * c.a, c.a);\n" "}\n" }; diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp index 34e6a06c3dc2..0f6651b83db3 100644 --- a/libs/hwui/SkiaShader.cpp +++ b/libs/hwui/SkiaShader.cpp @@ -81,7 +81,7 @@ static void computeScreenSpaceMatrix(mat4& screenSpace, const SkMatrix& unitMatr } /////////////////////////////////////////////////////////////////////////////// -// gradient shader matrix helpers +// Gradient shader matrix helpers /////////////////////////////////////////////////////////////////////////////// static void toLinearUnitMatrix(const SkPoint pts[2], SkMatrix* matrix) { @@ -161,7 +161,7 @@ bool tryStoreGradient(Caches& caches, const SkShader& shader, const Matrix4 mode gradInfo.fColorOffsets = &colorOffsets[0]; shader.asAGradient(&gradInfo); - if (CC_UNLIKELY(!isSimpleGradient(gradInfo))) { + if (CC_UNLIKELY(!description->isSimpleGradient)) { outData->gradientSampler = (*textureUnit)++; #ifndef SK_SCALAR_IS_FLOAT @@ -174,8 +174,8 @@ bool tryStoreGradient(Caches& caches, const SkShader& shader, const Matrix4 mode outData->gradientSampler = 0; outData->gradientTexture = nullptr; - outData->startColor.setSRGB(gradInfo.fColors[0]); - outData->endColor.setSRGB(gradInfo.fColors[1]); + outData->startColor.setUnPreMultipliedSRGB(gradInfo.fColors[0]); + outData->endColor.setUnPreMultipliedSRGB(gradInfo.fColors[1]); } return true; |