diff options
Diffstat (limited to 'libs')
-rw-r--r-- | libs/hwui/Extensions.cpp | 15 | ||||
-rw-r--r-- | libs/hwui/Extensions.h | 2 | ||||
-rw-r--r-- | libs/hwui/FloatColor.h | 12 | ||||
-rw-r--r-- | libs/hwui/GlopBuilder.cpp | 8 | ||||
-rw-r--r-- | libs/hwui/GradientCache.cpp | 28 | ||||
-rw-r--r-- | libs/hwui/GradientCache.h | 8 | ||||
-rw-r--r-- | libs/hwui/ProgramCache.cpp | 34 | ||||
-rw-r--r-- | libs/hwui/SkiaShader.cpp | 4 | ||||
-rw-r--r-- | libs/hwui/utils/Color.h | 18 |
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 |