-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
color banding on android #1065
Comments
Can you try adding this to your MainActivity.java @Override
public void onCreate(Bundle savedInstanceState)
{
Window window = getWindow();
window.setFormat(PixelFormat.RGBA_8888);
super.onCreate(savedInstanceState);
} I'm unable to replicate the issue on either emulator or real device. |
@msand thanks for the quick response! Unfortunately, I already tried that to no avail :( |
@msand it replicates on a Pixel 3 Android 9 emulator |
Created a Pixel 3 emulator with Google APIs Intel x86 Atom_64 System Image revision: 9 (system-images;android-28;google_apis;x86_64) With this code: import React, { Component } from 'react';
import { StyleSheet, View, Dimensions } from 'react-native';
import Svg, { Defs, LinearGradient, Stop, Rect } from 'react-native-svg';
const { width } = Dimensions.get('window');
const SvgBg = props => (
<Svg width={width} height="100%" {...props}>
<Defs>
<LinearGradient id="a" x1="50%" x2="50%" y1="0%" y2="100%">
<Stop offset="0%" stopColor="#0066ff" />
<Stop offset="100%" stopColor="#00aaff" />
</LinearGradient>
</Defs>
<Rect fill="url(#a)" width={width} height="100%" />
</Svg>
);
export default class App extends Component {
render() {
return (
<View style={styles.container}>
<SvgBg />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#F5FCFF',
},
}); I get this (Emulator screenshot): Looks quite fine to me? |
@msand hey man, thanks for pursuing this. I do see some banding in the image you posted, but let me see if i can pick two colors that make it even more obvious...give me a couple of minutes |
@msand what does #aaa and #bbb look like for you? I see: |
@msand do you see the banding on your image? Moving away from the monitor a bit and scrolling up and down on it helps the eye catch it. Then you can't unsee it... |
Well, you know, there are only 16 possible colors from #aaaaaa to #bbbbbb |
@msand that's the thing, if i do this with css gradients in Chrome on a OnePlus 6t, then open btw, this flag doesn't seem to change anything on the emulator |
and if you open this on a mac, you'll see no color banding: <html>
<style rel="stylesheet">
.gradient {
width: 100%;
height: 100%;
background: linear-gradient(to bottom, #aaa, #bbb);
}
</style>
<body style="padding:0px;margin:0px;">
<div class="gradient"></div>
</body>
</html> |
haha yeah, i tried that too, but i still see banding (in the image u posted too btw) |
Well, I think making it into png will force it to use a byte per color channel, not sure how to make RGBA_F16 screenshots. Seems chrome has some dithering/noise/turbulence to hide the banding. |
i also tried adding |
@msand do u mean that banding disappears for you on the emulator when u set RGBA_F16 and use setDither(true), but that the screenshot didn't capture it? If so, where exactly did u set it? |
No, I think it still has the issue, also tried: |
You'll probably need to use wide-color gamut images, opengl or vulkan: https://developer.android.com/training/wide-color-gamut |
ic, that doesn't sound like a quick fix then |
You might want to check out https://github.com/googlesamples/android-ndk/blob/ae568ca67d908040fb69aa08d8296ef64d4473c4/display-p3/README.md for how to get a wide color gamut opengl context. Then you'd need to render the gradient using that. http://www.cs.princeton.edu/~mhalber/blog/ogl_gradient/ If you only have a single rectangular area with a two color linear gradient. Then you only need to render a single quad. Should probably be learnable in a day, as it's among the smallest possible things that can be done using opengl. |
I have a gradient with banding rendered in the opengl context now, I replaced ImageViewEngine::DrawFrame in ImageViewEngine.cpp with this: #define SHADER_HEADER "#version 300 es\n"
#define SHADER_STR(x) #x
void mygl_GradientBackground( float top_r, float top_g, float top_b, float top_a,
float bot_r, float bot_g, float bot_b, float bot_a )
{
glDisable(GL_DEPTH_TEST);
static GLuint background_vao = 0;
static GLuint background_shader = 0;
if (background_vao == 0)
{
glGenVertexArrays(1, &background_vao);
const char* vs_src = (const char*) SHADER_HEADER SHADER_STR
(
out vec2 v_uv;
void main()
{
uint idx = uint(gl_VertexID);
gl_Position = vec4(idx & 1U, idx >> 1U, 0.0, 0.5 ) * 4.0 - 1.0;
v_uv = vec2( gl_Position.xy * 0.5 + 0.5 );
}
);
const char* fs_src = (const char*) SHADER_HEADER SHADER_STR
(
uniform vec4 top_color;
uniform vec4 bot_color;
in vec2 v_uv;
out vec4 frag_color;
void main()
{
frag_color = bot_color + (top_color - bot_color) * v_uv.y;
}
);
GLuint vs_id, fs_id;
vs_id = glCreateShader( GL_VERTEX_SHADER );
fs_id = glCreateShader( GL_FRAGMENT_SHADER );
glShaderSource(vs_id, 1, &vs_src, NULL);
glShaderSource(fs_id, 1, &fs_src, NULL);
glCompileShader(vs_id);
glCompileShader(fs_id);
background_shader = glCreateProgram();
glAttachShader( background_shader, vs_id );
glAttachShader( background_shader, fs_id );
glLinkProgram( background_shader );
glDetachShader( background_shader, fs_id );
glDetachShader( background_shader, vs_id );
glDeleteShader( fs_id );
glDeleteShader( vs_id );
glUseProgram( background_shader );
}
glUseProgram( background_shader );
GLuint top_color_loc = glGetUniformLocation( background_shader, "top_color" );
GLuint bot_color_loc = glGetUniformLocation( background_shader, "bot_color" );
glUniform4f( top_color_loc, top_r, top_g, top_b, top_a );
glUniform4f( bot_color_loc, bot_r, bot_g, bot_b, bot_a );
glBindVertexArray( background_vao );
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
glEnable(GL_DEPTH_TEST);
}
/*
* Draw quad(s) to view texture
*/
void ImageViewEngine::DrawFrame(void) {
if (display_ == NULL) {
return;
}
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
/*
mygl_GradientBackground( 1.0, 0.0, 0.0, 1.0,
0.0, 1.0, 0.0, 1.0 );
*/
mygl_GradientBackground( 0.67, 0.67, 0.67, 1.0,
0.73, 0.73, 0.73, 1.0 );
eglSwapBuffers(display_, surface_);
} |
@msand did you add |
Or, actually, it might be a limitation of my device as well, I don't think I even have any wide color gamut displays available. |
At least when I ran the original display-p3 example, both images looked the same, so my android doesn't support it at least. |
oh, what about on the pixel 3 emulator? |
@mvayngrib Can you try running the display-p3 example? And if the images look different for you, then try changing the code to render the gradient instead? |
@mvayngrib The emulators don't have any gpu, I think you can only run the native gpu examples on real devices. |
Interesting, that should be the only part affecting it as far as I understand at the moment. The gradient colors should be uniformly interpolated between 0 and 1, and the surface should decide the colorspace. Can you try adding this: precision mediump float; to the fragment shader: const char* fs_src = (const char*) SHADER_HEADER SHADER_STR
(
precision mediump float;
uniform vec4 top_color;
uniform vec4 bot_color;
in vec2 v_uv;
out vec4 frag_color;
void main()
{
frag_color = bot_color + (top_color - bot_color) * v_uv.y;
}
); |
@mvayngrib And might want to change v_uv.y to v_uv.x to exaggerate the banding more. |
@msand added that to the fragment shader, not sure what I should have expected, but it's still banding :) |
Not sure how to proceed at this point. I would need a device with support for it to do more testing. |
@msand makes sense. If you're up for it, I'm happy to jump on video and experiment together on my device |
I don't have any specific idea to explore atm, but thanks for the offer. I'll need to focus on more work related matters at this point 😄 |
This might be of tangential interest: http://litherum.blogspot.com/2017/07/wide-and-deep-color-in-metal-and-opengl.html |
@msand thanks, i'll have a read! Just fyi, the banding only occurs on android |
Did you try using the Paint.DITHER_FLAG? https://developer.android.com/reference/android/graphics/Paint.html#DITHER_FLAG react-native-svg\android\src\main\java\com\horcrux\svg\RenderableView.java private boolean setupFillPaint(Paint paint, float opacity) {
if (fill != null && fill.size() > 0) {
paint.reset();
paint.setFlags(Paint.DITHER_FLAG | Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG | Paint.SUBPIXEL_TEXT_FLAG);
paint.setStyle(Paint.Style.FILL);
setupPaint(paint, opacity, fill);
return true;
}
return false;
} I see significantly less banding in testing with a RadialGradient when using that flag. |
@lvalentine just tried, didn't help :( |
@mvayngrib Have you tried setting |
@msand thanks for the suggestion, but it doesn't seem to help on the device (testing on oneplus 6t) |
@mvayngrib Hmm, perhaps some other change I tried in addition to that then, can you try the latest commit from the dither branch: ee894b2 |
Sorry, wrong commit pushed: ee894b2 |
@msand do u mind explaining why |
@mvayngrib Great to hear :) Some (all?) devices when using hardware acceleration, do not apply any dithering algorithm, partially to make things faster, partially because it can be tricky to do well with a gpu, and partially because of laziness I guess. Probably main reason is performance. Would you mind attempting to reduce those changes to the smallest possible ones which still allow it to work the way you wish? |
@msand yep, let me play with it later today and get back to u |
@msand these are the minimal changes that it takes to get it to work on my device (plus |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. You may also mark this issue as a "discussion" and I will leave this open. |
Closing this issue after a prolonged period of inactivity. Fell free to reopen this issue, if this still affecting you. |
Hey @msand. I'd like to resurface this, if possible. I'm not a software engineer, but I do know a thing or two about digital colour. I did some research on this by taking screenshots of the same app in the same state but on iOS and Android. All screenshots were PNG, which is a lossless format. Here's what I have found out:
The solution here, in my humble opinion, is to add dithering to Android and don't bother with increased bit depth or wide colour gamut. React native has no support for either of these and it uses 8-bit colour with sRGB primaries anyway unless you wanna mess with OpenGL or Vulkan. Also, something tells me it's RN's Android colour bitmap that might be borked. IDK if you do bitmapping here Makes sense? If not, I'll be happy to clarify anything |
Also, my device (Pixel 3) can dither with hardware acceleration turned on, apparently. It would be great to have hardware acceleration exposed for dither controls |
Bug
i'm seeing color banding on some android devices. Below is a screenshot from a OnePlus 6t.
![image](https://user-images.githubusercontent.com/83948/62416808-65c9cf00-b60f-11e9-83b0-6cb84770ff51.png)
Environment info
React native info output:
Library version: 9.5.3
Steps To Reproduce
see branch mv/svg here: https://github.com/mvayngrib/gradientbanding/tree/mv/svg
Describe what you expected to happen:
smooth gradient
Reproducible sample code
see branch mv/svg here: https://github.com/mvayngrib/gradientbanding/tree/mv/svg
here's the main code though (colors hardcoded):
The text was updated successfully, but these errors were encountered: