From 211efea7376371ee755edd2ad03e83ef6eea464e Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Tue, 31 Jul 2012 21:16:07 -0700 Subject: Add dithering to gradients Change-Id: Ic1208855bde3a254eca2fd7cef43e0f1318ce419 --- libs/hwui/Android.mk | 1 + libs/hwui/Caches.cpp | 1 + libs/hwui/Caches.h | 2 ++ libs/hwui/Dither.cpp | 84 ++++++++++++++++++++++++++++++++++++++++++++++ libs/hwui/Dither.h | 47 ++++++++++++++++++++++++++ libs/hwui/ProgramCache.cpp | 58 +++++++++++++++++++++----------- libs/hwui/SkiaShader.cpp | 4 +++ 7 files changed, 178 insertions(+), 19 deletions(-) create mode 100755 libs/hwui/Dither.cpp create mode 100755 libs/hwui/Dither.h (limited to 'libs') diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index a54c188..1947c32 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -11,6 +11,7 @@ ifeq ($(USE_OPENGL_RENDERER),true) Caches.cpp \ DisplayListLogBuffer.cpp \ DisplayListRenderer.cpp \ + Dither.cpp \ FboCache.cpp \ GradientCache.cpp \ LayerCache.cpp \ diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp index 258ced0..aa2bc3f 100644 --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -252,6 +252,7 @@ void Caches::flush(FlushMode mode) { dropShadowCache.clear(); gradientCache.clear(); fontRenderer->clear(); + dither.clear(); // fall through case kFlushMode_Moderate: fontRenderer->flush(); diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h index 4cbac41..ad1098c 100644 --- a/libs/hwui/Caches.h +++ b/libs/hwui/Caches.h @@ -38,6 +38,7 @@ #include "TextDropShadowCache.h" #include "FboCache.h" #include "ResourceCache.h" +#include "Dither.h" namespace android { namespace uirenderer { @@ -250,6 +251,7 @@ public: TextDropShadowCache dropShadowCache; FboCache fboCache; ResourceCache resourceCache; + Dither dither; GammaFontRenderer* fontRenderer; diff --git a/libs/hwui/Dither.cpp b/libs/hwui/Dither.cpp new file mode 100755 index 0000000..5817977 --- /dev/null +++ b/libs/hwui/Dither.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Caches.h" +#include "Dither.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Defines +/////////////////////////////////////////////////////////////////////////////// + +// Must be a power of two +#define DITHER_KERNEL_SIZE 4 + +/////////////////////////////////////////////////////////////////////////////// +// Lifecycle +/////////////////////////////////////////////////////////////////////////////// + +void Dither::bindDitherTexture() { + if (!mInitialized) { + const uint8_t pattern[] = { + 0, 8, 2, 10, + 12, 4, 14, 6, + 3, 11, 1, 9, + 15, 7, 13, 5 + }; + + glGenTextures(1, &mDitherTexture); + glBindTexture(GL_TEXTURE_2D, mDitherTexture); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, DITHER_KERNEL_SIZE, DITHER_KERNEL_SIZE, 0, + GL_ALPHA, GL_UNSIGNED_BYTE, &pattern); + + mInitialized = true; + } else { + glBindTexture(GL_TEXTURE_2D, mDitherTexture); + } +} + +void Dither::clear() { + if (mInitialized) { + glDeleteTextures(1, &mDitherTexture); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Program management +/////////////////////////////////////////////////////////////////////////////// + +void Dither::setupProgram(Program* program, GLuint* textureUnit) { + GLuint textureSlot = (*textureUnit)++; + Caches::getInstance().activeTexture(textureSlot); + + bindDitherTexture(); + + glUniform1i(program->getUniform("ditherSampler"), textureSlot); + glUniform1f(program->getUniform("ditherSize"), 1.0f / DITHER_KERNEL_SIZE); +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/Dither.h b/libs/hwui/Dither.h new file mode 100755 index 0000000..34cf9bf --- /dev/null +++ b/libs/hwui/Dither.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_HWUI_DITHER_H +#define ANDROID_HWUI_DITHER_H + +#include + +#include "Program.h" + +namespace android { +namespace uirenderer { + +/** + * Handles dithering for programs. + */ +class Dither { +public: + Dither(): mInitialized(false), mDitherTexture(0) { } + + void clear(); + void setupProgram(Program* program, GLuint* textureUnit); + +private: + void bindDitherTexture(); + + bool mInitialized; + GLuint mDitherTexture; +}; + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_DITHER_H diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp index d601f01..0e77cb2 100644 --- a/libs/hwui/ProgramCache.cpp +++ b/libs/hwui/ProgramCache.cpp @@ -75,9 +75,11 @@ const char* gVS_Header_Varyings_HasGradient[6] = { // Linear "varying highp vec2 linear;\n", "varying highp float linear;\n", + // Circular "varying highp vec2 circular;\n", "varying highp vec2 circular;\n", + // Sweep "varying highp vec2 sweep;\n", "varying highp vec2 sweep;\n", @@ -92,9 +94,11 @@ const char* gVS_Main_OutGradient[6] = { // Linear " linear = vec2((screenSpace * position).x, 0.5);\n", " linear = (screenSpace * position).x;\n", + // Circular " circular = (screenSpace * position).xy;\n", " circular = (screenSpace * position).xy;\n", + // Sweep " sweep = (screenSpace * position).xy;\n", " sweep = (screenSpace * position).xy;\n", @@ -137,19 +141,24 @@ const char* gFS_Uniforms_TextureSampler = "uniform sampler2D sampler;\n"; const char* gFS_Uniforms_ExternalTextureSampler = "uniform samplerExternalOES sampler;\n"; +#define FS_UNIFORMS_DITHER \ + "uniform float ditherSize;\n" \ + "uniform sampler2D ditherSampler;\n" +#define FS_UNIFORMS_GRADIENT \ + "uniform vec4 startColor;\n" \ + "uniform vec4 endColor;\n" const char* gFS_Uniforms_GradientSampler[6] = { // Linear - "uniform sampler2D gradientSampler;\n", - "uniform vec4 startColor;\n" - "uniform vec4 endColor;\n", + FS_UNIFORMS_DITHER "uniform sampler2D gradientSampler;\n", + FS_UNIFORMS_DITHER FS_UNIFORMS_GRADIENT, + // Circular - "uniform sampler2D gradientSampler;\n", - "uniform vec4 startColor;\n" - "uniform vec4 endColor;\n", + FS_UNIFORMS_DITHER "uniform sampler2D gradientSampler;\n", + FS_UNIFORMS_DITHER FS_UNIFORMS_GRADIENT, + // Sweep - "uniform sampler2D gradientSampler;\n", - "uniform vec4 startColor;\n" - "uniform vec4 endColor;\n", + FS_UNIFORMS_DITHER "uniform sampler2D gradientSampler;\n", + FS_UNIFORMS_DITHER FS_UNIFORMS_GRADIENT }; const char* gFS_Uniforms_BitmapSampler = "uniform sampler2D bitmapSampler;\n"; @@ -176,6 +185,11 @@ const char* gFS_Main_PointBitmapTexCoords = " highp vec2 outBitmapTexCoords = outPointBitmapTexCoords + " "((gl_PointCoord - vec2(0.5, 0.5)) * textureDimension * vec2(pointSize, pointSize));\n"; +#define FS_MAIN_DITHER \ + "texture2D(ditherSampler, gl_FragCoord.xy * ditherSize).a * ditherSize * ditherSize" +const char* gFS_Main_AddDitherToGradient = + " gradientColor += " FS_MAIN_DITHER ";\n"; + // Fast cases const char* gFS_Fast_SingleColor = "\nvoid main(void) {\n" @@ -207,18 +221,18 @@ const char* gFS_Fast_SingleModulateA8Texture_ApplyGamma = "}\n\n"; const char* gFS_Fast_SingleGradient[2] = { "\nvoid main(void) {\n" - " gl_FragColor = texture2D(gradientSampler, linear);\n" + " gl_FragColor = " FS_MAIN_DITHER " + texture2D(gradientSampler, linear);\n" "}\n\n", "\nvoid main(void) {\n" - " gl_FragColor = mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n" + " gl_FragColor = " FS_MAIN_DITHER " + mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n" "}\n\n" }; const char* gFS_Fast_SingleModulateGradient[2] = { "\nvoid main(void) {\n" - " gl_FragColor = color.a * texture2D(gradientSampler, linear);\n" + " gl_FragColor " FS_MAIN_DITHER " + color.a * texture2D(gradientSampler, linear);\n" "}\n\n", "\nvoid main(void) {\n" - " gl_FragColor = color.a * mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n" + " gl_FragColor " FS_MAIN_DITHER " + color.a * mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n" "}\n\n" }; @@ -254,16 +268,21 @@ const char* gFS_Main_FetchA8Texture[2] = { }; 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", + " highp vec4 gradientColor = texture2D(gradientSampler, linear);\n", + + " highp vec4 gradientColor = mix(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", + " highp vec4 gradientColor = texture2D(gradientSampler, vec2(length(circular), 0.5));\n", + + " highp vec4 gradientColor = mix(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 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" + " highp vec4 gradientColor = mix(startColor, endColor, clamp(index - floor(index), 0.0, 1.0));\n" }; const char* gFS_Main_FetchBitmap = " vec4 bitmapColor = texture2D(bitmapSampler, outBitmapTexCoords);\n"; @@ -651,6 +670,7 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti } if (description.hasGradient) { shader.append(gFS_Main_FetchGradient[gradientIndex(description)]); + shader.append(gFS_Main_AddDitherToGradient); } if (description.hasBitmap) { if (description.isPoint) { diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp index 71e1739..8916efd 100644 --- a/libs/hwui/SkiaShader.cpp +++ b/libs/hwui/SkiaShader.cpp @@ -250,6 +250,8 @@ void SkiaLinearGradientShader::setupProgram(Program* program, const mat4& modelV bindUniformColor(program->getUniform("endColor"), mColors[1]); } + Caches::getInstance().dither.setupProgram(program, textureUnit); + mat4 screenSpace; computeScreenSpaceMatrix(screenSpace, modelView); glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]); @@ -375,6 +377,8 @@ void SkiaSweepGradientShader::setupProgram(Program* program, const mat4& modelVi bindUniformColor(program->getUniform("endColor"), mColors[1]); } + Caches::getInstance().dither.setupProgram(program, textureUnit); + mat4 screenSpace; computeScreenSpaceMatrix(screenSpace, modelView); glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]); -- cgit v1.1