1mod filter;
3mod loader;
4pub mod pipeline;
5pub use filter::{FilteredImage, ImageFilter};
6use pipeline::ImageAdjustments;
7mod mipmaps;
8mod texture;
9mod upload;
10mod video;
11#[cfg(target_os = "macos")]
12pub use gpui_apple::custom_draw::core_video::{CoreVideoFrame, Nv12Matrix};
13#[cfg(all(unix, feature = "wgpu"))]
14pub use gpui_wgpu::custom_draw::dma_buf::{DmaBufFrame, DmaBufLayout};
15#[cfg(target_os = "windows")]
16pub use gpui_windows::custom_draw::dxgi::DxgiFrame;
17pub use video::{VideoFrame, VideoSender, VideoStats, VideoStream, VideoView, video, video_frame};
18
19pub use loader::{ImageJob, LoadStatus, MediaLimits, MediaLoader};
20pub use texture::{MediaImage, UploadStatus};
21pub use upload::{FrameUploadLimits, UploadLimits, UploadStats};
22
23use gpui::{
24 App, Bounds, Corners, CustomGeometry, Element, ElementId, GlobalElementId, InspectorElementId,
25 IntoElement, LayoutId, Pixels, Style, StyleRefinement, Styled, Window,
26};
27use std::sync::Arc;
28
29impl MediaImage {
32 pub fn paint(&self, window: &mut Window, bounds: Bounds<Pixels>, radii: Corners<Pixels>) {
33 self.paint_filtered(window, bounds, radii, ImageFilter::new());
34 }
35 pub fn paint_filtered(
36 &self,
37 window: &mut Window,
38 bounds: Bounds<Pixels>,
39 radii: Corners<Pixels>,
40 filter: ImageFilter,
41 ) {
42 self.paint_content(window, bounds, radii, || Arc::new(self.filtered(filter)));
43 }
44 pub fn paint_adjusted(
45 &self,
46 window: &mut Window,
47 bounds: Bounds<Pixels>,
48 radii: Corners<Pixels>,
49 adjustments: ImageAdjustments,
50 ) {
51 self.paint_content(window, bounds, radii, || {
52 Arc::new(self.adjusted(adjustments))
53 });
54 }
55 fn paint_content(
56 &self,
57 window: &mut Window,
58 bounds: Bounds<Pixels>,
59 radii: Corners<Pixels>,
60 content: impl FnOnce() -> Arc<dyn gpui::CustomContent>,
61 ) {
62 if bounds.is_empty()
63 || bounds.intersect(&window.content_mask().bounds).is_empty()
64 || window.element_opacity() <= 0.
65 {
66 return;
67 }
68 if !matches!(
69 self.upload_status(),
70 UploadStatus::Ready | UploadStatus::Failed(_)
71 ) {
72 window.request_animation_frame();
73 }
74 window.paint_custom(
75 CustomGeometry::RoundedRect {
76 bounds,
77 corner_radii: radii,
78 },
79 content(),
80 );
81 }
82}
83
84pub fn image_view(image: MediaImage) -> MediaImageView {
88 MediaImageView {
89 image,
90 filter: ImageFilter::new(),
91 adjustments: None,
92 wide: false,
93 style: StyleRefinement::default(),
94 }
95}
96pub struct MediaImageView {
98 image: MediaImage,
99 filter: ImageFilter,
100 adjustments: Option<ImageAdjustments>,
101 wide: bool,
102 style: StyleRefinement,
103}
104impl MediaImageView {
105 pub fn wide_gamut(mut self) -> anyhow::Result<Self> {
108 self.image.wide(None)?;
109 anyhow::ensure!(
110 self.filter == ImageFilter::new(),
111 "wide images require linear ImageAdjustments"
112 );
113 self.wide = true;
114 Ok(self)
115 }
116
117 pub fn adjustments(mut self, adjustments: ImageAdjustments) -> Self {
120 self.adjustments = Some(adjustments);
121 self
122 }
123
124 pub fn filter(mut self, filter: ImageFilter) -> Self {
125 self.filter = filter;
126 self.wide = false;
127 self.adjustments = None;
128 self
129 }
130}
131impl Styled for MediaImageView {
132 fn style(&mut self) -> &mut StyleRefinement {
133 &mut self.style
134 }
135}
136impl IntoElement for MediaImageView {
137 type Element = Self;
138 fn into_element(self) -> Self {
139 self
140 }
141}
142impl Element for MediaImageView {
143 type RequestLayoutState = Style;
144 type PrepaintState = ();
145 fn id(&self) -> Option<ElementId> {
146 None
147 }
148 fn source_location(&self) -> Option<&'static std::panic::Location<'static>> {
149 None
150 }
151 fn request_layout(
152 &mut self,
153 _id: Option<&GlobalElementId>,
154 _inspector: Option<&InspectorElementId>,
155 window: &mut Window,
156 cx: &mut App,
157 ) -> (LayoutId, Style) {
158 use refineable::Refineable;
159 let mut style = Style::default();
160 style.refine(&self.style);
161 (window.request_layout(style.clone(), [], cx), style)
162 }
163 fn prepaint(
164 &mut self,
165 _id: Option<&GlobalElementId>,
166 _inspector: Option<&InspectorElementId>,
167 _bounds: Bounds<Pixels>,
168 _style: &mut Style,
169 _window: &mut Window,
170 _cx: &mut App,
171 ) {
172 }
173 fn paint(
174 &mut self,
175 _id: Option<&GlobalElementId>,
176 _inspector: Option<&InspectorElementId>,
177 bounds: Bounds<Pixels>,
178 style: &mut Style,
179 _prepaint: &mut (),
180 window: &mut Window,
181 cx: &mut App,
182 ) {
183 let radii = style
184 .corner_radii
185 .to_pixels(window.rem_size())
186 .clamp_radii_for_quad_size(bounds.size);
187 window.with_element_opacity(style.opacity, |window| {
188 style.paint(bounds, window, cx, |window, _| {
189 if self.wide {
190 self.image.paint_content(window, bounds, radii, || {
191 Arc::new(
192 self.image
193 .wide(self.adjustments)
194 .expect("validated wide image"),
195 )
196 });
197 } else if let Some(adjustments) = self.adjustments {
198 self.image
199 .paint_adjusted(window, bounds, radii, adjustments);
200 } else {
201 self.image
202 .paint_filtered(window, bounds, radii, self.filter);
203 }
204 });
205 });
206 }
207}
208
209#[cfg(test)]
210mod tests;
211
212pub fn mipmapped_rgba8(
217 width: u32,
218 height: u32,
219 rgba: Vec<u8>,
220) -> anyhow::Result<gpui::ImageTextureData> {
221 mipmaps::build(width, height, rgba, || false)
222}