summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Ian Douglas Scott <idscott@system76.com>2022-10-24 17:06:02 -0700
committerLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2022-11-05 03:19:38 +0100
commit5575e6ea0897e406674e7e4239808fbf9daa07c3 (patch)
tree888f1b22926a304e7e280710312727fc4137e373
parent2c7c42ee93a61f39562590f6a75eb2dd8b220fb8 (diff)
downloadiced-5575e6ea0897e406674e7e4239808fbf9daa07c3.tar.gz
iced-5575e6ea0897e406674e7e4239808fbf9daa07c3.tar.bz2
iced-5575e6ea0897e406674e7e4239808fbf9daa07c3.zip
Add image/svg support to `iced_glow`
https://github.com/iced-rs/iced/issues/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.
-rw-r--r--Cargo.toml4
-rw-r--r--glow/Cargo.toml19
-rw-r--r--glow/src/backend.rs26
-rw-r--r--glow/src/image.rs212
-rw-r--r--glow/src/image/textures.rs82
-rw-r--r--glow/src/lib.rs2
-rw-r--r--glow/src/shader/common/image.frag22
-rw-r--r--glow/src/shader/common/image.vert9
8 files changed, 368 insertions, 8 deletions
diff --git a/Cargo.toml b/Cargo.toml
index f7d578ba..e4801d7d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -15,9 +15,9 @@ resolver = "2"
[features]
default = ["wgpu"]
# Enables the `Image` widget
-image = ["iced_wgpu/image", "image_rs"]
+image = ["iced_wgpu?/image", "iced_glow?/image", "image_rs"]
# Enables the `Svg` widget
-svg = ["iced_wgpu/svg"]
+svg = ["iced_wgpu?/svg", "iced_glow?/svg"]
# Enables the `Canvas` widget
canvas = ["iced_graphics/canvas"]
# Enables the `QRCode` widget
diff --git a/glow/Cargo.toml b/glow/Cargo.toml
index 18215e9b..476547d4 100644
--- a/glow/Cargo.toml
+++ b/glow/Cargo.toml
@@ -8,12 +8,23 @@ license = "MIT AND OFL-1.1"
repository = "https://github.com/iced-rs/iced"
[features]
+svg = ["iced_graphics/svg"]
+image = ["image_rs", "iced_graphics/image", "png", "jpeg", "jpeg_rayon", "gif", "webp", "bmp"]
+image_rs = ["iced_graphics/image_rs"]
+png = ["iced_graphics/png"]
+jpeg = ["iced_graphics/jpeg"]
+jpeg_rayon = ["iced_graphics/jpeg_rayon"]
+gif = ["iced_graphics/gif"]
+webp = ["iced_graphics/webp"]
+pnm = ["iced_graphics/pnm"]
+ico = ["iced_graphics/ico"]
+bmp = ["iced_graphics/bmp"]
+hdr = ["iced_graphics/hdr"]
+dds = ["iced_graphics/dds"]
+farbfeld = ["iced_graphics/farbfeld"]
canvas = ["iced_graphics/canvas"]
qr_code = ["iced_graphics/qr_code"]
default_system_font = ["iced_graphics/font-source"]
-# Not supported yet!
-image = []
-svg = []
[dependencies]
glow = "0.11.1"
@@ -22,6 +33,8 @@ glyph_brush = "0.7"
euclid = "0.22"
bytemuck = "1.4"
log = "0.4"
+kamadak-exif = "0.5"
+bitflags = "1.2"
[dependencies.iced_native]
version = "0.5"
diff --git a/glow/src/backend.rs b/glow/src/backend.rs
index 21af2ecf..1ba70a49 100644
--- a/glow/src/backend.rs
+++ b/glow/src/backend.rs
@@ -1,3 +1,5 @@
+#[cfg(any(feature = "image_rs", feature = "svg"))]
+use crate::image;
use crate::quad;
use crate::text;
use crate::{program, triangle};
@@ -15,6 +17,8 @@ use iced_native::{Font, Size};
/// [`iced`]: https://github.com/iced-rs/iced
#[derive(Debug)]
pub struct Backend {
+ #[cfg(any(feature = "image_rs", feature = "svg"))]
+ image_pipeline: image::Pipeline,
quad_pipeline: quad::Pipeline,
text_pipeline: text::Pipeline,
triangle_pipeline: triangle::Pipeline,
@@ -32,10 +36,14 @@ impl Backend {
let shader_version = program::Version::new(gl);
+ #[cfg(any(feature = "image_rs", feature = "svg"))]
+ let image_pipeline = image::Pipeline::new(gl, &shader_version);
let quad_pipeline = quad::Pipeline::new(gl, &shader_version);
let triangle_pipeline = triangle::Pipeline::new(gl, &shader_version);
Self {
+ #[cfg(any(feature = "image_rs", feature = "svg"))]
+ image_pipeline,
quad_pipeline,
text_pipeline,
triangle_pipeline,
@@ -70,6 +78,9 @@ impl Backend {
viewport_size.height,
);
}
+
+ #[cfg(any(feature = "image_rs", feature = "svg"))]
+ self.image_pipeline.trim_cache(gl);
}
fn flush(
@@ -112,6 +123,15 @@ impl Backend {
);
}
+ #[cfg(any(feature = "image_rs", feature = "svg"))]
+ if !layer.images.is_empty() {
+ let scaled = transformation
+ * Transformation::scale(scale_factor, scale_factor);
+
+ self.image_pipeline
+ .draw(gl, scaled, scale_factor, &layer.images);
+ }
+
if !layer.text.is_empty() {
for text in layer.text.iter() {
// Target physical coordinates directly to avoid blurry text
@@ -236,10 +256,10 @@ impl backend::Text for Backend {
}
}
-#[cfg(feature = "image")]
+#[cfg(feature = "image_rs")]
impl backend::Image for Backend {
- fn dimensions(&self, _handle: &iced_native::image::Handle) -> (u32, u32) {
- (50, 50)
+ fn dimensions(&self, handle: &iced_native::image::Handle) -> (u32, u32) {
+ self.image_pipeline.dimensions(handle)
}
}
diff --git a/glow/src/image.rs b/glow/src/image.rs
new file mode 100644
index 00000000..51e3016e
--- /dev/null
+++ b/glow/src/image.rs
@@ -0,0 +1,212 @@
+use crate::program::{self, Shader};
+use crate::Transformation;
+use glow::HasContext;
+use iced_graphics::layer;
+#[cfg(feature = "image_rs")]
+use std::cell::RefCell;
+
+pub use iced_graphics::triangle::{Mesh2D, Vertex2D};
+
+#[cfg(feature = "image_rs")]
+use iced_graphics::image::raster;
+
+#[cfg(feature = "svg")]
+use iced_graphics::image::vector;
+
+mod textures;
+use textures::{Entry, Textures};
+
+#[derive(Debug)]
+pub(crate) struct Pipeline {
+ program: <glow::Context as HasContext>::Program,
+ vertex_array: <glow::Context as HasContext>::VertexArray,
+ vertex_buffer: <glow::Context as HasContext>::Buffer,
+ transform_location: <glow::Context as HasContext>::UniformLocation,
+ textures: Textures,
+ #[cfg(feature = "image_rs")]
+ raster_cache: RefCell<raster::Cache<Textures>>,
+ #[cfg(feature = "svg")]
+ vector_cache: vector::Cache<Textures>,
+}
+
+impl Pipeline {
+ pub fn new(
+ gl: &glow::Context,
+ shader_version: &program::Version,
+ ) -> Pipeline {
+ let program = unsafe {
+ let vertex_shader = Shader::vertex(
+ gl,
+ shader_version,
+ include_str!("shader/common/image.vert"),
+ );
+ let fragment_shader = Shader::fragment(
+ gl,
+ shader_version,
+ include_str!("shader/common/image.frag"),
+ );
+
+ program::create(
+ gl,
+ &[vertex_shader, fragment_shader],
+ &[(0, "i_Position")],
+ )
+ };
+
+ let transform_location =
+ unsafe { gl.get_uniform_location(program, "u_Transform") }
+ .expect("Get transform location");
+
+ unsafe {
+ gl.use_program(Some(program));
+
+ let transform: [f32; 16] = Transformation::identity().into();
+ gl.uniform_matrix_4_f32_slice(
+ Some(&transform_location),
+ false,
+ &transform,
+ );
+
+ gl.use_program(None);
+ }
+
+ let vertex_buffer =
+ unsafe { gl.create_buffer().expect("Create vertex buffer") };
+ let vertex_array =
+ unsafe { gl.create_vertex_array().expect("Create vertex array") };
+
+ unsafe {
+ gl.bind_vertex_array(Some(vertex_array));
+ gl.bind_buffer(glow::ARRAY_BUFFER, Some(vertex_buffer));
+
+ let vertices = &[0u8, 0, 1, 0, 0, 1, 1, 1];
+ gl.buffer_data_size(
+ glow::ARRAY_BUFFER,
+ vertices.len() as i32,
+ glow::STATIC_DRAW,
+ );
+ gl.buffer_sub_data_u8_slice(
+ glow::ARRAY_BUFFER,
+ 0,
+ bytemuck::cast_slice(vertices),
+ );
+
+ gl.enable_vertex_attrib_array(0);
+ gl.vertex_attrib_pointer_f32(
+ 0,
+ 2,
+ glow::UNSIGNED_BYTE,
+ false,
+ 0,
+ 0,
+ );
+
+ gl.bind_buffer(glow::ARRAY_BUFFER, None);
+ gl.bind_vertex_array(None);
+ }
+
+ Pipeline {
+ program,
+ vertex_array,
+ vertex_buffer,
+ transform_location,
+ textures: Textures::new(),
+ #[cfg(feature = "image_rs")]
+ raster_cache: RefCell::new(raster::Cache::default()),
+ #[cfg(feature = "svg")]
+ vector_cache: vector::Cache::default(),
+ }
+ }
+
+ #[cfg(feature = "image_rs")]
+ pub fn dimensions(
+ &self,
+ handle: &iced_native::image::Handle,
+ ) -> (u32, u32) {
+ self.raster_cache.borrow_mut().load(handle).dimensions()
+ }
+
+ pub fn draw(
+ &mut self,
+ mut gl: &glow::Context,
+ transformation: Transformation,
+ _scale_factor: f32,
+ images: &[layer::Image],
+ ) {
+ unsafe {
+ gl.use_program(Some(self.program));
+ gl.bind_vertex_array(Some(self.vertex_array));
+ gl.bind_buffer(glow::ARRAY_BUFFER, Some(self.vertex_buffer));
+ }
+
+ #[cfg(feature = "image_rs")]
+ let mut raster_cache = self.raster_cache.borrow_mut();
+ for image in images {
+ let (entry, bounds) = match &image {
+ #[cfg(feature = "image_rs")]
+ layer::Image::Raster { handle, bounds } => (
+ raster_cache.upload(handle, &mut gl, &mut self.textures),
+ bounds,
+ ),
+ #[cfg(not(feature = "image_rs"))]
+ layer::Image::Raster { handle: _, bounds } => (None, bounds),
+
+ #[cfg(feature = "svg")]
+ layer::Image::Vector { handle, bounds } => {
+ let size = [bounds.width, bounds.height];
+ (
+ self.vector_cache.upload(
+ handle,
+ size,
+ _scale_factor,
+ &mut gl,
+ &mut self.textures,
+ ),
+ bounds,
+ )
+ }
+
+ #[cfg(not(feature = "svg"))]
+ layer::Image::Vector { handle: _, bounds } => (None, bounds),
+ };
+
+ unsafe {
+ if let Some(Entry { texture, .. }) = entry {
+ gl.bind_texture(glow::TEXTURE_2D, Some(*texture))
+ } else {
+ continue;
+ }
+
+ let translate = Transformation::translate(bounds.x, bounds.y);
+ let scale = Transformation::scale(bounds.width, bounds.height);
+ let transformation = transformation * translate * scale;
+ let matrix: [f32; 16] = transformation.into();
+ gl.uniform_matrix_4_f32_slice(
+ Some(&self.transform_location),
+ false,
+ &matrix,
+ );
+
+ gl.draw_arrays(glow::TRIANGLE_STRIP, 0, 4);
+
+ gl.bind_texture(glow::TEXTURE_2D, None);
+ }
+ }
+
+ unsafe {
+ gl.bind_buffer(glow::ARRAY_BUFFER, None);
+ gl.bind_vertex_array(None);
+ gl.use_program(None);
+ }
+ }
+
+ pub fn trim_cache(&mut self, mut gl: &glow::Context) {
+ #[cfg(feature = "image_rs")]
+ self.raster_cache
+ .borrow_mut()
+ .trim(&mut self.textures, &mut gl);
+
+ #[cfg(feature = "svg")]
+ self.vector_cache.trim(&mut self.textures, &mut gl);
+ }
+}
diff --git a/glow/src/image/textures.rs b/glow/src/image/textures.rs
new file mode 100644
index 00000000..f43cae1c
--- /dev/null
+++ b/glow/src/image/textures.rs
@@ -0,0 +1,82 @@
+use glow::HasContext;
+use iced_graphics::image::{TextureStore, TextureStoreEntry};
+
+#[derive(Debug)]
+pub struct Textures;
+
+impl Textures {
+ pub fn new() -> Self {
+ Self
+ }
+}
+
+impl TextureStore for Textures {
+ type Entry = Entry;
+ type State<'a> = &'a glow::Context;
+
+ fn upload(
+ &mut self,
+ width: u32,
+ height: u32,
+ data: &[u8],
+ gl: &mut &glow::Context,
+ ) -> Option<Self::Entry> {
+ unsafe {
+ let texture = gl.create_texture().expect("create texture");
+ gl.bind_texture(glow::TEXTURE_2D, Some(texture));
+ gl.tex_image_2d(
+ glow::TEXTURE_2D,
+ 0,
+ glow::SRGB8_ALPHA8 as i32,
+ width as i32,
+ height as i32,
+ 0,
+ glow::BGRA,
+ glow::UNSIGNED_BYTE,
+ Some(data),
+ );
+ gl.tex_parameter_i32(
+ glow::TEXTURE_2D,
+ glow::TEXTURE_WRAP_S,
+ glow::CLAMP_TO_EDGE as _,
+ );
+ gl.tex_parameter_i32(
+ glow::TEXTURE_2D,
+ glow::TEXTURE_WRAP_T,
+ glow::CLAMP_TO_EDGE as _,
+ );
+ gl.tex_parameter_i32(
+ glow::TEXTURE_2D,
+ glow::TEXTURE_MIN_FILTER,
+ glow::LINEAR as _,
+ );
+ gl.tex_parameter_i32(
+ glow::TEXTURE_2D,
+ glow::TEXTURE_MAG_FILTER,
+ glow::LINEAR as _,
+ );
+ gl.bind_texture(glow::TEXTURE_2D, None);
+
+ Some(Entry {
+ size: (width, height),
+ texture,
+ })
+ }
+ }
+
+ fn remove(&mut self, entry: &Entry, gl: &mut &glow::Context) {
+ unsafe { gl.delete_texture(entry.texture) }
+ }
+}
+
+#[derive(Debug)]
+pub struct Entry {
+ size: (u32, u32),
+ pub texture: glow::NativeTexture,
+}
+
+impl TextureStoreEntry for Entry {
+ fn size(&self) -> (u32, u32) {
+ self.size
+ }
+}
diff --git a/glow/src/lib.rs b/glow/src/lib.rs
index de9c0002..daeb3e32 100644
--- a/glow/src/lib.rs
+++ b/glow/src/lib.rs
@@ -24,6 +24,8 @@
pub use glow;
mod backend;
+#[cfg(any(feature = "image_rs", feature = "svg"))]
+mod image;
mod program;
mod quad;
mod text;
diff --git a/glow/src/shader/common/image.frag b/glow/src/shader/common/image.frag
new file mode 100644
index 00000000..5e05abdf
--- /dev/null
+++ b/glow/src/shader/common/image.frag
@@ -0,0 +1,22 @@
+#ifdef GL_ES
+#ifdef GL_FRAGMENT_PRECISION_HIGH
+precision highp float;
+#else
+precision mediump float;
+#endif
+#endif
+
+uniform sampler2D tex;
+in vec2 tex_pos;
+
+#ifdef HIGHER_THAN_300
+out vec4 fragColor;
+#define gl_FragColor fragColor
+#endif
+#ifdef GL_ES
+#define texture texture2D
+#endif
+
+void main() {
+ gl_FragColor = texture(tex, tex_pos);
+}
diff --git a/glow/src/shader/common/image.vert b/glow/src/shader/common/image.vert
new file mode 100644
index 00000000..93e541f2
--- /dev/null
+++ b/glow/src/shader/common/image.vert
@@ -0,0 +1,9 @@
+uniform mat4 u_Transform;
+
+in vec2 i_Position;
+out vec2 tex_pos;
+
+void main() {
+ gl_Position = u_Transform * vec4(i_Position, 0.0, 1.0);
+ tex_pos = i_Position;
+}