Implement image support for canvas widget
This commit is contained in:
parent
87a613edd1
commit
0ceee1cf3a
16 changed files with 485 additions and 29 deletions
|
|
@ -47,6 +47,62 @@ impl Rectangle<f32> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a new square [`Rectangle`] with the center at the origin and
|
||||||
|
/// with the given radius.
|
||||||
|
pub fn with_radius(radius: f32) -> Self {
|
||||||
|
Self {
|
||||||
|
x: -radius,
|
||||||
|
y: -radius,
|
||||||
|
width: radius * 2.0,
|
||||||
|
height: radius * 2.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new axis-aligned [`Rectangle`] from the given vertices; returning the
|
||||||
|
/// rotation in [`Radians`] that must be applied to the axis-aligned [`Rectangle`]
|
||||||
|
/// to obtain the desired result.
|
||||||
|
pub fn with_vertices(
|
||||||
|
top_left: Point,
|
||||||
|
top_right: Point,
|
||||||
|
bottom_left: Point,
|
||||||
|
) -> (Rectangle, Radians) {
|
||||||
|
let width = (top_right.x - top_left.x).hypot(top_right.y - top_left.y);
|
||||||
|
|
||||||
|
let height =
|
||||||
|
(bottom_left.x - top_left.x).hypot(bottom_left.y - top_left.y);
|
||||||
|
|
||||||
|
let rotation =
|
||||||
|
(top_right.y - top_left.y).atan2(top_right.x - top_left.x);
|
||||||
|
|
||||||
|
let rotation = if rotation < 0.0 {
|
||||||
|
2.0 * std::f32::consts::PI + rotation
|
||||||
|
} else {
|
||||||
|
rotation
|
||||||
|
};
|
||||||
|
|
||||||
|
let position = {
|
||||||
|
let center = Point::new(
|
||||||
|
(top_right.x + bottom_left.x) / 2.0,
|
||||||
|
(top_right.y + bottom_left.y) / 2.0,
|
||||||
|
);
|
||||||
|
|
||||||
|
let rotation = -rotation - std::f32::consts::PI * 2.0;
|
||||||
|
|
||||||
|
Point::new(
|
||||||
|
center.x + (top_left.x - center.x) * rotation.cos()
|
||||||
|
- (top_left.y - center.y) * rotation.sin(),
|
||||||
|
center.y
|
||||||
|
+ (top_left.x - center.x) * rotation.sin()
|
||||||
|
+ (top_left.y - center.y) * rotation.cos(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
(
|
||||||
|
Rectangle::new(position, Size::new(width, height)),
|
||||||
|
Radians(rotation),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the [`Point`] at the center of the [`Rectangle`].
|
/// Returns the [`Point`] at the center of the [`Rectangle`].
|
||||||
pub fn center(&self) -> Point {
|
pub fn center(&self) -> Point {
|
||||||
Point::new(self.center_x(), self.center_y())
|
Point::new(self.center_x(), self.center_y())
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ all-features = true
|
||||||
[features]
|
[features]
|
||||||
geometry = ["lyon_path"]
|
geometry = ["lyon_path"]
|
||||||
image = ["dep:image", "kamadak-exif"]
|
image = ["dep:image", "kamadak-exif"]
|
||||||
|
svg = []
|
||||||
web-colors = []
|
web-colors = []
|
||||||
fira-sans = []
|
fira-sans = []
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
//! Draw and generate geometry.
|
//! Draw and generate geometry.
|
||||||
use crate::core::{Point, Radians, Rectangle, Size, Vector};
|
use crate::core::image;
|
||||||
|
use crate::core::svg;
|
||||||
|
use crate::core::{Color, Point, Radians, Rectangle, Size, Vector};
|
||||||
use crate::geometry::{self, Fill, Path, Stroke, Text};
|
use crate::geometry::{self, Fill, Path, Stroke, Text};
|
||||||
|
|
||||||
/// The region of a surface that can be used to draw geometry.
|
/// The region of a surface that can be used to draw geometry.
|
||||||
|
|
@ -75,6 +77,25 @@ where
|
||||||
self.raw.fill_text(text);
|
self.raw.fill_text(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Draws the given image on the [`Frame`] inside the given bounds.
|
||||||
|
#[cfg(feature = "image")]
|
||||||
|
pub fn draw_image(
|
||||||
|
&mut self,
|
||||||
|
handle: &image::Handle,
|
||||||
|
bounds: Rectangle,
|
||||||
|
filter_method: image::FilterMethod,
|
||||||
|
rotation: impl Into<Radians>,
|
||||||
|
opacity: f32,
|
||||||
|
) {
|
||||||
|
self.raw.draw_image(
|
||||||
|
handle,
|
||||||
|
bounds,
|
||||||
|
filter_method,
|
||||||
|
rotation.into(),
|
||||||
|
opacity,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Stores the current transform of the [`Frame`] and executes the given
|
/// Stores the current transform of the [`Frame`] and executes the given
|
||||||
/// drawing operations, restoring the transform afterwards.
|
/// drawing operations, restoring the transform afterwards.
|
||||||
///
|
///
|
||||||
|
|
@ -116,8 +137,7 @@ where
|
||||||
let mut frame = self.draft(region);
|
let mut frame = self.draft(region);
|
||||||
|
|
||||||
let result = f(&mut frame);
|
let result = f(&mut frame);
|
||||||
|
self.paste(frame);
|
||||||
self.paste(frame, Point::new(region.x, region.y));
|
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
@ -134,8 +154,8 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draws the contents of the given [`Frame`] with origin at the given [`Point`].
|
/// Draws the contents of the given [`Frame`] with origin at the given [`Point`].
|
||||||
fn paste(&mut self, frame: Self, at: Point) {
|
fn paste(&mut self, frame: Self) {
|
||||||
self.raw.paste(frame.raw, at);
|
self.raw.paste(frame.raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Applies a translation to the current transform of the [`Frame`].
|
/// Applies a translation to the current transform of the [`Frame`].
|
||||||
|
|
@ -186,7 +206,7 @@ pub trait Backend: Sized {
|
||||||
fn scale_nonuniform(&mut self, scale: impl Into<Vector>);
|
fn scale_nonuniform(&mut self, scale: impl Into<Vector>);
|
||||||
|
|
||||||
fn draft(&mut self, clip_bounds: Rectangle) -> Self;
|
fn draft(&mut self, clip_bounds: Rectangle) -> Self;
|
||||||
fn paste(&mut self, frame: Self, at: Point);
|
fn paste(&mut self, frame: Self);
|
||||||
|
|
||||||
fn stroke<'a>(&mut self, path: &Path, stroke: impl Into<Stroke<'a>>);
|
fn stroke<'a>(&mut self, path: &Path, stroke: impl Into<Stroke<'a>>);
|
||||||
|
|
||||||
|
|
@ -199,6 +219,24 @@ pub trait Backend: Sized {
|
||||||
fill: impl Into<Fill>,
|
fill: impl Into<Fill>,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
fn draw_image(
|
||||||
|
&mut self,
|
||||||
|
handle: &image::Handle,
|
||||||
|
bounds: Rectangle,
|
||||||
|
filter_method: image::FilterMethod,
|
||||||
|
rotation: Radians,
|
||||||
|
opacity: f32,
|
||||||
|
);
|
||||||
|
|
||||||
|
fn draw_svg(
|
||||||
|
&mut self,
|
||||||
|
handle: &svg::Handle,
|
||||||
|
bounds: Rectangle,
|
||||||
|
color: Option<Color>,
|
||||||
|
rotation: Radians,
|
||||||
|
opacity: f32,
|
||||||
|
);
|
||||||
|
|
||||||
fn into_geometry(self) -> Self::Geometry;
|
fn into_geometry(self) -> Self::Geometry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -231,7 +269,7 @@ impl Backend for () {
|
||||||
fn scale_nonuniform(&mut self, _scale: impl Into<Vector>) {}
|
fn scale_nonuniform(&mut self, _scale: impl Into<Vector>) {}
|
||||||
|
|
||||||
fn draft(&mut self, _clip_bounds: Rectangle) -> Self {}
|
fn draft(&mut self, _clip_bounds: Rectangle) -> Self {}
|
||||||
fn paste(&mut self, _frame: Self, _at: Point) {}
|
fn paste(&mut self, _frame: Self) {}
|
||||||
|
|
||||||
fn stroke<'a>(&mut self, _path: &Path, _stroke: impl Into<Stroke<'a>>) {}
|
fn stroke<'a>(&mut self, _path: &Path, _stroke: impl Into<Stroke<'a>>) {}
|
||||||
|
|
||||||
|
|
@ -246,4 +284,24 @@ impl Backend for () {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn into_geometry(self) -> Self::Geometry {}
|
fn into_geometry(self) -> Self::Geometry {}
|
||||||
|
|
||||||
|
fn draw_image(
|
||||||
|
&mut self,
|
||||||
|
_handle: &image::Handle,
|
||||||
|
_bounds: Rectangle,
|
||||||
|
_filter_method: image::FilterMethod,
|
||||||
|
_rotation: Radians,
|
||||||
|
_opacity: f32,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_svg(
|
||||||
|
&mut self,
|
||||||
|
_handle: &svg::Handle,
|
||||||
|
_bounds: Rectangle,
|
||||||
|
_color: Option<Color>,
|
||||||
|
_rotation: Radians,
|
||||||
|
_opacity: f32,
|
||||||
|
) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,12 @@ pub enum Image {
|
||||||
|
|
||||||
/// The opacity of the image.
|
/// The opacity of the image.
|
||||||
opacity: f32,
|
opacity: f32,
|
||||||
|
|
||||||
|
/// If set to `true`, the image will be snapped to the pixel grid.
|
||||||
|
///
|
||||||
|
/// This can avoid graphical glitches, specially when using a
|
||||||
|
/// [`image::FilterMethod::Nearest`].
|
||||||
|
snap: bool,
|
||||||
},
|
},
|
||||||
/// A vector image.
|
/// A vector image.
|
||||||
Vector {
|
Vector {
|
||||||
|
|
|
||||||
|
|
@ -572,6 +572,42 @@ mod geometry {
|
||||||
delegate!(self, frame, frame.fill_text(text));
|
delegate!(self, frame, frame.fill_text(text));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn draw_image(
|
||||||
|
&mut self,
|
||||||
|
handle: &iced_wgpu::core::image::Handle,
|
||||||
|
bounds: Rectangle,
|
||||||
|
filter_method: iced_wgpu::core::image::FilterMethod,
|
||||||
|
rotation: Radians,
|
||||||
|
opacity: f32,
|
||||||
|
) {
|
||||||
|
delegate!(
|
||||||
|
self,
|
||||||
|
frame,
|
||||||
|
frame.draw_image(
|
||||||
|
handle,
|
||||||
|
bounds,
|
||||||
|
filter_method,
|
||||||
|
rotation,
|
||||||
|
opacity
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_svg(
|
||||||
|
&mut self,
|
||||||
|
handle: &iced_wgpu::core::svg::Handle,
|
||||||
|
bounds: Rectangle,
|
||||||
|
color: Option<iced_wgpu::core::Color>,
|
||||||
|
rotation: Radians,
|
||||||
|
opacity: f32,
|
||||||
|
) {
|
||||||
|
delegate!(
|
||||||
|
self,
|
||||||
|
frame,
|
||||||
|
frame.draw_svg(handle, bounds, color, rotation, opacity)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fn push_transform(&mut self) {
|
fn push_transform(&mut self) {
|
||||||
delegate!(self, frame, frame.push_transform());
|
delegate!(self, frame, frame.push_transform());
|
||||||
}
|
}
|
||||||
|
|
@ -587,13 +623,13 @@ mod geometry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paste(&mut self, frame: Self, at: Point) {
|
fn paste(&mut self, frame: Self) {
|
||||||
match (self, frame) {
|
match (self, frame) {
|
||||||
(Self::Primary(target), Self::Primary(source)) => {
|
(Self::Primary(target), Self::Primary(source)) => {
|
||||||
target.paste(source, at);
|
target.paste(source);
|
||||||
}
|
}
|
||||||
(Self::Secondary(target), Self::Secondary(source)) => {
|
(Self::Secondary(target), Self::Secondary(source)) => {
|
||||||
target.paste(source, at);
|
target.paste(source);
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ workspace = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
image = ["iced_graphics/image"]
|
image = ["iced_graphics/image"]
|
||||||
svg = ["resvg"]
|
svg = ["iced_graphics/svg", "resvg"]
|
||||||
geometry = ["iced_graphics/geometry"]
|
geometry = ["iced_graphics/geometry"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
|
||||||
|
|
@ -556,6 +556,7 @@ impl Engine {
|
||||||
bounds,
|
bounds,
|
||||||
rotation,
|
rotation,
|
||||||
opacity,
|
opacity,
|
||||||
|
snap: _,
|
||||||
} => {
|
} => {
|
||||||
let physical_bounds = *bounds * _transformation;
|
let physical_bounds = *bounds * _transformation;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
|
use crate::core::image;
|
||||||
|
use crate::core::svg;
|
||||||
use crate::core::text::LineHeight;
|
use crate::core::text::LineHeight;
|
||||||
use crate::core::{Pixels, Point, Radians, Rectangle, Size, Vector};
|
use crate::core::{Color, Pixels, Point, Radians, Rectangle, Size, Vector};
|
||||||
use crate::graphics::cache::{self, Cached};
|
use crate::graphics::cache::{self, Cached};
|
||||||
use crate::graphics::geometry::fill::{self, Fill};
|
use crate::graphics::geometry::fill::{self, Fill};
|
||||||
use crate::graphics::geometry::stroke::{self, Stroke};
|
use crate::graphics::geometry::stroke::{self, Stroke};
|
||||||
use crate::graphics::geometry::{self, Path, Style};
|
use crate::graphics::geometry::{self, Path, Style};
|
||||||
use crate::graphics::{Gradient, Text};
|
use crate::graphics::{Gradient, Image, Text};
|
||||||
use crate::Primitive;
|
use crate::Primitive;
|
||||||
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
@ -13,6 +15,7 @@ use std::rc::Rc;
|
||||||
pub enum Geometry {
|
pub enum Geometry {
|
||||||
Live {
|
Live {
|
||||||
text: Vec<Text>,
|
text: Vec<Text>,
|
||||||
|
images: Vec<Image>,
|
||||||
primitives: Vec<Primitive>,
|
primitives: Vec<Primitive>,
|
||||||
clip_bounds: Rectangle,
|
clip_bounds: Rectangle,
|
||||||
},
|
},
|
||||||
|
|
@ -22,6 +25,7 @@ pub enum Geometry {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Cache {
|
pub struct Cache {
|
||||||
pub text: Rc<[Text]>,
|
pub text: Rc<[Text]>,
|
||||||
|
pub images: Rc<[Image]>,
|
||||||
pub primitives: Rc<[Primitive]>,
|
pub primitives: Rc<[Primitive]>,
|
||||||
pub clip_bounds: Rectangle,
|
pub clip_bounds: Rectangle,
|
||||||
}
|
}
|
||||||
|
|
@ -37,10 +41,12 @@ impl Cached for Geometry {
|
||||||
match self {
|
match self {
|
||||||
Self::Live {
|
Self::Live {
|
||||||
primitives,
|
primitives,
|
||||||
|
images,
|
||||||
text,
|
text,
|
||||||
clip_bounds,
|
clip_bounds,
|
||||||
} => Cache {
|
} => Cache {
|
||||||
primitives: Rc::from(primitives),
|
primitives: Rc::from(primitives),
|
||||||
|
images: Rc::from(images),
|
||||||
text: Rc::from(text),
|
text: Rc::from(text),
|
||||||
clip_bounds,
|
clip_bounds,
|
||||||
},
|
},
|
||||||
|
|
@ -55,6 +61,7 @@ pub struct Frame {
|
||||||
transform: tiny_skia::Transform,
|
transform: tiny_skia::Transform,
|
||||||
stack: Vec<tiny_skia::Transform>,
|
stack: Vec<tiny_skia::Transform>,
|
||||||
primitives: Vec<Primitive>,
|
primitives: Vec<Primitive>,
|
||||||
|
images: Vec<Image>,
|
||||||
text: Vec<Text>,
|
text: Vec<Text>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -68,6 +75,7 @@ impl Frame {
|
||||||
clip_bounds,
|
clip_bounds,
|
||||||
stack: Vec::new(),
|
stack: Vec::new(),
|
||||||
primitives: Vec::new(),
|
primitives: Vec::new(),
|
||||||
|
images: Vec::new(),
|
||||||
text: Vec::new(),
|
text: Vec::new(),
|
||||||
transform: tiny_skia::Transform::from_translate(
|
transform: tiny_skia::Transform::from_translate(
|
||||||
clip_bounds.x,
|
clip_bounds.x,
|
||||||
|
|
@ -238,7 +246,7 @@ impl geometry::frame::Backend for Frame {
|
||||||
Self::with_clip(clip_bounds)
|
Self::with_clip(clip_bounds)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paste(&mut self, frame: Self, _at: Point) {
|
fn paste(&mut self, frame: Self) {
|
||||||
self.primitives.extend(frame.primitives);
|
self.primitives.extend(frame.primitives);
|
||||||
self.text.extend(frame.text);
|
self.text.extend(frame.text);
|
||||||
}
|
}
|
||||||
|
|
@ -269,10 +277,82 @@ impl geometry::frame::Backend for Frame {
|
||||||
fn into_geometry(self) -> Geometry {
|
fn into_geometry(self) -> Geometry {
|
||||||
Geometry::Live {
|
Geometry::Live {
|
||||||
primitives: self.primitives,
|
primitives: self.primitives,
|
||||||
|
images: self.images,
|
||||||
text: self.text,
|
text: self.text,
|
||||||
clip_bounds: self.clip_bounds,
|
clip_bounds: self.clip_bounds,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn draw_image(
|
||||||
|
&mut self,
|
||||||
|
handle: &image::Handle,
|
||||||
|
bounds: Rectangle,
|
||||||
|
filter_method: image::FilterMethod,
|
||||||
|
rotation: Radians,
|
||||||
|
opacity: f32,
|
||||||
|
) {
|
||||||
|
let (bounds, external_rotation) =
|
||||||
|
transform_rectangle(bounds, self.transform);
|
||||||
|
|
||||||
|
self.images.push(Image::Raster {
|
||||||
|
handle: handle.clone(),
|
||||||
|
filter_method,
|
||||||
|
bounds,
|
||||||
|
rotation: rotation + external_rotation,
|
||||||
|
opacity,
|
||||||
|
snap: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_svg(
|
||||||
|
&mut self,
|
||||||
|
handle: &svg::Handle,
|
||||||
|
bounds: Rectangle,
|
||||||
|
color: Option<Color>,
|
||||||
|
rotation: Radians,
|
||||||
|
opacity: f32,
|
||||||
|
) {
|
||||||
|
let (bounds, external_rotation) =
|
||||||
|
transform_rectangle(bounds, self.transform);
|
||||||
|
|
||||||
|
self.images.push(Image::Vector {
|
||||||
|
handle: handle.clone(),
|
||||||
|
bounds,
|
||||||
|
color,
|
||||||
|
rotation: rotation + external_rotation,
|
||||||
|
opacity,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transform_rectangle(
|
||||||
|
rectangle: Rectangle,
|
||||||
|
transform: tiny_skia::Transform,
|
||||||
|
) -> (Rectangle, Radians) {
|
||||||
|
let mut top_left = tiny_skia::Point {
|
||||||
|
x: rectangle.x,
|
||||||
|
y: rectangle.y,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut top_right = tiny_skia::Point {
|
||||||
|
x: rectangle.x + rectangle.width,
|
||||||
|
y: rectangle.y,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut bottom_left = tiny_skia::Point {
|
||||||
|
x: rectangle.x,
|
||||||
|
y: rectangle.y + rectangle.height,
|
||||||
|
};
|
||||||
|
|
||||||
|
transform.map_point(&mut top_left);
|
||||||
|
transform.map_point(&mut top_right);
|
||||||
|
transform.map_point(&mut bottom_left);
|
||||||
|
|
||||||
|
Rectangle::with_vertices(
|
||||||
|
Point::new(top_left.x, top_left.y),
|
||||||
|
Point::new(top_right.x, top_right.y),
|
||||||
|
Point::new(bottom_left.x, bottom_left.y),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_path(path: &Path) -> Option<tiny_skia::Path> {
|
fn convert_path(path: &Path) -> Option<tiny_skia::Path> {
|
||||||
|
|
|
||||||
|
|
@ -116,6 +116,48 @@ impl Layer {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_image(
|
pub fn draw_image(
|
||||||
|
&mut self,
|
||||||
|
image: &Image,
|
||||||
|
transformation: Transformation,
|
||||||
|
) {
|
||||||
|
match image {
|
||||||
|
Image::Raster {
|
||||||
|
handle,
|
||||||
|
filter_method,
|
||||||
|
bounds,
|
||||||
|
rotation,
|
||||||
|
opacity,
|
||||||
|
snap: _,
|
||||||
|
} => {
|
||||||
|
self.draw_raster(
|
||||||
|
handle.clone(),
|
||||||
|
*filter_method,
|
||||||
|
*bounds,
|
||||||
|
transformation,
|
||||||
|
*rotation,
|
||||||
|
*opacity,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Image::Vector {
|
||||||
|
handle,
|
||||||
|
color,
|
||||||
|
bounds,
|
||||||
|
rotation,
|
||||||
|
opacity,
|
||||||
|
} => {
|
||||||
|
self.draw_svg(
|
||||||
|
handle.clone(),
|
||||||
|
*color,
|
||||||
|
*bounds,
|
||||||
|
transformation,
|
||||||
|
*rotation,
|
||||||
|
*opacity,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw_raster(
|
||||||
&mut self,
|
&mut self,
|
||||||
handle: image::Handle,
|
handle: image::Handle,
|
||||||
filter_method: image::FilterMethod,
|
filter_method: image::FilterMethod,
|
||||||
|
|
@ -130,6 +172,7 @@ impl Layer {
|
||||||
bounds: bounds * transformation,
|
bounds: bounds * transformation,
|
||||||
rotation,
|
rotation,
|
||||||
opacity,
|
opacity,
|
||||||
|
snap: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.images.push(image);
|
self.images.push(image);
|
||||||
|
|
|
||||||
|
|
@ -330,6 +330,7 @@ impl graphics::geometry::Renderer for Renderer {
|
||||||
match geometry {
|
match geometry {
|
||||||
Geometry::Live {
|
Geometry::Live {
|
||||||
primitives,
|
primitives,
|
||||||
|
images,
|
||||||
text,
|
text,
|
||||||
clip_bounds,
|
clip_bounds,
|
||||||
} => {
|
} => {
|
||||||
|
|
@ -339,6 +340,10 @@ impl graphics::geometry::Renderer for Renderer {
|
||||||
transformation,
|
transformation,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
for image in images {
|
||||||
|
layer.draw_image(&image, transformation);
|
||||||
|
}
|
||||||
|
|
||||||
layer.draw_text_group(text, clip_bounds, transformation);
|
layer.draw_text_group(text, clip_bounds, transformation);
|
||||||
}
|
}
|
||||||
Geometry::Cache(cache) => {
|
Geometry::Cache(cache) => {
|
||||||
|
|
@ -348,6 +353,10 @@ impl graphics::geometry::Renderer for Renderer {
|
||||||
transformation,
|
transformation,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
for image in cache.images.iter() {
|
||||||
|
layer.draw_image(image, transformation);
|
||||||
|
}
|
||||||
|
|
||||||
layer.draw_text_cache(
|
layer.draw_text_cache(
|
||||||
cache.text,
|
cache.text,
|
||||||
cache.clip_bounds,
|
cache.clip_bounds,
|
||||||
|
|
@ -381,7 +390,7 @@ impl core::image::Renderer for Renderer {
|
||||||
opacity: f32,
|
opacity: f32,
|
||||||
) {
|
) {
|
||||||
let (layer, transformation) = self.layers.current_mut();
|
let (layer, transformation) = self.layers.current_mut();
|
||||||
layer.draw_image(
|
layer.draw_raster(
|
||||||
handle,
|
handle,
|
||||||
filter_method,
|
filter_method,
|
||||||
bounds,
|
bounds,
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ all-features = true
|
||||||
[features]
|
[features]
|
||||||
geometry = ["iced_graphics/geometry", "lyon"]
|
geometry = ["iced_graphics/geometry", "lyon"]
|
||||||
image = ["iced_graphics/image"]
|
image = ["iced_graphics/image"]
|
||||||
svg = ["resvg/text"]
|
svg = ["iced_graphics/svg", "resvg/text"]
|
||||||
web-colors = ["iced_graphics/web-colors"]
|
web-colors = ["iced_graphics/web-colors"]
|
||||||
webgl = ["wgpu/webgl"]
|
webgl = ["wgpu/webgl"]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
//! Build and draw geometry.
|
//! Build and draw geometry.
|
||||||
|
use crate::core::image;
|
||||||
|
use crate::core::svg;
|
||||||
use crate::core::text::LineHeight;
|
use crate::core::text::LineHeight;
|
||||||
use crate::core::{
|
use crate::core::{
|
||||||
Pixels, Point, Radians, Rectangle, Size, Transformation, Vector,
|
Color, Pixels, Point, Radians, Rectangle, Size, Transformation, Vector,
|
||||||
};
|
};
|
||||||
use crate::graphics::cache::{self, Cached};
|
use crate::graphics::cache::{self, Cached};
|
||||||
use crate::graphics::color;
|
use crate::graphics::color;
|
||||||
|
|
@ -11,7 +13,7 @@ use crate::graphics::geometry::{
|
||||||
};
|
};
|
||||||
use crate::graphics::gradient::{self, Gradient};
|
use crate::graphics::gradient::{self, Gradient};
|
||||||
use crate::graphics::mesh::{self, Mesh};
|
use crate::graphics::mesh::{self, Mesh};
|
||||||
use crate::graphics::{self, Text};
|
use crate::graphics::{self, Image, Text};
|
||||||
use crate::text;
|
use crate::text;
|
||||||
use crate::triangle;
|
use crate::triangle;
|
||||||
|
|
||||||
|
|
@ -19,16 +21,22 @@ use lyon::geom::euclid;
|
||||||
use lyon::tessellation;
|
use lyon::tessellation;
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Geometry {
|
pub enum Geometry {
|
||||||
Live { meshes: Vec<Mesh>, text: Vec<Text> },
|
Live {
|
||||||
|
meshes: Vec<Mesh>,
|
||||||
|
images: Vec<Image>,
|
||||||
|
text: Vec<Text>,
|
||||||
|
},
|
||||||
Cached(Cache),
|
Cached(Cache),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Cache {
|
pub struct Cache {
|
||||||
pub meshes: Option<triangle::Cache>,
|
pub meshes: Option<triangle::Cache>,
|
||||||
|
pub images: Option<Arc<[Image]>>,
|
||||||
pub text: Option<text::Cache>,
|
pub text: Option<text::Cache>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -45,7 +53,17 @@ impl Cached for Geometry {
|
||||||
previous: Option<Self::Cache>,
|
previous: Option<Self::Cache>,
|
||||||
) -> Self::Cache {
|
) -> Self::Cache {
|
||||||
match self {
|
match self {
|
||||||
Self::Live { meshes, text } => {
|
Self::Live {
|
||||||
|
meshes,
|
||||||
|
images,
|
||||||
|
text,
|
||||||
|
} => {
|
||||||
|
let images = if images.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(Arc::from(images))
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(mut previous) = previous {
|
if let Some(mut previous) = previous {
|
||||||
if let Some(cache) = &mut previous.meshes {
|
if let Some(cache) = &mut previous.meshes {
|
||||||
cache.update(meshes);
|
cache.update(meshes);
|
||||||
|
|
@ -59,10 +77,13 @@ impl Cached for Geometry {
|
||||||
previous.text = text::Cache::new(group, text);
|
previous.text = text::Cache::new(group, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
previous.images = images;
|
||||||
|
|
||||||
previous
|
previous
|
||||||
} else {
|
} else {
|
||||||
Cache {
|
Cache {
|
||||||
meshes: triangle::Cache::new(meshes),
|
meshes: triangle::Cache::new(meshes),
|
||||||
|
images,
|
||||||
text: text::Cache::new(group, text),
|
text: text::Cache::new(group, text),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -78,6 +99,7 @@ pub struct Frame {
|
||||||
clip_bounds: Rectangle,
|
clip_bounds: Rectangle,
|
||||||
buffers: BufferStack,
|
buffers: BufferStack,
|
||||||
meshes: Vec<Mesh>,
|
meshes: Vec<Mesh>,
|
||||||
|
images: Vec<Image>,
|
||||||
text: Vec<Text>,
|
text: Vec<Text>,
|
||||||
transforms: Transforms,
|
transforms: Transforms,
|
||||||
fill_tessellator: tessellation::FillTessellator,
|
fill_tessellator: tessellation::FillTessellator,
|
||||||
|
|
@ -96,6 +118,7 @@ impl Frame {
|
||||||
clip_bounds: bounds,
|
clip_bounds: bounds,
|
||||||
buffers: BufferStack::new(),
|
buffers: BufferStack::new(),
|
||||||
meshes: Vec::new(),
|
meshes: Vec::new(),
|
||||||
|
images: Vec::new(),
|
||||||
text: Vec::new(),
|
text: Vec::new(),
|
||||||
transforms: Transforms {
|
transforms: Transforms {
|
||||||
previous: Vec::new(),
|
previous: Vec::new(),
|
||||||
|
|
@ -335,10 +358,11 @@ impl geometry::frame::Backend for Frame {
|
||||||
Frame::with_clip(clip_bounds)
|
Frame::with_clip(clip_bounds)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paste(&mut self, frame: Frame, _at: Point) {
|
fn paste(&mut self, frame: Frame) {
|
||||||
self.meshes
|
self.meshes
|
||||||
.extend(frame.buffers.into_meshes(frame.clip_bounds));
|
.extend(frame.buffers.into_meshes(frame.clip_bounds));
|
||||||
|
|
||||||
|
self.images.extend(frame.images);
|
||||||
self.text.extend(frame.text);
|
self.text.extend(frame.text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -348,9 +372,51 @@ impl geometry::frame::Backend for Frame {
|
||||||
|
|
||||||
Geometry::Live {
|
Geometry::Live {
|
||||||
meshes: self.meshes,
|
meshes: self.meshes,
|
||||||
|
images: self.images,
|
||||||
text: self.text,
|
text: self.text,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn draw_image(
|
||||||
|
&mut self,
|
||||||
|
handle: &image::Handle,
|
||||||
|
bounds: Rectangle,
|
||||||
|
filter_method: image::FilterMethod,
|
||||||
|
rotation: Radians,
|
||||||
|
opacity: f32,
|
||||||
|
) {
|
||||||
|
let (bounds, external_rotation) =
|
||||||
|
self.transforms.current.transform_rectangle(bounds);
|
||||||
|
|
||||||
|
self.images.push(Image::Raster {
|
||||||
|
handle: handle.clone(),
|
||||||
|
filter_method,
|
||||||
|
bounds,
|
||||||
|
rotation: rotation + external_rotation,
|
||||||
|
opacity,
|
||||||
|
snap: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_svg(
|
||||||
|
&mut self,
|
||||||
|
handle: &svg::Handle,
|
||||||
|
bounds: Rectangle,
|
||||||
|
color: Option<Color>,
|
||||||
|
rotation: Radians,
|
||||||
|
opacity: f32,
|
||||||
|
) {
|
||||||
|
let (bounds, external_rotation) =
|
||||||
|
self.transforms.current.transform_rectangle(bounds);
|
||||||
|
|
||||||
|
self.images.push(Image::Vector {
|
||||||
|
handle: handle.clone(),
|
||||||
|
color,
|
||||||
|
bounds,
|
||||||
|
rotation: rotation + external_rotation,
|
||||||
|
opacity,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Buffer {
|
enum Buffer {
|
||||||
|
|
@ -518,6 +584,21 @@ impl Transform {
|
||||||
|
|
||||||
gradient
|
gradient
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn transform_rectangle(
|
||||||
|
&self,
|
||||||
|
rectangle: Rectangle,
|
||||||
|
) -> (Rectangle, Radians) {
|
||||||
|
let top_left = self.transform_point(rectangle.position());
|
||||||
|
let top_right = self.transform_point(
|
||||||
|
rectangle.position() + Vector::new(rectangle.width, 0.0),
|
||||||
|
);
|
||||||
|
let bottom_left = self.transform_point(
|
||||||
|
rectangle.position() + Vector::new(0.0, rectangle.height),
|
||||||
|
);
|
||||||
|
|
||||||
|
Rectangle::with_vertices(top_left, top_right, bottom_left)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
struct GradientVertex2DBuilder {
|
struct GradientVertex2DBuilder {
|
||||||
gradient: gradient::Packed,
|
gradient: gradient::Packed,
|
||||||
|
|
|
||||||
|
|
@ -149,6 +149,8 @@ impl Pipeline {
|
||||||
6 => Float32x2,
|
6 => Float32x2,
|
||||||
// Layer
|
// Layer
|
||||||
7 => Sint32,
|
7 => Sint32,
|
||||||
|
// Snap
|
||||||
|
8 => Uint32,
|
||||||
),
|
),
|
||||||
}],
|
}],
|
||||||
},
|
},
|
||||||
|
|
@ -212,8 +214,6 @@ impl Pipeline {
|
||||||
transformation: Transformation,
|
transformation: Transformation,
|
||||||
scale: f32,
|
scale: f32,
|
||||||
) {
|
) {
|
||||||
let transformation = transformation * Transformation::scale(scale);
|
|
||||||
|
|
||||||
let nearest_instances: &mut Vec<Instance> = &mut Vec::new();
|
let nearest_instances: &mut Vec<Instance> = &mut Vec::new();
|
||||||
let linear_instances: &mut Vec<Instance> = &mut Vec::new();
|
let linear_instances: &mut Vec<Instance> = &mut Vec::new();
|
||||||
|
|
||||||
|
|
@ -226,6 +226,7 @@ impl Pipeline {
|
||||||
bounds,
|
bounds,
|
||||||
rotation,
|
rotation,
|
||||||
opacity,
|
opacity,
|
||||||
|
snap,
|
||||||
} => {
|
} => {
|
||||||
if let Some(atlas_entry) =
|
if let Some(atlas_entry) =
|
||||||
cache.upload_raster(device, encoder, handle)
|
cache.upload_raster(device, encoder, handle)
|
||||||
|
|
@ -235,6 +236,7 @@ impl Pipeline {
|
||||||
[bounds.width, bounds.height],
|
[bounds.width, bounds.height],
|
||||||
f32::from(*rotation),
|
f32::from(*rotation),
|
||||||
*opacity,
|
*opacity,
|
||||||
|
*snap,
|
||||||
atlas_entry,
|
atlas_entry,
|
||||||
match filter_method {
|
match filter_method {
|
||||||
crate::core::image::FilterMethod::Nearest => {
|
crate::core::image::FilterMethod::Nearest => {
|
||||||
|
|
@ -268,6 +270,7 @@ impl Pipeline {
|
||||||
size,
|
size,
|
||||||
f32::from(*rotation),
|
f32::from(*rotation),
|
||||||
*opacity,
|
*opacity,
|
||||||
|
true,
|
||||||
atlas_entry,
|
atlas_entry,
|
||||||
nearest_instances,
|
nearest_instances,
|
||||||
);
|
);
|
||||||
|
|
@ -300,6 +303,7 @@ impl Pipeline {
|
||||||
nearest_instances,
|
nearest_instances,
|
||||||
linear_instances,
|
linear_instances,
|
||||||
transformation,
|
transformation,
|
||||||
|
scale,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.prepare_layer += 1;
|
self.prepare_layer += 1;
|
||||||
|
|
@ -375,9 +379,12 @@ impl Layer {
|
||||||
nearest_instances: &[Instance],
|
nearest_instances: &[Instance],
|
||||||
linear_instances: &[Instance],
|
linear_instances: &[Instance],
|
||||||
transformation: Transformation,
|
transformation: Transformation,
|
||||||
|
scale_factor: f32,
|
||||||
) {
|
) {
|
||||||
let uniforms = Uniforms {
|
let uniforms = Uniforms {
|
||||||
transform: transformation.into(),
|
transform: transformation.into(),
|
||||||
|
scale_factor,
|
||||||
|
_padding: [0.0; 3],
|
||||||
};
|
};
|
||||||
|
|
||||||
let bytes = bytemuck::bytes_of(&uniforms);
|
let bytes = bytemuck::bytes_of(&uniforms);
|
||||||
|
|
@ -492,6 +499,7 @@ struct Instance {
|
||||||
_position_in_atlas: [f32; 2],
|
_position_in_atlas: [f32; 2],
|
||||||
_size_in_atlas: [f32; 2],
|
_size_in_atlas: [f32; 2],
|
||||||
_layer: u32,
|
_layer: u32,
|
||||||
|
_snap: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Instance {
|
impl Instance {
|
||||||
|
|
@ -502,6 +510,10 @@ impl Instance {
|
||||||
#[derive(Debug, Clone, Copy, Zeroable, Pod)]
|
#[derive(Debug, Clone, Copy, Zeroable, Pod)]
|
||||||
struct Uniforms {
|
struct Uniforms {
|
||||||
transform: [f32; 16],
|
transform: [f32; 16],
|
||||||
|
scale_factor: f32,
|
||||||
|
// Uniforms must be aligned to their largest member,
|
||||||
|
// this uses a mat4x4<f32> which aligns to 16, so align to that
|
||||||
|
_padding: [f32; 3],
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_instances(
|
fn add_instances(
|
||||||
|
|
@ -509,6 +521,7 @@ fn add_instances(
|
||||||
image_size: [f32; 2],
|
image_size: [f32; 2],
|
||||||
rotation: f32,
|
rotation: f32,
|
||||||
opacity: f32,
|
opacity: f32,
|
||||||
|
snap: bool,
|
||||||
entry: &atlas::Entry,
|
entry: &atlas::Entry,
|
||||||
instances: &mut Vec<Instance>,
|
instances: &mut Vec<Instance>,
|
||||||
) {
|
) {
|
||||||
|
|
@ -525,6 +538,7 @@ fn add_instances(
|
||||||
image_size,
|
image_size,
|
||||||
rotation,
|
rotation,
|
||||||
opacity,
|
opacity,
|
||||||
|
snap,
|
||||||
allocation,
|
allocation,
|
||||||
instances,
|
instances,
|
||||||
);
|
);
|
||||||
|
|
@ -554,8 +568,8 @@ fn add_instances(
|
||||||
];
|
];
|
||||||
|
|
||||||
add_instance(
|
add_instance(
|
||||||
position, center, size, rotation, opacity, allocation,
|
position, center, size, rotation, opacity, snap,
|
||||||
instances,
|
allocation, instances,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -569,6 +583,7 @@ fn add_instance(
|
||||||
size: [f32; 2],
|
size: [f32; 2],
|
||||||
rotation: f32,
|
rotation: f32,
|
||||||
opacity: f32,
|
opacity: f32,
|
||||||
|
snap: bool,
|
||||||
allocation: &atlas::Allocation,
|
allocation: &atlas::Allocation,
|
||||||
instances: &mut Vec<Instance>,
|
instances: &mut Vec<Instance>,
|
||||||
) {
|
) {
|
||||||
|
|
@ -591,6 +606,7 @@ fn add_instance(
|
||||||
(height as f32 - 1.0) / atlas::SIZE as f32,
|
(height as f32 - 1.0) / atlas::SIZE as f32,
|
||||||
],
|
],
|
||||||
_layer: layer as u32,
|
_layer: layer as u32,
|
||||||
|
_snap: snap as u32,
|
||||||
};
|
};
|
||||||
|
|
||||||
instances.push(instance);
|
instances.push(instance);
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,49 @@ impl Layer {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_image(
|
pub fn draw_image(
|
||||||
|
&mut self,
|
||||||
|
image: &Image,
|
||||||
|
transformation: Transformation,
|
||||||
|
) {
|
||||||
|
match image {
|
||||||
|
Image::Raster {
|
||||||
|
handle,
|
||||||
|
filter_method,
|
||||||
|
bounds,
|
||||||
|
rotation,
|
||||||
|
opacity,
|
||||||
|
snap,
|
||||||
|
} => {
|
||||||
|
self.draw_raster(
|
||||||
|
handle.clone(),
|
||||||
|
*filter_method,
|
||||||
|
*bounds,
|
||||||
|
transformation,
|
||||||
|
*rotation,
|
||||||
|
*opacity,
|
||||||
|
*snap,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Image::Vector {
|
||||||
|
handle,
|
||||||
|
color,
|
||||||
|
bounds,
|
||||||
|
rotation,
|
||||||
|
opacity,
|
||||||
|
} => {
|
||||||
|
self.draw_svg(
|
||||||
|
handle.clone(),
|
||||||
|
*color,
|
||||||
|
*bounds,
|
||||||
|
transformation,
|
||||||
|
*rotation,
|
||||||
|
*opacity,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw_raster(
|
||||||
&mut self,
|
&mut self,
|
||||||
handle: crate::core::image::Handle,
|
handle: crate::core::image::Handle,
|
||||||
filter_method: crate::core::image::FilterMethod,
|
filter_method: crate::core::image::FilterMethod,
|
||||||
|
|
@ -120,6 +163,7 @@ impl Layer {
|
||||||
transformation: Transformation,
|
transformation: Transformation,
|
||||||
rotation: Radians,
|
rotation: Radians,
|
||||||
opacity: f32,
|
opacity: f32,
|
||||||
|
snap: bool,
|
||||||
) {
|
) {
|
||||||
let image = Image::Raster {
|
let image = Image::Raster {
|
||||||
handle,
|
handle,
|
||||||
|
|
@ -127,6 +171,7 @@ impl Layer {
|
||||||
bounds: bounds * transformation,
|
bounds: bounds * transformation,
|
||||||
rotation,
|
rotation,
|
||||||
opacity,
|
opacity,
|
||||||
|
snap,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.images.push(image);
|
self.images.push(image);
|
||||||
|
|
|
||||||
|
|
@ -536,13 +536,14 @@ impl core::image::Renderer for Renderer {
|
||||||
opacity: f32,
|
opacity: f32,
|
||||||
) {
|
) {
|
||||||
let (layer, transformation) = self.layers.current_mut();
|
let (layer, transformation) = self.layers.current_mut();
|
||||||
layer.draw_image(
|
layer.draw_raster(
|
||||||
handle,
|
handle,
|
||||||
filter_method,
|
filter_method,
|
||||||
bounds,
|
bounds,
|
||||||
transformation,
|
transformation,
|
||||||
rotation,
|
rotation,
|
||||||
opacity,
|
opacity,
|
||||||
|
true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -593,8 +594,17 @@ impl graphics::geometry::Renderer for Renderer {
|
||||||
let (layer, transformation) = self.layers.current_mut();
|
let (layer, transformation) = self.layers.current_mut();
|
||||||
|
|
||||||
match geometry {
|
match geometry {
|
||||||
Geometry::Live { meshes, text } => {
|
Geometry::Live {
|
||||||
|
meshes,
|
||||||
|
images,
|
||||||
|
text,
|
||||||
|
} => {
|
||||||
layer.draw_mesh_group(meshes, transformation);
|
layer.draw_mesh_group(meshes, transformation);
|
||||||
|
|
||||||
|
for image in images {
|
||||||
|
layer.draw_image(&image, transformation);
|
||||||
|
}
|
||||||
|
|
||||||
layer.draw_text_group(text, transformation);
|
layer.draw_text_group(text, transformation);
|
||||||
}
|
}
|
||||||
Geometry::Cached(cache) => {
|
Geometry::Cached(cache) => {
|
||||||
|
|
@ -602,6 +612,12 @@ impl graphics::geometry::Renderer for Renderer {
|
||||||
layer.draw_mesh_cache(meshes, transformation);
|
layer.draw_mesh_cache(meshes, transformation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(images) = cache.images {
|
||||||
|
for image in images.iter() {
|
||||||
|
layer.draw_image(image, transformation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(text) = cache.text {
|
if let Some(text) = cache.text {
|
||||||
layer.draw_text_cache(text, transformation);
|
layer.draw_text_cache(text, transformation);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
struct Globals {
|
struct Globals {
|
||||||
transform: mat4x4<f32>,
|
transform: mat4x4<f32>,
|
||||||
|
scale_factor: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
@group(0) @binding(0) var<uniform> globals: Globals;
|
@group(0) @binding(0) var<uniform> globals: Globals;
|
||||||
|
|
@ -16,6 +17,7 @@ struct VertexInput {
|
||||||
@location(5) atlas_pos: vec2<f32>,
|
@location(5) atlas_pos: vec2<f32>,
|
||||||
@location(6) atlas_scale: vec2<f32>,
|
@location(6) atlas_scale: vec2<f32>,
|
||||||
@location(7) layer: i32,
|
@location(7) layer: i32,
|
||||||
|
@location(8) snap: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct VertexOutput {
|
struct VertexOutput {
|
||||||
|
|
@ -38,7 +40,7 @@ fn vs_main(input: VertexInput) -> VertexOutput {
|
||||||
out.opacity = input.opacity;
|
out.opacity = input.opacity;
|
||||||
|
|
||||||
// Calculate the vertex position and move the center to the origin
|
// Calculate the vertex position and move the center to the origin
|
||||||
v_pos = round(input.pos) + v_pos * input.scale - input.center;
|
v_pos = input.pos + v_pos * input.scale - input.center;
|
||||||
|
|
||||||
// Apply the rotation around the center of the image
|
// Apply the rotation around the center of the image
|
||||||
let cos_rot = cos(input.rotation);
|
let cos_rot = cos(input.rotation);
|
||||||
|
|
@ -51,7 +53,13 @@ fn vs_main(input: VertexInput) -> VertexOutput {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Calculate the final position of the vertex
|
// Calculate the final position of the vertex
|
||||||
out.position = globals.transform * (vec4<f32>(input.center, 0.0, 0.0) + rotate * vec4<f32>(v_pos, 0.0, 1.0));
|
out.position = vec4(vec2(globals.scale_factor), 1.0, 1.0) * (vec4<f32>(input.center, 0.0, 0.0) + rotate * vec4<f32>(v_pos, 0.0, 1.0));
|
||||||
|
|
||||||
|
if bool(input.snap) {
|
||||||
|
out.position = round(out.position);
|
||||||
|
}
|
||||||
|
|
||||||
|
out.position = globals.transform * out.position;
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue