1pub use xgpui_color as color;
5
6#[cfg(feature = "instanced")]
7pub mod instanced;
8
9use anyhow::{Result, ensure};
10use gpui::{CustomContent, DevicePixels, Size};
11use std::any::Any;
12
13#[cfg(test)]
15#[cfg_attr(not(any(target_os = "macos", target_os = "windows")), allow(dead_code))]
16static PRODUCER_ALLOCATIONS: std::sync::atomic::AtomicUsize =
17 std::sync::atomic::AtomicUsize::new(0);
18
19#[cfg(target_os = "windows")]
20mod dx11;
21#[cfg(target_os = "macos")]
22mod metal;
23
24#[derive(Debug)]
32pub struct WideColor {
33 #[cfg_attr(
35 not(any(target_os = "macos", target_os = "windows", feature = "instanced")),
36 allow(dead_code)
37 )]
38 rgba: [f32; 4],
39}
40impl WideColor {
41 pub fn linear_p3(rgba: [f32; 4]) -> Result<Self> {
44 ensure!(
45 rgba.iter().all(|c| c.is_finite() && (0. ..=1.).contains(c)),
46 "linear P3 SDR channels must be finite and in 0..=1"
47 );
48 Ok(Self { rgba })
49 }
50}
51impl CustomContent for WideColor {
52 fn prepare(&self, backend: &dyn Any, extent: Size<DevicePixels>) -> Result<Box<dyn Any>> {
53 validate_extent(extent)?;
54 #[cfg(target_os = "macos")]
55 {
56 self.prepare_metal(backend, extent)
57 }
58 #[cfg(target_os = "windows")]
59 {
60 self.prepare_dx11(backend, extent)
61 }
62 #[cfg(not(any(target_os = "macos", target_os = "windows")))]
63 {
64 let _ = backend;
65 anyhow::bail!("WideColor requires native Metal or D3D11 preparation")
66 }
67 }
68}
69fn validate_extent(extent: Size<DevicePixels>) -> Result<()> {
70 ensure!(
71 (1..=16384).contains(&extent.width.0) && (1..=16384).contains(&extent.height.0),
72 "invalid wide color extent"
73 );
74 ensure!(
75 extent.width.0 as u64 * extent.height.0 as u64 * 8 <= 64 * 1024 * 1024,
76 "wide color exceeds 64 MiB"
77 );
78 Ok(())
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84 use gpui::size;
85 #[test]
86 fn rejects_invalid_color_channels() {
87 for bad in [f32::NAN, f32::INFINITY, f32::NEG_INFINITY, -0.01, 1.01] {
88 for i in 0..4 {
89 let mut rgba = [0.5; 4];
90 rgba[i] = bad;
91 assert!(WideColor::linear_p3(rgba).is_err());
92 }
93 }
94 assert!(WideColor::linear_p3([0.; 4]).is_ok());
95 assert!(WideColor::linear_p3([1.; 4]).is_ok());
96 }
97 #[test]
98 fn checks_float16_budget_and_extent_before_backend_work() {
99 for (w, h) in [(0, 1), (-1, 1), (1, 0), (16385, 1), (4096, 2049)] {
100 assert!(validate_extent(size(DevicePixels(w), DevicePixels(h))).is_err());
101 }
102 assert!(validate_extent(size(DevicePixels(4096), DevicePixels(2048))).is_ok());
103 let color = WideColor::linear_p3([1., 0., 0., 0.5]).unwrap();
104 assert!(
105 color
106 .prepare(&(), size(DevicePixels(0), DevicePixels(1)))
107 .unwrap_err()
108 .to_string()
109 .contains("extent")
110 );
111 }
112 #[test]
113 fn rejects_unknown_backend_without_reinterpreting_color() {
114 let color = WideColor::linear_p3([1., 0., 0., 0.5]).unwrap();
115 assert!(
116 color
117 .prepare(&(), size(DevicePixels(8), DevicePixels(8)))
118 .is_err()
119 );
120 }
121}