X Tutup
Skip to content

Round gradient colors#109012

Merged
Repiteo merged 1 commit intogodotengine:masterfrom
Lielay9:fix_gradient_color
Sep 22, 2025
Merged

Round gradient colors#109012
Repiteo merged 1 commit intogodotengine:masterfrom
Lielay9:fix_gradient_color

Conversation

@Lielay9
Copy link
Contributor

@Lielay9 Lielay9 commented Jul 27, 2025

Fixes: #100780

In majority of the cases this doesn't change anything, but if the gradient colors happen to be close you now get the full range without duplicates e.g.:
Master:
master_color
This pr:
pr_color
3px gradient from (11, 0, 11) to (13, 100, 13). The center pixel is now (12, 50, 12) instead of (11, 50, 11). (I know you can't see sh*t, but take a color picker and try yourself).

Rounding is necessary since color values are floats on disk and won't yield the same value when multiplied by 255.0 and floored:
E.g. 11 is represented as 0.04313725.

print(String.num(0.04313725 * 255.0)) # 10.99999875

@Lielay9 Lielay9 changed the title Round gradient colors. Round gradient colors Jul 27, 2025
@AThousandShips AThousandShips requested a review from a team July 28, 2025 09:14
@AThousandShips AThousandShips added this to the 4.x milestone Jul 28, 2025
@Lielay9 Lielay9 force-pushed the fix_gradient_color branch from 65f1dd7 to 288418a Compare July 29, 2025 09:18
@Lielay9 Lielay9 force-pushed the fix_gradient_color branch from 288418a to f6602d4 Compare July 29, 2025 09:22
Copy link
Member

@Calinou Calinou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code looks good to me.

This pattern (uint8_t(CLAMP() is reused in a few other locations in the codebase. Should some of these be changed too?

51 results - 9 files

core/io/image.cpp:
  3264  		case FORMAT_L8: {
  3265: 			ptr[ofs] = uint8_t(CLAMP(p_color.get_v() * 255.0, 0, 255));
  3266  		} break;
  3267  		case FORMAT_LA8: {
  3268: 			ptr[ofs * 2 + 0] = uint8_t(CLAMP(p_color.get_v() * 255.0, 0, 255));
  3269: 			ptr[ofs * 2 + 1] = uint8_t(CLAMP(p_color.a * 255.0, 0, 255));
  3270  		} break;
  3271  		case FORMAT_R8: {
  3272: 			ptr[ofs] = uint8_t(CLAMP(p_color.r * 255.0, 0, 255));
  3273  		} break;
  3274  		case FORMAT_RG8: {
  3275: 			ptr[ofs * 2 + 0] = uint8_t(CLAMP(p_color.r * 255.0, 0, 255));
  3276: 			ptr[ofs * 2 + 1] = uint8_t(CLAMP(p_color.g * 255.0, 0, 255));
  3277  		} break;
  3278  		case FORMAT_RGB8: {
  3279: 			ptr[ofs * 3 + 0] = uint8_t(CLAMP(p_color.r * 255.0, 0, 255));
  3280: 			ptr[ofs * 3 + 1] = uint8_t(CLAMP(p_color.g * 255.0, 0, 255));
  3281: 			ptr[ofs * 3 + 2] = uint8_t(CLAMP(p_color.b * 255.0, 0, 255));
  3282  		} break;
  3283  		case FORMAT_RGBA8: {
  3284: 			ptr[ofs * 4 + 0] = uint8_t(CLAMP(p_color.r * 255.0, 0, 255));
  3285: 			ptr[ofs * 4 + 1] = uint8_t(CLAMP(p_color.g * 255.0, 0, 255));
  3286: 			ptr[ofs * 4 + 2] = uint8_t(CLAMP(p_color.b * 255.0, 0, 255));
  3287: 			ptr[ofs * 4 + 3] = uint8_t(CLAMP(p_color.a * 255.0, 0, 255));
  3288  		} break;

drivers/gles3/rasterizer_canvas_gles3.cpp:
  174  			for (int i = 0; i < 4; i++) {
  175: 				state.light_uniforms[index].shadow_color[i] = uint8_t(CLAMP(int32_t(l->shadow_color[i] * 255.0), 0, 255));
  176  				state.light_uniforms[index].color[i] = l->color[i];

  245  			for (int i = 0; i < 4; i++) {
  246: 				state.light_uniforms[index].shadow_color[i] = uint8_t(CLAMP(int32_t(l->shadow_color[i] * 255.0), 0, 255));
  247  				state.light_uniforms[index].color[i] = l->color[i];

scene/3d/lightmap_gi.cpp:
  986  				for (uint32_t i = 0; i < len; i += 4) {
  987: 					w_albedo[i + 0] = uint8_t(CLAMP(float(r_aa[i + 0]) * (1.0 - float(r_orm[i + 2] / 255.0)), 0, 255));
  988: 					w_albedo[i + 1] = uint8_t(CLAMP(float(r_aa[i + 1]) * (1.0 - float(r_orm[i + 2] / 255.0)), 0, 255));
  989: 					w_albedo[i + 2] = uint8_t(CLAMP(float(r_aa[i + 2]) * (1.0 - float(r_orm[i + 2] / 255.0)), 0, 255));
  990  					w_albedo[i + 3] = r_aa[i + 3];

scene/3d/sprite_3d.cpp:
  217  	uint8_t v_color[4] = {
  218: 		uint8_t(CLAMP(color.r * 255.0, 0.0, 255.0)),
  219: 		uint8_t(CLAMP(color.g * 255.0, 0.0, 255.0)),
  220: 		uint8_t(CLAMP(color.b * 255.0, 0.0, 255.0)),
  221: 		uint8_t(CLAMP(color.a * 255.0, 0.0, 255.0))
  222  	};

scene/resources/gradient_texture.cpp:
  122  
  123: 				wd8[i * 4 + 0] = uint8_t(CLAMP(color.r * 255.0, 0, 255));
  124: 				wd8[i * 4 + 1] = uint8_t(CLAMP(color.g * 255.0, 0, 255));
  125: 				wd8[i * 4 + 2] = uint8_t(CLAMP(color.b * 255.0, 0, 255));
  126: 				wd8[i * 4 + 3] = uint8_t(CLAMP(color.a * 255.0, 0, 255));
  127  			}

  261  
  262: 						wd8[(x + (y * width)) * 4 + 0] = uint8_t(CLAMP(c.r * 255.0, 0, 255));
  263: 						wd8[(x + (y * width)) * 4 + 1] = uint8_t(CLAMP(c.g * 255.0, 0, 255));
  264: 						wd8[(x + (y * width)) * 4 + 2] = uint8_t(CLAMP(c.b * 255.0, 0, 255));
  265: 						wd8[(x + (y * width)) * 4 + 3] = uint8_t(CLAMP(c.a * 255.0, 0, 255));
  266  					}

scene/resources/immediate_mesh.cpp:
  256  
  257: 				color8[0] = uint8_t(CLAMP(colors[i].r * 255.0, 0.0, 255.0));
  258: 				color8[1] = uint8_t(CLAMP(colors[i].g * 255.0, 0.0, 255.0));
  259: 				color8[2] = uint8_t(CLAMP(colors[i].b * 255.0, 0.0, 255.0));
  260: 				color8[3] = uint8_t(CLAMP(colors[i].a * 255.0, 0.0, 255.0));
  261  			}

scene/resources/mesh.cpp:
  1220  
  1221: 						dst[0] = uint8_t(CLAMP(src[0] * 255.0, 0.0, 255.0));
  1222: 						dst[1] = uint8_t(CLAMP(src[1] * 255.0, 0.0, 255.0));
  1223: 						dst[2] = uint8_t(CLAMP(src[2] * 255.0, 0.0, 255.0));
  1224: 						dst[3] = uint8_t(CLAMP(src[3] * 255.0, 0.0, 255.0));
  1225  					}

servers/rendering_server.cpp:
  133  
  134: 				w[(y * TEST_TEXTURE_SIZE + x) * 3 + 0] = uint8_t(CLAMP(c.r, 0, 255));
  135: 				w[(y * TEST_TEXTURE_SIZE + x) * 3 + 1] = uint8_t(CLAMP(c.g, 0, 255));
  136: 				w[(y * TEST_TEXTURE_SIZE + x) * 3 + 2] = uint8_t(CLAMP(c.b, 0, 255));
  137  			}

  712  					uint8_t color8[4] = {
  713: 						uint8_t(CLAMP(src[i].r * 255.0, 0.0, 255.0)),
  714: 						uint8_t(CLAMP(src[i].g * 255.0, 0.0, 255.0)),
  715: 						uint8_t(CLAMP(src[i].b * 255.0, 0.0, 255.0)),
  716: 						uint8_t(CLAMP(src[i].a * 255.0, 0.0, 255.0))
  717  					};

servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp:
   550  			for (int i = 0; i < 4; i++) {
   551: 				state.light_uniforms[index].shadow_color[i] = uint8_t(CLAMP(int32_t(l->shadow_color[i] * 255.0), 0, 255));
   552  				state.light_uniforms[index].color[i] = l->color[i];

   620  			for (int i = 0; i < 4; i++) {
   621: 				state.light_uniforms[index].shadow_color[i] = uint8_t(CLAMP(int32_t(l->shadow_color[i] * 255.0), 0, 255));
   622  				state.light_uniforms[index].color[i] = l->color[i];

  3286  
  3287: 	uint8_t a = uint8_t(CLAMP(info.specular_color.a * 255.0, 0.0, 255.0));
  3288: 	uint8_t b = uint8_t(CLAMP(info.specular_color.b * 255.0, 0.0, 255.0));
  3289: 	uint8_t g = uint8_t(CLAMP(info.specular_color.g * 255.0, 0.0, 255.0));
  3290: 	uint8_t r = uint8_t(CLAMP(info.specular_color.r * 255.0, 0.0, 255.0));
  3291  	p_info->specular_shininess = uint32_t(a) << 24 | uint32_t(b) << 16 | uint32_t(g) << 8 | uint32_t(r);

@Lielay9
Copy link
Contributor Author

Lielay9 commented Aug 2, 2025

This pattern (uint8_t(CLAMP() is reused in a few other locations in the codebase. Should some of these be changed too?

Yes, the color should likely be rounded everywhere on that list: e.g. it doesn't make sense to pick 254 over 255 for shadow color if the exact value is 254.9999847412109375. I'm little hesitant about batching them with this pr as most of the other places seem to work with discrete colors i.e. you always want the closest color; here with gradients you have to worry about interpolation and the offsets (so they are in fact more safer to change than this pr).

@Repiteo Repiteo modified the milestones: 4.x, 4.6 Sep 21, 2025
@Repiteo Repiteo merged commit 999b94c into godotengine:master Sep 22, 2025
20 checks passed
@Repiteo
Copy link
Contributor

Repiteo commented Sep 22, 2025

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Wrong gradient colors when game or editor start

5 participants

X Tutup