Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Glow image rendering support; move image/svg code to iced_graphics #1485

Merged
merged 8 commits into from
Nov 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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