Skip to content

Commit 7ff4694

Browse files
committed
Add image/svg support to iced_glow
#674 Uses image/svg support in `iced_graphics`. The is not currently using an atlas, and uses one texture/draw per image. This should be good enough for now; supporting images with glow is better than not supporting them, and if something else performs better, that improvement can be made without any change to the public API.
1 parent f89ae1c commit 7ff4694

8 files changed

Lines changed: 368 additions & 8 deletions

File tree

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ resolver = "2"
1515
[features]
1616
default = ["wgpu"]
1717
# Enables the `Image` widget
18-
image = ["iced_wgpu/image", "image_rs"]
18+
image = ["iced_wgpu?/image", "iced_glow?/image", "image_rs"]
1919
# Enables the `Svg` widget
20-
svg = ["iced_wgpu/svg"]
20+
svg = ["iced_wgpu?/svg", "iced_glow?/svg"]
2121
# Enables the `Canvas` widget
2222
canvas = ["iced_graphics/canvas"]
2323
# Enables the `QRCode` widget

glow/Cargo.toml

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,23 @@ license = "MIT AND OFL-1.1"
88
repository = "https://github.com/iced-rs/iced"
99

1010
[features]
11+
svg = ["iced_graphics/svg"]
12+
image = ["image_rs", "iced_graphics/image", "png", "jpeg", "jpeg_rayon", "gif", "webp", "bmp"]
13+
image_rs = ["iced_graphics/image_rs"]
14+
png = ["iced_graphics/png"]
15+
jpeg = ["iced_graphics/jpeg"]
16+
jpeg_rayon = ["iced_graphics/jpeg_rayon"]
17+
gif = ["iced_graphics/gif"]
18+
webp = ["iced_graphics/webp"]
19+
pnm = ["iced_graphics/pnm"]
20+
ico = ["iced_graphics/ico"]
21+
bmp = ["iced_graphics/bmp"]
22+
hdr = ["iced_graphics/hdr"]
23+
dds = ["iced_graphics/dds"]
24+
farbfeld = ["iced_graphics/farbfeld"]
1125
canvas = ["iced_graphics/canvas"]
1226
qr_code = ["iced_graphics/qr_code"]
1327
default_system_font = ["iced_graphics/font-source"]
14-
# Not supported yet!
15-
image = []
16-
svg = []
1728

1829
[dependencies]
1930
glow = "0.11.1"
@@ -22,6 +33,8 @@ glyph_brush = "0.7"
2233
euclid = "0.22"
2334
bytemuck = "1.4"
2435
log = "0.4"
36+
kamadak-exif = "0.5"
37+
bitflags = "1.2"
2538

2639
[dependencies.iced_native]
2740
version = "0.5"

glow/src/backend.rs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#[cfg(any(feature = "image_rs", feature = "svg"))]
2+
use crate::image;
13
use crate::quad;
24
use crate::text;
35
use crate::{program, triangle};
@@ -15,6 +17,8 @@ use iced_native::{Font, Size};
1517
/// [`iced`]: https://github.com/iced-rs/iced
1618
#[derive(Debug)]
1719
pub struct Backend {
20+
#[cfg(any(feature = "image_rs", feature = "svg"))]
21+
image_pipeline: image::Pipeline,
1822
quad_pipeline: quad::Pipeline,
1923
text_pipeline: text::Pipeline,
2024
triangle_pipeline: triangle::Pipeline,
@@ -32,10 +36,14 @@ impl Backend {
3236

3337
let shader_version = program::Version::new(gl);
3438

39+
#[cfg(any(feature = "image_rs", feature = "svg"))]
40+
let image_pipeline = image::Pipeline::new(gl, &shader_version);
3541
let quad_pipeline = quad::Pipeline::new(gl, &shader_version);
3642
let triangle_pipeline = triangle::Pipeline::new(gl, &shader_version);
3743

3844
Self {
45+
#[cfg(any(feature = "image_rs", feature = "svg"))]
46+
image_pipeline,
3947
quad_pipeline,
4048
text_pipeline,
4149
triangle_pipeline,
@@ -70,6 +78,9 @@ impl Backend {
7078
viewport_size.height,
7179
);
7280
}
81+
82+
#[cfg(any(feature = "image_rs", feature = "svg"))]
83+
self.image_pipeline.trim_cache(gl);
7384
}
7485

7586
fn flush(
@@ -112,6 +123,15 @@ impl Backend {
112123
);
113124
}
114125

126+
#[cfg(any(feature = "image_rs", feature = "svg"))]
127+
if !layer.images.is_empty() {
128+
let scaled = transformation
129+
* Transformation::scale(scale_factor, scale_factor);
130+
131+
self.image_pipeline
132+
.draw(gl, scaled, scale_factor, &layer.images);
133+
}
134+
115135
if !layer.text.is_empty() {
116136
for text in layer.text.iter() {
117137
// Target physical coordinates directly to avoid blurry text
@@ -236,10 +256,10 @@ impl backend::Text for Backend {
236256
}
237257
}
238258

239-
#[cfg(feature = "image")]
259+
#[cfg(feature = "image_rs")]
240260
impl backend::Image for Backend {
241-
fn dimensions(&self, _handle: &iced_native::image::Handle) -> (u32, u32) {
242-
(50, 50)
261+
fn dimensions(&self, handle: &iced_native::image::Handle) -> (u32, u32) {
262+
self.image_pipeline.dimensions(handle)
243263
}
244264
}
245265

glow/src/image.rs

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
use crate::program::{self, Shader};
2+
use crate::Transformation;
3+
use glow::HasContext;
4+
use iced_graphics::layer;
5+
#[cfg(feature = "image_rs")]
6+
use std::cell::RefCell;
7+
8+
pub use iced_graphics::triangle::{Mesh2D, Vertex2D};
9+
10+
#[cfg(feature = "image_rs")]
11+
use iced_graphics::image::raster;
12+
13+
#[cfg(feature = "svg")]
14+
use iced_graphics::image::vector;
15+
16+
mod textures;
17+
use textures::{Entry, Textures};
18+
19+
#[derive(Debug)]
20+
pub(crate) struct Pipeline {
21+
program: <glow::Context as HasContext>::Program,
22+
vertex_array: <glow::Context as HasContext>::VertexArray,
23+
vertex_buffer: <glow::Context as HasContext>::Buffer,
24+
transform_location: <glow::Context as HasContext>::UniformLocation,
25+
textures: Textures,
26+
#[cfg(feature = "image_rs")]
27+
raster_cache: RefCell<raster::Cache<Textures>>,
28+
#[cfg(feature = "svg")]
29+
vector_cache: vector::Cache<Textures>,
30+
}
31+
32+
impl Pipeline {
33+
pub fn new(
34+
gl: &glow::Context,
35+
shader_version: &program::Version,
36+
) -> Pipeline {
37+
let program = unsafe {
38+
let vertex_shader = Shader::vertex(
39+
gl,
40+
shader_version,
41+
include_str!("shader/common/image.vert"),
42+
);
43+
let fragment_shader = Shader::fragment(
44+
gl,
45+
shader_version,
46+
include_str!("shader/common/image.frag"),
47+
);
48+
49+
program::create(
50+
gl,
51+
&[vertex_shader, fragment_shader],
52+
&[(0, "i_Position")],
53+
)
54+
};
55+
56+
let transform_location =
57+
unsafe { gl.get_uniform_location(program, "u_Transform") }
58+
.expect("Get transform location");
59+
60+
unsafe {
61+
gl.use_program(Some(program));
62+
63+
let transform: [f32; 16] = Transformation::identity().into();
64+
gl.uniform_matrix_4_f32_slice(
65+
Some(&transform_location),
66+
false,
67+
&transform,
68+
);
69+
70+
gl.use_program(None);
71+
}
72+
73+
let vertex_buffer =
74+
unsafe { gl.create_buffer().expect("Create vertex buffer") };
75+
let vertex_array =
76+
unsafe { gl.create_vertex_array().expect("Create vertex array") };
77+
78+
unsafe {
79+
gl.bind_vertex_array(Some(vertex_array));
80+
gl.bind_buffer(glow::ARRAY_BUFFER, Some(vertex_buffer));
81+
82+
let vertices = &[0u8, 0, 1, 0, 0, 1, 1, 1];
83+
gl.buffer_data_size(
84+
glow::ARRAY_BUFFER,
85+
vertices.len() as i32,
86+
glow::STATIC_DRAW,
87+
);
88+
gl.buffer_sub_data_u8_slice(
89+
glow::ARRAY_BUFFER,
90+
0,
91+
bytemuck::cast_slice(vertices),
92+
);
93+
94+
gl.enable_vertex_attrib_array(0);
95+
gl.vertex_attrib_pointer_f32(
96+
0,
97+
2,
98+
glow::UNSIGNED_BYTE,
99+
false,
100+
0,
101+
0,
102+
);
103+
104+
gl.bind_buffer(glow::ARRAY_BUFFER, None);
105+
gl.bind_vertex_array(None);
106+
}
107+
108+
Pipeline {
109+
program,
110+
vertex_array,
111+
vertex_buffer,
112+
transform_location,
113+
textures: Textures::new(),
114+
#[cfg(feature = "image_rs")]
115+
raster_cache: RefCell::new(raster::Cache::default()),
116+
#[cfg(feature = "svg")]
117+
vector_cache: vector::Cache::default(),
118+
}
119+
}
120+
121+
#[cfg(feature = "image_rs")]
122+
pub fn dimensions(
123+
&self,
124+
handle: &iced_native::image::Handle,
125+
) -> (u32, u32) {
126+
self.raster_cache.borrow_mut().load(handle).dimensions()
127+
}
128+
129+
pub fn draw(
130+
&mut self,
131+
mut gl: &glow::Context,
132+
transformation: Transformation,
133+
_scale_factor: f32,
134+
images: &[layer::Image],
135+
) {
136+
unsafe {
137+
gl.use_program(Some(self.program));
138+
gl.bind_vertex_array(Some(self.vertex_array));
139+
gl.bind_buffer(glow::ARRAY_BUFFER, Some(self.vertex_buffer));
140+
}
141+
142+
#[cfg(feature = "image_rs")]
143+
let mut raster_cache = self.raster_cache.borrow_mut();
144+
for image in images {
145+
let (entry, bounds) = match &image {
146+
#[cfg(feature = "image_rs")]
147+
layer::Image::Raster { handle, bounds } => (
148+
raster_cache.upload(handle, &mut gl, &mut self.textures),
149+
bounds,
150+
),
151+
#[cfg(not(feature = "image_rs"))]
152+
layer::Image::Raster { handle: _, bounds } => (None, bounds),
153+
154+
#[cfg(feature = "svg")]
155+
layer::Image::Vector { handle, bounds } => {
156+
let size = [bounds.width, bounds.height];
157+
(
158+
self.vector_cache.upload(
159+
handle,
160+
size,
161+
_scale_factor,
162+
&mut gl,
163+
&mut self.textures,
164+
),
165+
bounds,
166+
)
167+
}
168+
169+
#[cfg(not(feature = "svg"))]
170+
layer::Image::Vector { handle: _, bounds } => (None, bounds),
171+
};
172+
173+
unsafe {
174+
if let Some(Entry { texture, .. }) = entry {
175+
gl.bind_texture(glow::TEXTURE_2D, Some(*texture))
176+
} else {
177+
continue;
178+
}
179+
180+
let translate = Transformation::translate(bounds.x, bounds.y);
181+
let scale = Transformation::scale(bounds.width, bounds.height);
182+
let transformation = transformation * translate * scale;
183+
let matrix: [f32; 16] = transformation.into();
184+
gl.uniform_matrix_4_f32_slice(
185+
Some(&self.transform_location),
186+
false,
187+
&matrix,
188+
);
189+
190+
gl.draw_arrays(glow::TRIANGLE_STRIP, 0, 4);
191+
192+
gl.bind_texture(glow::TEXTURE_2D, None);
193+
}
194+
}
195+
196+
unsafe {
197+
gl.bind_buffer(glow::ARRAY_BUFFER, None);
198+
gl.bind_vertex_array(None);
199+
gl.use_program(None);
200+
}
201+
}
202+
203+
pub fn trim_cache(&mut self, mut gl: &glow::Context) {
204+
#[cfg(feature = "image_rs")]
205+
self.raster_cache
206+
.borrow_mut()
207+
.trim(&mut self.textures, &mut gl);
208+
209+
#[cfg(feature = "svg")]
210+
self.vector_cache.trim(&mut self.textures, &mut gl);
211+
}
212+
}

0 commit comments

Comments
 (0)