Skip to content

Commit

Permalink
Merge pull request #1485 from ids1024/glow-image
Browse files Browse the repository at this point in the history
Glow image rendering support; move image/svg code to iced_graphics
  • Loading branch information
hecrj authored Nov 5, 2022
2 parents 7b12991 + 078cadf commit 370fa14
Show file tree
Hide file tree
Showing 29 changed files with 750 additions and 267 deletions.
6 changes: 3 additions & 3 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 Expand Up @@ -104,7 +104,7 @@ iced_glow = { version = "0.3", path = "glow", optional = true }
thiserror = "1.0"

[dependencies.image_rs]
version = "0.23"
version = "0.24"
package = "image"
optional = true

Expand Down
16 changes: 13 additions & 3 deletions glow/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,22 @@ license = "MIT AND OFL-1.1"
repository = "https://github.com/iced-rs/iced"

[features]
svg = ["iced_graphics/svg"]
image = ["iced_graphics/image"]
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 Down
30 changes: 25 additions & 5 deletions glow/src/backend.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#[cfg(any(feature = "image", 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", 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", 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", 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", feature = "svg"))]
self.image_pipeline.trim_cache(gl);
}

fn flush(
Expand Down Expand Up @@ -112,6 +123,15 @@ impl Backend {
);
}

#[cfg(any(feature = "image", 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 @@ -238,17 +258,17 @@ impl backend::Text for Backend {

#[cfg(feature = "image")]
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) -> Size<u32> {
self.image_pipeline.dimensions(handle)
}
}

#[cfg(feature = "svg")]
impl backend::Svg for Backend {
fn viewport_dimensions(
&self,
_handle: &iced_native::svg::Handle,
) -> (u32, u32) {
(50, 50)
handle: &iced_native::svg::Handle,
) -> Size<u32> {
self.image_pipeline.viewport_dimensions(handle)
}
}
230 changes: 230 additions & 0 deletions glow/src/image.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
mod storage;

use storage::Storage;

pub use iced_graphics::triangle::{Mesh2D, Vertex2D};

use crate::program::{self, Shader};
use crate::Transformation;

#[cfg(feature = "image")]
use iced_graphics::image::raster;

#[cfg(feature = "svg")]
use iced_graphics::image::vector;

use iced_graphics::layer;
use iced_graphics::Size;

use glow::HasContext;

use std::cell::RefCell;

#[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,
storage: Storage,
#[cfg(feature = "image")]
raster_cache: RefCell<raster::Cache<Storage>>,
#[cfg(feature = "svg")]
vector_cache: RefCell<vector::Cache<Storage>>,
}

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,
storage: Storage::default(),
#[cfg(feature = "image")]
raster_cache: RefCell::new(raster::Cache::default()),
#[cfg(feature = "svg")]
vector_cache: RefCell::new(vector::Cache::default()),
}
}

#[cfg(feature = "image")]
pub fn dimensions(&self, handle: &iced_native::image::Handle) -> Size<u32> {
self.raster_cache.borrow_mut().load(handle).dimensions()
}

#[cfg(feature = "svg")]
pub fn viewport_dimensions(
&self,
handle: &iced_native::svg::Handle,
) -> Size<u32> {
let mut cache = self.vector_cache.borrow_mut();
let svg = cache.load(handle);

svg.viewport_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")]
let mut raster_cache = self.raster_cache.borrow_mut();

#[cfg(feature = "svg")]
let mut vector_cache = self.vector_cache.borrow_mut();

for image in images {
let (entry, bounds) = match &image {
#[cfg(feature = "image")]
layer::Image::Raster { handle, bounds } => (
raster_cache.upload(handle, &mut gl, &mut self.storage),
bounds,
),
#[cfg(not(feature = "image"))]
layer::Image::Raster { handle: _, bounds } => (None, bounds),

#[cfg(feature = "svg")]
layer::Image::Vector { handle, bounds } => {
let size = [bounds.width, bounds.height];
(
vector_cache.upload(
handle,
size,
_scale_factor,
&mut gl,
&mut self.storage,
),
bounds,
)
}

#[cfg(not(feature = "svg"))]
layer::Image::Vector { handle: _, bounds } => (None, bounds),
};

unsafe {
if let Some(storage::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")]
self.raster_cache
.borrow_mut()
.trim(&mut self.storage, &mut gl);

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

0 comments on commit 370fa14

Please sign in to comment.