Skip to content

Commit

Permalink
Add image/svg support to iced_glow
Browse files Browse the repository at this point in the history
#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.
  • Loading branch information
ids1024 committed Nov 4, 2022
1 parent 0acc3b0 commit 058cb58
Show file tree
Hide file tree
Showing 8 changed files with 366 additions and 8 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 16 additions & 3 deletions glow/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
Expand Down
26 changes: 23 additions & 3 deletions glow/src/backend.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#[cfg(any(feature = "image_rs", feature = "svg"))]
use crate::image;
use crate::quad;
use crate::text;
use crate::{program, triangle};
Expand All @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -70,6 +78,9 @@ impl Backend {
viewport_size.height,
);
}

#[cfg(any(feature = "image_rs", feature = "svg"))]
self.image_pipeline.trim_cache(gl);
}

fn flush(
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
}

Expand Down
210 changes: 210 additions & 0 deletions glow/src/image.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
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::new()),
#[cfg(feature = "svg")]
vector_cache: vector::Cache::new(),
}
}

#[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.trim(&mut self.textures, &mut gl);

#[cfg(feature = "svg")]
self.vector_cache.trim(&mut self.textures, &mut gl);
}
}
Loading

0 comments on commit 058cb58

Please sign in to comment.