Skip to content

Commit 171a716

Browse files
Preconvert colors to sRGB
1 parent c9c8964 commit 171a716

File tree

2 files changed

+200
-3
lines changed

2 files changed

+200
-3
lines changed
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
#import bevy_render::view::View
2+
#import bevy_ui::ui_node::{
3+
draw_uinode_background,
4+
draw_uinode_border,
5+
}
6+
7+
const PI: f32 = 3.14159265358979323846;
8+
const TAU: f32 = 2. * PI;
9+
10+
const TEXTURED = 1u;
11+
const RIGHT_VERTEX = 2u;
12+
const BOTTOM_VERTEX = 4u;
13+
// must align with BORDER_* shader_flags from bevy_ui/render/mod.rs
14+
const RADIAL: u32 = 16u;
15+
const FILL_START: u32 = 32u;
16+
const FILL_END: u32 = 64u;
17+
const CONIC: u32 = 128u;
18+
const BORDER_LEFT: u32 = 256u;
19+
const BORDER_TOP: u32 = 512u;
20+
const BORDER_RIGHT: u32 = 1024u;
21+
const BORDER_BOTTOM: u32 = 2048u;
22+
const BORDER_ANY: u32 = BORDER_LEFT + BORDER_TOP + BORDER_RIGHT + BORDER_BOTTOM;
23+
24+
fn enabled(flags: u32, mask: u32) -> bool {
25+
return (flags & mask) != 0u;
26+
}
27+
28+
@group(0) @binding(0) var<uniform> view: View;
29+
30+
struct GradientVertexOutput {
31+
@location(0) uv: vec2<f32>,
32+
@location(1) @interpolate(flat) size: vec2<f32>,
33+
@location(2) @interpolate(flat) flags: u32,
34+
@location(3) @interpolate(flat) radius: vec4<f32>,
35+
@location(4) @interpolate(flat) border: vec4<f32>,
36+
37+
// Position relative to the center of the rectangle.
38+
@location(5) point: vec2<f32>,
39+
@location(6) @interpolate(flat) g_start: vec2<f32>,
40+
@location(7) @interpolate(flat) dir: vec2<f32>,
41+
@location(8) @interpolate(flat) start_color: vec4<f32>,
42+
@location(9) @interpolate(flat) start_len: f32,
43+
@location(10) @interpolate(flat) end_len: f32,
44+
@location(11) @interpolate(flat) end_color: vec4<f32>,
45+
@location(12) @interpolate(flat) hint: f32,
46+
@builtin(position) position: vec4<f32>,
47+
};
48+
49+
@vertex
50+
fn vertex(
51+
@location(0) vertex_position: vec3<f32>,
52+
@location(1) vertex_uv: vec2<f32>,
53+
@location(2) flags: u32,
54+
55+
// x: top left, y: top right, z: bottom right, w: bottom left.
56+
@location(3) radius: vec4<f32>,
57+
58+
// x: left, y: top, z: right, w: bottom.
59+
@location(4) border: vec4<f32>,
60+
@location(5) size: vec2<f32>,
61+
@location(6) point: vec2<f32>,
62+
@location(7) @interpolate(flat) g_start: vec2<f32>,
63+
@location(8) @interpolate(flat) dir: vec2<f32>,
64+
@location(9) @interpolate(flat) start_color: vec4<f32>,
65+
@location(10) @interpolate(flat) start_len: f32,
66+
@location(11) @interpolate(flat) end_len: f32,
67+
@location(12) @interpolate(flat) end_color: vec4<f32>,
68+
@location(13) @interpolate(flat) hint: f32
69+
) -> GradientVertexOutput {
70+
var out: GradientVertexOutput;
71+
out.position = view.clip_from_world * vec4(vertex_position, 1.0);
72+
out.uv = vertex_uv;
73+
out.size = size;
74+
out.flags = flags;
75+
out.radius = radius;
76+
out.border = border;
77+
out.point = point;
78+
out.dir = dir;
79+
out.start_color = start_color;
80+
out.start_len = start_len;
81+
out.end_len = end_len;
82+
out.end_color = end_color;
83+
out.g_start = g_start;
84+
out.hint = hint;
85+
86+
return out;
87+
}
88+
89+
@fragment
90+
fn fragment(in: GradientVertexOutput) -> @location(0) vec4<f32> {
91+
var g_distance: f32;
92+
if enabled(in.flags, RADIAL) {
93+
g_distance = radial_distance(in.point, in.g_start, in.dir.x);
94+
} else if enabled(in.flags, CONIC) {
95+
g_distance = conic_distance(in.dir.x, in.point, in.g_start);
96+
} else {
97+
g_distance = linear_distance(in.point, in.g_start, in.dir);
98+
}
99+
100+
let gradient_color = interpolate_gradient(
101+
g_distance,
102+
in.start_color,
103+
in.start_len,
104+
in.end_color,
105+
in.end_len,
106+
in.hint,
107+
in.flags
108+
);
109+
110+
if enabled(in.flags, BORDER_ANY) {
111+
return draw_uinode_border(gradient_color, in.point, in.size, in.radius, in.border, in.flags);
112+
} else {
113+
return draw_uinode_background(gradient_color, in.point, in.size, in.radius, in.border);
114+
}
115+
}
116+
117+
// Mix colors in sRGB space and convert back to linear for GPU output
118+
fn mix_srgb_colors(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f32> {
119+
let mixed_srgb = mix(a, b, t);
120+
// Convert from sRGB back to linear RGB for GPU output
121+
return vec4(pow(mixed_srgb.rgb, vec3(2.2)), mixed_srgb.a);
122+
}
123+
124+
// These functions are used to calculate the distance in gradient space from the start of the gradient to the point.
125+
// The distance in gradient space is then used to interpolate between the start and end colors.
126+
127+
fn linear_distance(
128+
point: vec2<f32>,
129+
g_start: vec2<f32>,
130+
g_dir: vec2<f32>,
131+
) -> f32 {
132+
return dot(point - g_start, g_dir);
133+
}
134+
135+
fn radial_distance(
136+
point: vec2<f32>,
137+
center: vec2<f32>,
138+
ratio: f32,
139+
) -> f32 {
140+
let d = point - center;
141+
return length(vec2(d.x, d.y * ratio));
142+
}
143+
144+
fn conic_distance(
145+
start: f32,
146+
point: vec2<f32>,
147+
center: vec2<f32>,
148+
) -> f32 {
149+
let d = point - center;
150+
let angle = atan2(-d.x, d.y) + PI;
151+
return (((angle - start) % TAU) + TAU) % TAU;
152+
}
153+
154+
fn interpolate_gradient(
155+
distance: f32,
156+
start_color: vec4<f32>,
157+
start_distance: f32,
158+
end_color: vec4<f32>,
159+
end_distance: f32,
160+
hint: f32,
161+
flags: u32,
162+
) -> vec4<f32> {
163+
if start_distance == end_distance {
164+
if distance <= start_distance && enabled(flags, FILL_START) {
165+
return start_color;
166+
}
167+
if start_distance <= distance && enabled(flags, FILL_END) {
168+
return end_color;
169+
}
170+
return vec4(0.);
171+
}
172+
173+
var t = (distance - start_distance) / (end_distance - start_distance);
174+
175+
if t < 0.0 {
176+
if enabled(flags, FILL_START) {
177+
return start_color;
178+
}
179+
return vec4(0.0);
180+
}
181+
182+
if 1. < t {
183+
if enabled(flags, FILL_END) {
184+
return end_color;
185+
}
186+
return vec4(0.0);
187+
}
188+
189+
if t < hint {
190+
t = 0.5 * t / hint;
191+
} else {
192+
t = 0.5 * (1 + (t - hint) / (1.0 - hint));
193+
}
194+
195+
// Only color interpolation in SRGB space is supported atm.
196+
return mix_linear_rgb_in_srgb_space(start_color, end_color, t);
197+
}

crates/bevy_ui_render/src/gradient.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use core::{
77
use super::shader_flags::BORDER_ALL;
88
use crate::*;
99
use bevy_asset::*;
10-
use bevy_color::{ColorToComponents, LinearRgba};
10+
use bevy_color::{ColorToComponents, LinearRgba, Srgba};
1111
use bevy_ecs::{
1212
prelude::Component,
1313
system::{
@@ -804,8 +804,8 @@ pub fn prepare_gradient(
804804
continue;
805805
}
806806
}
807-
let start_color = start_stop.0.to_f32_array();
808-
let end_color = end_stop.0.to_f32_array();
807+
let start_color = Srgba::from(start_stop.0).to_f32_array();
808+
let end_color = Srgba::from(end_stop.0).to_f32_array();
809809
let mut stop_flags = flags;
810810
if 0. < start_stop.1
811811
&& (stop_index == gradient.stops_range.start || segment_count == 0)

0 commit comments

Comments
 (0)