Preconvert colors to sRGB
This commit is contained in:
parent
f1eace62f0
commit
fff060d638
11
Cargo.toml
11
Cargo.toml
@ -3068,6 +3068,17 @@ description = "Test rendering of many UI elements"
|
|||||||
category = "Stress Tests"
|
category = "Stress Tests"
|
||||||
wasm = true
|
wasm = true
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "many_gradients"
|
||||||
|
path = "examples/stress_tests/many_gradients.rs"
|
||||||
|
doc-scrape-examples = true
|
||||||
|
|
||||||
|
[package.metadata.example.many_gradients]
|
||||||
|
name = "Many Gradients"
|
||||||
|
description = "Stress test for gradient rendering performance"
|
||||||
|
category = "Stress Tests"
|
||||||
|
wasm = true
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "many_cameras_lights"
|
name = "many_cameras_lights"
|
||||||
path = "examples/stress_tests/many_cameras_lights.rs"
|
path = "examples/stress_tests/many_cameras_lights.rs"
|
||||||
|
@ -7,7 +7,7 @@ use core::{
|
|||||||
use super::shader_flags::BORDER_ALL;
|
use super::shader_flags::BORDER_ALL;
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use bevy_asset::*;
|
use bevy_asset::*;
|
||||||
use bevy_color::{ColorToComponents, LinearRgba};
|
use bevy_color::{ColorToComponents, Hsla, Hsva, LinearRgba, Oklaba, Oklcha, Srgba};
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
prelude::Component,
|
prelude::Component,
|
||||||
system::{
|
system::{
|
||||||
@ -654,6 +654,36 @@ struct UiGradientVertex {
|
|||||||
hint: f32,
|
hint: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn convert_color_to_space(color: LinearRgba, space: InterpolationColorSpace) -> [f32; 4] {
|
||||||
|
match space {
|
||||||
|
InterpolationColorSpace::OkLab => {
|
||||||
|
let oklaba: Oklaba = color.into();
|
||||||
|
[oklaba.lightness, oklaba.a, oklaba.b, oklaba.alpha]
|
||||||
|
}
|
||||||
|
InterpolationColorSpace::OkLch | InterpolationColorSpace::OkLchLong => {
|
||||||
|
let oklcha: Oklcha = color.into();
|
||||||
|
[oklcha.lightness, oklcha.chroma, oklcha.hue.to_radians(), oklcha.alpha]
|
||||||
|
}
|
||||||
|
InterpolationColorSpace::Srgb => {
|
||||||
|
let srgba: Srgba = color.into();
|
||||||
|
[srgba.red, srgba.green, srgba.blue, srgba.alpha]
|
||||||
|
}
|
||||||
|
InterpolationColorSpace::LinearRgb => {
|
||||||
|
color.to_f32_array()
|
||||||
|
}
|
||||||
|
InterpolationColorSpace::Hsl | InterpolationColorSpace::HslLong => {
|
||||||
|
let hsla: Hsla = color.into();
|
||||||
|
// Normalize hue to 0..1 range for shader
|
||||||
|
[hsla.hue / 360.0, hsla.saturation, hsla.lightness, hsla.alpha]
|
||||||
|
}
|
||||||
|
InterpolationColorSpace::Hsv | InterpolationColorSpace::HsvLong => {
|
||||||
|
let hsva: Hsva = color.into();
|
||||||
|
// Normalize hue to 0..1 range for shader
|
||||||
|
[hsva.hue / 360.0, hsva.saturation, hsva.value, hsva.alpha]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn prepare_gradient(
|
pub fn prepare_gradient(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
render_device: Res<RenderDevice>,
|
render_device: Res<RenderDevice>,
|
||||||
@ -804,8 +834,8 @@ pub fn prepare_gradient(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let start_color = start_stop.0.to_f32_array();
|
let start_color = convert_color_to_space(start_stop.0, gradient.color_space);
|
||||||
let end_color = end_stop.0.to_f32_array();
|
let end_color = convert_color_to_space(end_stop.0, gradient.color_space);
|
||||||
let mut stop_flags = flags;
|
let mut stop_flags = flags;
|
||||||
if 0. < start_stop.1
|
if 0. < start_stop.1
|
||||||
&& (stop_index == gradient.stops_range.start || segment_count == 0)
|
&& (stop_index == gradient.stops_range.start || segment_count == 0)
|
||||||
|
@ -114,26 +114,6 @@ fn fragment(in: GradientVertexOutput) -> @location(0) vec4<f32> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function converts two linear rgba colors to srgba space, mixes them, and then converts the result back to linear rgb space.
|
|
||||||
fn mix_linear_rgba_in_srgba_space(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f32> {
|
|
||||||
let a_srgb = pow(a.rgb, vec3(1. / 2.2));
|
|
||||||
let b_srgb = pow(b.rgb, vec3(1. / 2.2));
|
|
||||||
let mixed_srgb = mix(a_srgb, b_srgb, t);
|
|
||||||
return vec4(pow(mixed_srgb, vec3(2.2)), mix(a.a, b.a, t));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn linear_rgba_to_oklaba(c: vec4<f32>) -> vec4<f32> {
|
|
||||||
let l = pow(0.41222146 * c.x + 0.53633255 * c.y + 0.051445995 * c.z, 1. / 3.);
|
|
||||||
let m = pow(0.2119035 * c.x + 0.6806995 * c.y + 0.10739696 * c.z, 1. / 3.);
|
|
||||||
let s = pow(0.08830246 * c.x + 0.28171885 * c.y + 0.6299787 * c.z, 1. / 3.);
|
|
||||||
return vec4(
|
|
||||||
0.21045426 * l + 0.7936178 * m - 0.004072047 * s,
|
|
||||||
1.9779985 * l - 2.4285922 * m + 0.4505937 * s,
|
|
||||||
0.025904037 * l + 0.78277177 * m - 0.80867577 * s,
|
|
||||||
c.a
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn oklaba_to_linear_rgba(c: vec4<f32>) -> vec4<f32> {
|
fn oklaba_to_linear_rgba(c: vec4<f32>) -> vec4<f32> {
|
||||||
let l_ = c.x + 0.39633778 * c.y + 0.21580376 * c.z;
|
let l_ = c.x + 0.39633778 * c.y + 0.21580376 * c.z;
|
||||||
let m_ = c.x - 0.105561346 * c.y - 0.06385417 * c.z;
|
let m_ = c.x - 0.105561346 * c.y - 0.06385417 * c.z;
|
||||||
@ -149,33 +129,6 @@ fn oklaba_to_linear_rgba(c: vec4<f32>) -> vec4<f32> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mix_linear_rgba_in_oklaba_space(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f32> {
|
|
||||||
return oklaba_to_linear_rgba(mix(linear_rgba_to_oklaba(a), linear_rgba_to_oklaba(b), t));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn linear_rgba_to_hsla(c: vec4<f32>) -> vec4<f32> {
|
|
||||||
let max = max(max(c.r, c.g), c.b);
|
|
||||||
let min = min(min(c.r, c.g), c.b);
|
|
||||||
let l = (max + min) * 0.5;
|
|
||||||
if max == min {
|
|
||||||
return vec4(0., 0., l, c.a);
|
|
||||||
} else {
|
|
||||||
let delta = max - min;
|
|
||||||
let s = delta / (1. - abs(2. * l - 1.));
|
|
||||||
var h = 0.;
|
|
||||||
if max == c.r {
|
|
||||||
h = ((c.g - c.b) / delta) % 6.;
|
|
||||||
} else if max == c.g {
|
|
||||||
h = ((c.b - c.r) / delta) + 2.;
|
|
||||||
} else {
|
|
||||||
h = ((c.r - c.g) / delta) + 4.;
|
|
||||||
}
|
|
||||||
h = h / 6.;
|
|
||||||
return vec4<f32>(h, s, l, c.a);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn hsla_to_linear_rgba(hsl: vec4<f32>) -> vec4<f32> {
|
fn hsla_to_linear_rgba(hsl: vec4<f32>) -> vec4<f32> {
|
||||||
let h = hsl.x;
|
let h = hsl.x;
|
||||||
let s = hsl.y;
|
let s = hsl.y;
|
||||||
@ -203,30 +156,6 @@ fn hsla_to_linear_rgba(hsl: vec4<f32>) -> vec4<f32> {
|
|||||||
return vec4<f32>(r + m, g + m, b + m, hsl.a);
|
return vec4<f32>(r + m, g + m, b + m, hsl.a);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn linear_rgba_to_hsva(c: vec4<f32>) -> vec4<f32> {
|
|
||||||
let maxc = max(max(c.r, c.g), c.b);
|
|
||||||
let minc = min(min(c.r, c.g), c.b);
|
|
||||||
let delta = maxc - minc;
|
|
||||||
var h: f32 = 0.0;
|
|
||||||
var s: f32 = 0.0;
|
|
||||||
let v: f32 = maxc;
|
|
||||||
if delta != 0.0 {
|
|
||||||
s = delta / maxc;
|
|
||||||
if maxc == c.r {
|
|
||||||
h = ((c.g - c.b) / delta) % 6.0;
|
|
||||||
} else if maxc == c.g {
|
|
||||||
h = ((c.b - c.r) / delta) + 2.0;
|
|
||||||
} else {
|
|
||||||
h = ((c.r - c.g) / delta) + 4.0;
|
|
||||||
}
|
|
||||||
h = h / 6.0;
|
|
||||||
if h < 0.0 {
|
|
||||||
h = h + 1.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return vec4<f32>(h, s, v, c.a);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn hsva_to_linear_rgba(hsva: vec4<f32>) -> vec4<f32> {
|
fn hsva_to_linear_rgba(hsva: vec4<f32>) -> vec4<f32> {
|
||||||
let h = hsva.x * 6.0;
|
let h = hsva.x * 6.0;
|
||||||
let s = hsva.y;
|
let s = hsva.y;
|
||||||
@ -253,14 +182,6 @@ fn hsva_to_linear_rgba(hsva: vec4<f32>) -> vec4<f32> {
|
|||||||
return vec4<f32>(r + m, g + m, b + m, hsva.a);
|
return vec4<f32>(r + m, g + m, b + m, hsva.a);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// hue is left in radians and not converted to degrees
|
|
||||||
fn linear_rgba_to_oklcha(c: vec4<f32>) -> vec4<f32> {
|
|
||||||
let o = linear_rgba_to_oklaba(c);
|
|
||||||
let chroma = sqrt(o.y * o.y + o.z * o.z);
|
|
||||||
let hue = atan2(o.z, o.y);
|
|
||||||
return vec4(o.x, chroma, rem_euclid(hue, TAU), o.a);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn oklcha_to_linear_rgba(c: vec4<f32>) -> vec4<f32> {
|
fn oklcha_to_linear_rgba(c: vec4<f32>) -> vec4<f32> {
|
||||||
let a = c.y * cos(c.z);
|
let a = c.y * cos(c.z);
|
||||||
let b = c.y * sin(c.z);
|
let b = c.y * sin(c.z);
|
||||||
@ -271,90 +192,6 @@ fn rem_euclid(a: f32, b: f32) -> f32 {
|
|||||||
return ((a % b) + b) % b;
|
return ((a % b) + b) % b;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lerp_hue(a: f32, b: f32, t: f32) -> f32 {
|
|
||||||
let diff = rem_euclid(b - a + PI, TAU) - PI;
|
|
||||||
return rem_euclid(a + diff * t, TAU);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lerp_hue_long(a: f32, b: f32, t: f32) -> f32 {
|
|
||||||
let diff = rem_euclid(b - a + PI, TAU) - PI;
|
|
||||||
return rem_euclid(a + (diff + select(TAU, -TAU, 0. < diff)) * t, TAU);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mix_oklcha(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f32> {
|
|
||||||
let ah = select(a.z, b.z, a.y == 0.);
|
|
||||||
let bh = select(b.z, a.z, b.y == 0.);
|
|
||||||
return vec4(
|
|
||||||
mix(a.xy, b.xy, t),
|
|
||||||
lerp_hue(ah, bh, t),
|
|
||||||
mix(a.a, b.a, t)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mix_oklcha_long(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f32> {
|
|
||||||
let ah = select(a.z, b.z, a.y == 0.);
|
|
||||||
let bh = select(b.z, a.z, b.y == 0.);
|
|
||||||
return vec4(
|
|
||||||
mix(a.xy, b.xy, t),
|
|
||||||
lerp_hue_long(ah, bh, t),
|
|
||||||
mix(a.w, b.w, t)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mix_linear_rgba_in_oklcha_space(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f32> {
|
|
||||||
return oklcha_to_linear_rgba(mix_oklcha(linear_rgba_to_oklcha(a), linear_rgba_to_oklcha(b), t));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mix_linear_rgba_in_oklcha_space_long(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f32> {
|
|
||||||
return oklcha_to_linear_rgba(mix_oklcha_long(linear_rgba_to_oklcha(a), linear_rgba_to_oklcha(b), t));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mix_linear_rgba_in_hsva_space(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f32> {
|
|
||||||
let ha = linear_rgba_to_hsva(a);
|
|
||||||
let hb = linear_rgba_to_hsva(b);
|
|
||||||
var h: f32;
|
|
||||||
if ha.y == 0. {
|
|
||||||
h = hb.x;
|
|
||||||
} else if hb.y == 0. {
|
|
||||||
h = ha.x;
|
|
||||||
} else {
|
|
||||||
h = lerp_hue(ha.x * TAU, hb.x * TAU, t) / TAU;
|
|
||||||
}
|
|
||||||
let s = mix(ha.y, hb.y, t);
|
|
||||||
let v = mix(ha.z, hb.z, t);
|
|
||||||
let a_alpha = mix(ha.a, hb.a, t);
|
|
||||||
return hsva_to_linear_rgba(vec4<f32>(h, s, v, a_alpha));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mix_linear_rgba_in_hsva_space_long(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f32> {
|
|
||||||
let ha = linear_rgba_to_hsva(a);
|
|
||||||
let hb = linear_rgba_to_hsva(b);
|
|
||||||
let h = lerp_hue_long(ha.x * TAU, hb.x * TAU, t) / TAU;
|
|
||||||
let s = mix(ha.y, hb.y, t);
|
|
||||||
let v = mix(ha.z, hb.z, t);
|
|
||||||
let a_alpha = mix(ha.a, hb.a, t);
|
|
||||||
return hsva_to_linear_rgba(vec4<f32>(h, s, v, a_alpha));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mix_linear_rgba_in_hsla_space(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f32> {
|
|
||||||
let ha = linear_rgba_to_hsla(a);
|
|
||||||
let hb = linear_rgba_to_hsla(b);
|
|
||||||
let h = lerp_hue(ha.x * TAU, hb.x * TAU, t) / TAU;
|
|
||||||
let s = mix(ha.y, hb.y, t);
|
|
||||||
let l = mix(ha.z, hb.z, t);
|
|
||||||
let a_alpha = mix(ha.a, hb.a, t);
|
|
||||||
return hsla_to_linear_rgba(vec4<f32>(h, s, l, a_alpha));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mix_linear_rgba_in_hsla_space_long(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f32> {
|
|
||||||
let ha = linear_rgba_to_hsla(a);
|
|
||||||
let hb = linear_rgba_to_hsla(b);
|
|
||||||
let h = lerp_hue_long(ha.x * TAU, hb.x * TAU, t) / TAU;
|
|
||||||
let s = mix(ha.y, hb.y, t);
|
|
||||||
let l = mix(ha.z, hb.z, t);
|
|
||||||
let a_alpha = mix(ha.a, hb.a, t);
|
|
||||||
return hsla_to_linear_rgba(vec4<f32>(h, s, l, a_alpha));
|
|
||||||
}
|
|
||||||
|
|
||||||
// These functions are used to calculate the distance in gradient space from the start of the gradient to the point.
|
// These functions are used to calculate the distance in gradient space from the start of the gradient to the point.
|
||||||
// The distance in gradient space is then used to interpolate between the start and end colors.
|
// The distance in gradient space is then used to interpolate between the start and end colors.
|
||||||
@ -386,6 +223,126 @@ fn conic_distance(
|
|||||||
return (((angle - start) % TAU) + TAU) % TAU;
|
return (((angle - start) % TAU) + TAU) % TAU;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn mix_oklcha(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f32> {
|
||||||
|
let hue_diff = b.z - a.z;
|
||||||
|
var adjusted_hue = a.z;
|
||||||
|
if abs(hue_diff) > PI {
|
||||||
|
if hue_diff > 0.0 {
|
||||||
|
adjusted_hue = a.z + (hue_diff - TAU) * t;
|
||||||
|
} else {
|
||||||
|
adjusted_hue = a.z + (hue_diff + TAU) * t;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
adjusted_hue = a.z + hue_diff * t;
|
||||||
|
}
|
||||||
|
return vec4(
|
||||||
|
mix(a.x, b.x, t),
|
||||||
|
mix(a.y, b.y, t),
|
||||||
|
rem_euclid(adjusted_hue, TAU),
|
||||||
|
mix(a.w, b.w, t)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mix_oklcha_long(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f32> {
|
||||||
|
let hue_diff = b.z - a.z;
|
||||||
|
var adjusted_hue = a.z;
|
||||||
|
if abs(hue_diff) < PI {
|
||||||
|
if hue_diff >= 0.0 {
|
||||||
|
adjusted_hue = a.z + (hue_diff - TAU) * t;
|
||||||
|
} else {
|
||||||
|
adjusted_hue = a.z + (hue_diff + TAU) * t;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
adjusted_hue = a.z + hue_diff * t;
|
||||||
|
}
|
||||||
|
return vec4(
|
||||||
|
mix(a.x, b.x, t),
|
||||||
|
mix(a.y, b.y, t),
|
||||||
|
rem_euclid(adjusted_hue, TAU),
|
||||||
|
mix(a.w, b.w, t)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mix_hsla(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f32> {
|
||||||
|
let hue_diff = b.x - a.x;
|
||||||
|
var adjusted_hue = a.x;
|
||||||
|
if abs(hue_diff) > 0.5 {
|
||||||
|
if hue_diff > 0.0 {
|
||||||
|
adjusted_hue = a.x + (hue_diff - 1.0) * t;
|
||||||
|
} else {
|
||||||
|
adjusted_hue = a.x + (hue_diff + 1.0) * t;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
adjusted_hue = a.x + hue_diff * t;
|
||||||
|
}
|
||||||
|
return vec4(
|
||||||
|
fract(adjusted_hue),
|
||||||
|
mix(a.y, b.y, t),
|
||||||
|
mix(a.z, b.z, t),
|
||||||
|
mix(a.w, b.w, t)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mix_hsla_long(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f32> {
|
||||||
|
let hue_diff = b.x - a.x;
|
||||||
|
var adjusted_hue = a.x;
|
||||||
|
if abs(hue_diff) < 0.5 {
|
||||||
|
if hue_diff >= 0.0 {
|
||||||
|
adjusted_hue = a.x + (hue_diff - 1.0) * t;
|
||||||
|
} else {
|
||||||
|
adjusted_hue = a.x + (hue_diff + 1.0) * t;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
adjusted_hue = a.x + hue_diff * t;
|
||||||
|
}
|
||||||
|
return vec4(
|
||||||
|
fract(adjusted_hue),
|
||||||
|
mix(a.y, b.y, t),
|
||||||
|
mix(a.z, b.z, t),
|
||||||
|
mix(a.w, b.w, t)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mix_hsva(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f32> {
|
||||||
|
let hue_diff = b.x - a.x;
|
||||||
|
var adjusted_hue = a.x;
|
||||||
|
if abs(hue_diff) > 0.5 {
|
||||||
|
if hue_diff > 0.0 {
|
||||||
|
adjusted_hue = a.x + (hue_diff - 1.0) * t;
|
||||||
|
} else {
|
||||||
|
adjusted_hue = a.x + (hue_diff + 1.0) * t;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
adjusted_hue = a.x + hue_diff * t;
|
||||||
|
}
|
||||||
|
return vec4(
|
||||||
|
fract(adjusted_hue),
|
||||||
|
mix(a.y, b.y, t),
|
||||||
|
mix(a.z, b.z, t),
|
||||||
|
mix(a.w, b.w, t)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mix_hsva_long(a: vec4<f32>, b: vec4<f32>, t: f32) -> vec4<f32> {
|
||||||
|
let hue_diff = b.x - a.x;
|
||||||
|
var adjusted_hue = a.x;
|
||||||
|
if abs(hue_diff) < 0.5 {
|
||||||
|
if hue_diff >= 0.0 {
|
||||||
|
adjusted_hue = a.x + (hue_diff - 1.0) * t;
|
||||||
|
} else {
|
||||||
|
adjusted_hue = a.x + (hue_diff + 1.0) * t;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
adjusted_hue = a.x + hue_diff * t;
|
||||||
|
}
|
||||||
|
return vec4(
|
||||||
|
fract(adjusted_hue),
|
||||||
|
mix(a.y, b.y, t),
|
||||||
|
mix(a.z, b.z, t),
|
||||||
|
mix(a.w, b.w, t)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fn interpolate_gradient(
|
fn interpolate_gradient(
|
||||||
distance: f32,
|
distance: f32,
|
||||||
start_color: vec4<f32>,
|
start_color: vec4<f32>,
|
||||||
@ -426,23 +383,23 @@ fn interpolate_gradient(
|
|||||||
} else {
|
} else {
|
||||||
t = 0.5 * (1 + (t - hint) / (1.0 - hint));
|
t = 0.5 * (1 + (t - hint) / (1.0 - hint));
|
||||||
}
|
}
|
||||||
|
#ifdef IN_OKLCH
|
||||||
#ifdef IN_SRGB
|
return oklcha_to_linear_rgba(mix_oklcha(start_color, end_color, t));
|
||||||
return mix_linear_rgba_in_srgba_space(start_color, end_color, t);
|
|
||||||
#else ifdef IN_OKLAB
|
|
||||||
return mix_linear_rgba_in_oklaba_space(start_color, end_color, t);
|
|
||||||
#else ifdef IN_OKLCH
|
|
||||||
return mix_linear_rgba_in_oklcha_space(start_color, end_color, t);
|
|
||||||
#else ifdef IN_OKLCH_LONG
|
#else ifdef IN_OKLCH_LONG
|
||||||
return mix_linear_rgba_in_oklcha_space_long(start_color, end_color, t);
|
return oklcha_to_linear_rgba(mix_oklcha_long(start_color, end_color, t));
|
||||||
#else ifdef IN_HSV
|
#else ifdef IN_HSV
|
||||||
return mix_linear_rgba_in_hsva_space(start_color, end_color, t);
|
return hsva_to_linear_rgba(mix_hsva(start_color, end_color, t));
|
||||||
#else ifdef IN_HSV_LONG
|
#else ifdef IN_HSV_LONG
|
||||||
return mix_linear_rgba_in_hsva_space_long(start_color, end_color, t);
|
return hsva_to_linear_rgba(mix_hsva_long(start_color, end_color, t));
|
||||||
#else ifdef IN_HSL
|
#else ifdef IN_HSL
|
||||||
return mix_linear_rgba_in_hsla_space(start_color, end_color, t);
|
return hsla_to_linear_rgba(mix_hsla(start_color, end_color, t));
|
||||||
#else ifdef IN_HSL_LONG
|
#else ifdef IN_HSL_LONG
|
||||||
return mix_linear_rgba_in_hsla_space_long(start_color, end_color, t);
|
return hsla_to_linear_rgba(mix_hsla_long(start_color, end_color, t));
|
||||||
|
#else ifdef IN_OKLAB
|
||||||
|
return oklaba_to_linear_rgba(mix(start_color, end_color, t));
|
||||||
|
#else ifdef IN_SRGB
|
||||||
|
let mixed_srgb = mix(start_color, end_color, t);
|
||||||
|
return vec4(pow(mixed_srgb.rgb, vec3(2.2)), mixed_srgb.a);
|
||||||
#else
|
#else
|
||||||
return mix(start_color, end_color, t);
|
return mix(start_color, end_color, t);
|
||||||
#endif
|
#endif
|
||||||
|
145
examples/stress_tests/many_gradients.rs
Normal file
145
examples/stress_tests/many_gradients.rs
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
//! Stress test demonstrating gradient performance improvements.
|
||||||
|
//!
|
||||||
|
//! This example creates many UI nodes with gradients to measure the performance
|
||||||
|
//! impact of pre-converting colors to the target color space on the CPU.
|
||||||
|
|
||||||
|
use argh::FromArgs;
|
||||||
|
use bevy::{
|
||||||
|
color::palettes::css::*,
|
||||||
|
diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
|
||||||
|
prelude::*,
|
||||||
|
ui::{BackgroundGradient, LinearGradient, ColorStop, Gradient, RepeatedGridTrack, Display, InterpolationColorSpace},
|
||||||
|
window::{PresentMode, WindowResolution},
|
||||||
|
};
|
||||||
|
|
||||||
|
const COLS: usize = 30;
|
||||||
|
|
||||||
|
#[derive(FromArgs, Resource, Debug)]
|
||||||
|
/// Gradient stress test
|
||||||
|
struct Args {
|
||||||
|
/// how many gradients per group (default: 900)
|
||||||
|
#[argh(option, default = "900")]
|
||||||
|
gradient_count: usize,
|
||||||
|
|
||||||
|
/// whether to animate gradients by changing colors
|
||||||
|
#[argh(switch)]
|
||||||
|
animate: bool,
|
||||||
|
|
||||||
|
/// use sRGB interpolation
|
||||||
|
#[argh(switch)]
|
||||||
|
srgb: bool,
|
||||||
|
|
||||||
|
/// use HSL interpolation
|
||||||
|
#[argh(switch)]
|
||||||
|
hsl: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let args: Args = argh::from_env();
|
||||||
|
let total_gradients = args.gradient_count;
|
||||||
|
|
||||||
|
println!("Gradient stress test with {} gradients", total_gradients);
|
||||||
|
println!("Color space: {}", if args.srgb {
|
||||||
|
"sRGB"
|
||||||
|
} else if args.hsl {
|
||||||
|
"HSL"
|
||||||
|
} else {
|
||||||
|
"OkLab (default)"
|
||||||
|
});
|
||||||
|
|
||||||
|
App::new()
|
||||||
|
.add_plugins((
|
||||||
|
LogDiagnosticsPlugin::default(),
|
||||||
|
FrameTimeDiagnosticsPlugin::default(),
|
||||||
|
DefaultPlugins.set(WindowPlugin {
|
||||||
|
primary_window: Some(Window {
|
||||||
|
title: "Gradient Stress Test".to_string(),
|
||||||
|
resolution: WindowResolution::new(1920.0, 1080.0),
|
||||||
|
present_mode: PresentMode::AutoNoVsync,
|
||||||
|
..default()
|
||||||
|
}),
|
||||||
|
..default()
|
||||||
|
}),
|
||||||
|
))
|
||||||
|
.insert_resource(args)
|
||||||
|
.add_systems(Startup, setup)
|
||||||
|
.add_systems(Update, animate_gradients)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup(mut commands: Commands, args: Res<Args>) {
|
||||||
|
commands.spawn(Camera2d);
|
||||||
|
|
||||||
|
let rows_to_spawn = (args.gradient_count + COLS - 1) / COLS;
|
||||||
|
|
||||||
|
// Create a grid of gradients
|
||||||
|
commands
|
||||||
|
.spawn(Node {
|
||||||
|
width: Val::Percent(100.0),
|
||||||
|
height: Val::Percent(100.0),
|
||||||
|
display: Display::Grid,
|
||||||
|
grid_template_columns: RepeatedGridTrack::flex(COLS as u16, 1.0),
|
||||||
|
grid_template_rows: RepeatedGridTrack::flex(rows_to_spawn as u16, 1.0),
|
||||||
|
..default()
|
||||||
|
})
|
||||||
|
.with_children(|parent| {
|
||||||
|
for i in 0..args.gradient_count {
|
||||||
|
let angle = (i as f32 * 10.0) % 360.0;
|
||||||
|
|
||||||
|
let mut gradient = LinearGradient::new(angle, vec![
|
||||||
|
ColorStop::new(RED, Val::Percent(0.0)),
|
||||||
|
ColorStop::new(BLUE, Val::Percent(100.0)),
|
||||||
|
]);
|
||||||
|
|
||||||
|
gradient.color_space = if args.srgb {
|
||||||
|
InterpolationColorSpace::Srgb
|
||||||
|
} else if args.hsl {
|
||||||
|
InterpolationColorSpace::Hsl
|
||||||
|
} else {
|
||||||
|
InterpolationColorSpace::OkLab
|
||||||
|
};
|
||||||
|
|
||||||
|
parent.spawn((
|
||||||
|
Node {
|
||||||
|
width: Val::Percent(100.0),
|
||||||
|
height: Val::Percent(100.0),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
BackgroundGradient(vec![Gradient::Linear(gradient)]),
|
||||||
|
GradientNode { index: i },
|
||||||
|
));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
struct GradientNode {
|
||||||
|
index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn animate_gradients(
|
||||||
|
mut gradients: Query<(&mut BackgroundGradient, &GradientNode)>,
|
||||||
|
args: Res<Args>,
|
||||||
|
time: Res<Time>,
|
||||||
|
) {
|
||||||
|
if !args.animate {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let t = time.elapsed_secs();
|
||||||
|
|
||||||
|
for (mut bg_gradient, node) in &mut gradients {
|
||||||
|
let offset = node.index as f32 * 0.01;
|
||||||
|
let hue_shift = (t + offset).sin() * 0.5 + 0.5;
|
||||||
|
|
||||||
|
if let Some(Gradient::Linear(gradient)) = bg_gradient.0.get_mut(0) {
|
||||||
|
let color1 = Color::hsl(hue_shift * 360.0, 1.0, 0.5);
|
||||||
|
let color2 = Color::hsl((hue_shift + 0.3) * 360.0 % 360.0, 1.0, 0.5);
|
||||||
|
|
||||||
|
gradient.stops = vec![
|
||||||
|
ColorStop::new(color1, Val::Percent(0.0)),
|
||||||
|
ColorStop::new(color2, Val::Percent(100.0)),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user