Merge pull request #2537 from iced-rs/feature/canvas-image-support
`image` and `svg` support for `canvas`
This commit is contained in:
commit
145c3dc8fc
31 changed files with 627 additions and 400 deletions
|
|
@ -7,6 +7,73 @@ use rustc_hash::FxHasher;
|
|||
use std::hash::{Hash, Hasher};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// A raster image that can be drawn.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Image<H = Handle> {
|
||||
/// The handle of the image.
|
||||
pub handle: H,
|
||||
|
||||
/// The filter method of the image.
|
||||
pub filter_method: FilterMethod,
|
||||
|
||||
/// The rotation to be applied to the image; on its center.
|
||||
pub rotation: Radians,
|
||||
|
||||
/// The opacity of the image.
|
||||
///
|
||||
/// 0 means transparent. 1 means opaque.
|
||||
pub opacity: f32,
|
||||
|
||||
/// If set to `true`, the image will be snapped to the pixel grid.
|
||||
///
|
||||
/// This can avoid graphical glitches, specially when using
|
||||
/// [`FilterMethod::Nearest`].
|
||||
pub snap: bool,
|
||||
}
|
||||
|
||||
impl Image<Handle> {
|
||||
/// Creates a new [`Image`] with the given handle.
|
||||
pub fn new(handle: impl Into<Handle>) -> Self {
|
||||
Self {
|
||||
handle: handle.into(),
|
||||
filter_method: FilterMethod::default(),
|
||||
rotation: Radians(0.0),
|
||||
opacity: 1.0,
|
||||
snap: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the filter method of the [`Image`].
|
||||
pub fn filter_method(mut self, filter_method: FilterMethod) -> Self {
|
||||
self.filter_method = filter_method;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the rotation of the [`Image`].
|
||||
pub fn rotation(mut self, rotation: impl Into<Radians>) -> Self {
|
||||
self.rotation = rotation.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the opacity of the [`Image`].
|
||||
pub fn opacity(mut self, opacity: impl Into<f32>) -> Self {
|
||||
self.opacity = opacity.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets whether the [`Image`] should be snapped to the pixel grid.
|
||||
pub fn snap(mut self, snap: bool) -> Self {
|
||||
self.snap = snap;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Handle> for Image {
|
||||
fn from(handle: &Handle) -> Self {
|
||||
Image::new(handle.clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// A handle of some image data.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub enum Handle {
|
||||
|
|
@ -101,6 +168,12 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl From<&Handle> for Handle {
|
||||
fn from(value: &Handle) -> Self {
|
||||
value.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Handle {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
|
|
@ -166,14 +239,6 @@ pub trait Renderer: crate::Renderer {
|
|||
/// Returns the dimensions of an image for the given [`Handle`].
|
||||
fn measure_image(&self, handle: &Self::Handle) -> Size<u32>;
|
||||
|
||||
/// Draws an image with the given [`Handle`] and inside the provided
|
||||
/// `bounds`.
|
||||
fn draw_image(
|
||||
&mut self,
|
||||
handle: Self::Handle,
|
||||
filter_method: FilterMethod,
|
||||
bounds: Rectangle,
|
||||
rotation: Radians,
|
||||
opacity: f32,
|
||||
);
|
||||
/// Draws an [`Image`] inside the provided `bounds`.
|
||||
fn draw_image(&mut self, image: Image<Self::Handle>, bounds: Rectangle);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ pub use element::Element;
|
|||
pub use event::Event;
|
||||
pub use font::Font;
|
||||
pub use gradient::Gradient;
|
||||
pub use image::Image;
|
||||
pub use layout::Layout;
|
||||
pub use length::Length;
|
||||
pub use overlay::Overlay;
|
||||
|
|
@ -69,6 +70,7 @@ pub use rotation::Rotation;
|
|||
pub use shadow::Shadow;
|
||||
pub use shell::Shell;
|
||||
pub use size::Size;
|
||||
pub use svg::Svg;
|
||||
pub use text::Text;
|
||||
pub use theme::Theme;
|
||||
pub use transformation::Transformation;
|
||||
|
|
|
|||
|
|
@ -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`].
|
||||
pub fn center(&self) -> Point {
|
||||
Point::new(self.center_x(), self.center_y())
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
use crate::alignment;
|
||||
use crate::image;
|
||||
use crate::image::{self, Image};
|
||||
use crate::renderer::{self, Renderer};
|
||||
use crate::svg;
|
||||
use crate::text::{self, Text};
|
||||
use crate::{
|
||||
Background, Color, Font, Pixels, Point, Radians, Rectangle, Size,
|
||||
Transformation,
|
||||
Background, Color, Font, Pixels, Point, Rectangle, Size, Transformation,
|
||||
};
|
||||
|
||||
impl Renderer for () {
|
||||
|
|
@ -178,21 +177,13 @@ impl text::Editor for () {
|
|||
}
|
||||
|
||||
impl image::Renderer for () {
|
||||
type Handle = ();
|
||||
type Handle = image::Handle;
|
||||
|
||||
fn measure_image(&self, _handle: &Self::Handle) -> Size<u32> {
|
||||
Size::default()
|
||||
}
|
||||
|
||||
fn draw_image(
|
||||
&mut self,
|
||||
_handle: Self::Handle,
|
||||
_filter_method: image::FilterMethod,
|
||||
_bounds: Rectangle,
|
||||
_rotation: Radians,
|
||||
_opacity: f32,
|
||||
) {
|
||||
}
|
||||
fn draw_image(&mut self, _image: Image, _bounds: Rectangle) {}
|
||||
}
|
||||
|
||||
impl svg::Renderer for () {
|
||||
|
|
@ -200,13 +191,5 @@ impl svg::Renderer for () {
|
|||
Size::default()
|
||||
}
|
||||
|
||||
fn draw_svg(
|
||||
&mut self,
|
||||
_handle: svg::Handle,
|
||||
_color: Option<Color>,
|
||||
_bounds: Rectangle,
|
||||
_rotation: Radians,
|
||||
_opacity: f32,
|
||||
) {
|
||||
}
|
||||
fn draw_svg(&mut self, _svg: svg::Svg, _bounds: Rectangle) {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,66 @@ use std::hash::{Hash, Hasher as _};
|
|||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// A raster image that can be drawn.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Svg<H = Handle> {
|
||||
/// The handle of the [`Svg`].
|
||||
pub handle: H,
|
||||
|
||||
/// The [`Color`] filter to be applied to the [`Svg`].
|
||||
///
|
||||
/// If some [`Color`] is set, the whole [`Svg`] will be
|
||||
/// painted with it—ignoring any intrinsic colors.
|
||||
///
|
||||
/// This can be useful for coloring icons programmatically
|
||||
/// (e.g. with a theme).
|
||||
pub color: Option<Color>,
|
||||
|
||||
/// The rotation to be applied to the image; on its center.
|
||||
pub rotation: Radians,
|
||||
|
||||
/// The opacity of the [`Svg`].
|
||||
///
|
||||
/// 0 means transparent. 1 means opaque.
|
||||
pub opacity: f32,
|
||||
}
|
||||
|
||||
impl Svg<Handle> {
|
||||
/// Creates a new [`Svg`] with the given handle.
|
||||
pub fn new(handle: impl Into<Handle>) -> Self {
|
||||
Self {
|
||||
handle: handle.into(),
|
||||
color: None,
|
||||
rotation: Radians(0.0),
|
||||
opacity: 1.0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the [`Color`] filter of the [`Svg`].
|
||||
pub fn color(mut self, color: impl Into<Color>) -> Self {
|
||||
self.color = Some(color.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the rotation of the [`Svg`].
|
||||
pub fn rotation(mut self, rotation: impl Into<Radians>) -> Self {
|
||||
self.rotation = rotation.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the opacity of the [`Svg`].
|
||||
pub fn opacity(mut self, opacity: impl Into<f32>) -> Self {
|
||||
self.opacity = opacity.into();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Handle> for Svg {
|
||||
fn from(handle: &Handle) -> Self {
|
||||
Svg::new(handle.clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// A handle of Svg data.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Handle {
|
||||
|
|
@ -95,12 +155,5 @@ pub trait Renderer: crate::Renderer {
|
|||
fn measure_svg(&self, handle: &Handle) -> Size<u32>;
|
||||
|
||||
/// Draws an SVG with the given [`Handle`], an optional [`Color`] filter, and inside the provided `bounds`.
|
||||
fn draw_svg(
|
||||
&mut self,
|
||||
handle: Handle,
|
||||
color: Option<Color>,
|
||||
bounds: Rectangle,
|
||||
rotation: Radians,
|
||||
opacity: f32,
|
||||
);
|
||||
fn draw_svg(&mut self, svg: Svg, bounds: Rectangle);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ fn main() -> iced::Result {
|
|||
|
||||
#[derive(Default)]
|
||||
struct Example {
|
||||
screenshot: Option<Screenshot>,
|
||||
screenshot: Option<(Screenshot, image::Handle)>,
|
||||
saved_png_path: Option<Result<String, PngError>>,
|
||||
png_saving: bool,
|
||||
crop_error: Option<screenshot::CropError>,
|
||||
|
|
@ -52,10 +52,17 @@ impl Example {
|
|||
.map(Message::Screenshotted);
|
||||
}
|
||||
Message::Screenshotted(screenshot) => {
|
||||
self.screenshot = Some(screenshot);
|
||||
self.screenshot = Some((
|
||||
screenshot.clone(),
|
||||
image::Handle::from_rgba(
|
||||
screenshot.size.width,
|
||||
screenshot.size.height,
|
||||
screenshot.bytes,
|
||||
),
|
||||
));
|
||||
}
|
||||
Message::Png => {
|
||||
if let Some(screenshot) = &self.screenshot {
|
||||
if let Some((screenshot, _handle)) = &self.screenshot {
|
||||
self.png_saving = true;
|
||||
|
||||
return Task::perform(
|
||||
|
|
@ -81,7 +88,7 @@ impl Example {
|
|||
self.height_input_value = new_value;
|
||||
}
|
||||
Message::Crop => {
|
||||
if let Some(screenshot) = &self.screenshot {
|
||||
if let Some((screenshot, _handle)) = &self.screenshot {
|
||||
let cropped = screenshot.crop(Rectangle::<u32> {
|
||||
x: self.x_input_value.unwrap_or(0),
|
||||
y: self.y_input_value.unwrap_or(0),
|
||||
|
|
@ -91,7 +98,14 @@ impl Example {
|
|||
|
||||
match cropped {
|
||||
Ok(screenshot) => {
|
||||
self.screenshot = Some(screenshot);
|
||||
self.screenshot = Some((
|
||||
screenshot.clone(),
|
||||
image::Handle::from_rgba(
|
||||
screenshot.size.width,
|
||||
screenshot.size.height,
|
||||
screenshot.bytes,
|
||||
),
|
||||
));
|
||||
self.crop_error = None;
|
||||
}
|
||||
Err(crop_error) => {
|
||||
|
|
@ -106,20 +120,16 @@ impl Example {
|
|||
}
|
||||
|
||||
fn view(&self) -> Element<'_, Message> {
|
||||
let image: Element<Message> = if let Some(screenshot) = &self.screenshot
|
||||
{
|
||||
image(image::Handle::from_rgba(
|
||||
screenshot.size.width,
|
||||
screenshot.size.height,
|
||||
screenshot.clone(),
|
||||
))
|
||||
.content_fit(ContentFit::Contain)
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
.into()
|
||||
} else {
|
||||
text("Press the button to take a screenshot!").into()
|
||||
};
|
||||
let image: Element<Message> =
|
||||
if let Some((_screenshot, handle)) = &self.screenshot {
|
||||
image(handle)
|
||||
.content_fit(ContentFit::Contain)
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
.into()
|
||||
} else {
|
||||
text("Press the button to take a screenshot!").into()
|
||||
};
|
||||
|
||||
let image = container(image)
|
||||
.center_y(FillPortion(2))
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ publish = false
|
|||
|
||||
[dependencies]
|
||||
iced.workspace = true
|
||||
iced.features = ["debug", "canvas", "tokio"]
|
||||
iced.features = ["debug", "canvas", "image", "tokio"]
|
||||
|
||||
rand = "0.8.3"
|
||||
tracing-subscriber = "0.3"
|
||||
|
|
|
|||
BIN
examples/solar_system/assets/earth.png
Normal file
BIN
examples/solar_system/assets/earth.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 90 KiB |
BIN
examples/solar_system/assets/moon.png
Normal file
BIN
examples/solar_system/assets/moon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 103 KiB |
BIN
examples/solar_system/assets/sun.png
Normal file
BIN
examples/solar_system/assets/sun.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 112 KiB |
|
|
@ -7,10 +7,9 @@
|
|||
//!
|
||||
//! [1]: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Basic_animations#An_animated_solar_system
|
||||
use iced::mouse;
|
||||
use iced::widget::canvas;
|
||||
use iced::widget::canvas::gradient;
|
||||
use iced::widget::canvas::stroke::{self, Stroke};
|
||||
use iced::widget::canvas::{Geometry, Path};
|
||||
use iced::widget::{canvas, image};
|
||||
use iced::window;
|
||||
use iced::{
|
||||
Color, Element, Fill, Point, Rectangle, Renderer, Size, Subscription,
|
||||
|
|
@ -66,6 +65,9 @@ impl SolarSystem {
|
|||
|
||||
#[derive(Debug)]
|
||||
struct State {
|
||||
sun: image::Handle,
|
||||
earth: image::Handle,
|
||||
moon: image::Handle,
|
||||
space_cache: canvas::Cache,
|
||||
system_cache: canvas::Cache,
|
||||
start: Instant,
|
||||
|
|
@ -85,6 +87,15 @@ impl State {
|
|||
let size = window::Settings::default().size;
|
||||
|
||||
State {
|
||||
sun: image::Handle::from_bytes(
|
||||
include_bytes!("../assets/sun.png").as_slice(),
|
||||
),
|
||||
earth: image::Handle::from_bytes(
|
||||
include_bytes!("../assets/earth.png").as_slice(),
|
||||
),
|
||||
moon: image::Handle::from_bytes(
|
||||
include_bytes!("../assets/moon.png").as_slice(),
|
||||
),
|
||||
space_cache: canvas::Cache::default(),
|
||||
system_cache: canvas::Cache::default(),
|
||||
start: now,
|
||||
|
|
@ -132,6 +143,8 @@ impl<Message> canvas::Program<Message> for State {
|
|||
|
||||
let background =
|
||||
self.space_cache.draw(renderer, bounds.size(), |frame| {
|
||||
frame.fill_rectangle(Point::ORIGIN, frame.size(), Color::BLACK);
|
||||
|
||||
let stars = Path::new(|path| {
|
||||
for (p, size) in &self.stars {
|
||||
path.rectangle(*p, Size::new(*size, *size));
|
||||
|
|
@ -144,17 +157,18 @@ impl<Message> canvas::Program<Message> for State {
|
|||
|
||||
let system = self.system_cache.draw(renderer, bounds.size(), |frame| {
|
||||
let center = frame.center();
|
||||
frame.translate(Vector::new(center.x, center.y));
|
||||
|
||||
let sun = Path::circle(center, Self::SUN_RADIUS);
|
||||
let orbit = Path::circle(center, Self::ORBIT_RADIUS);
|
||||
frame.draw_image(
|
||||
Rectangle::with_radius(Self::SUN_RADIUS),
|
||||
&self.sun,
|
||||
);
|
||||
|
||||
frame.fill(&sun, Color::from_rgb8(0xF9, 0xD7, 0x1C));
|
||||
let orbit = Path::circle(Point::ORIGIN, Self::ORBIT_RADIUS);
|
||||
frame.stroke(
|
||||
&orbit,
|
||||
Stroke {
|
||||
style: stroke::Style::Solid(Color::from_rgba8(
|
||||
0, 153, 255, 0.1,
|
||||
)),
|
||||
style: stroke::Style::Solid(Color::WHITE.scale_alpha(0.1)),
|
||||
width: 1.0,
|
||||
line_dash: canvas::LineDash {
|
||||
offset: 0,
|
||||
|
|
@ -168,30 +182,21 @@ impl<Message> canvas::Program<Message> for State {
|
|||
let rotation = (2.0 * PI / 60.0) * elapsed.as_secs() as f32
|
||||
+ (2.0 * PI / 60_000.0) * elapsed.subsec_millis() as f32;
|
||||
|
||||
frame.with_save(|frame| {
|
||||
frame.translate(Vector::new(center.x, center.y));
|
||||
frame.rotate(rotation);
|
||||
frame.translate(Vector::new(Self::ORBIT_RADIUS, 0.0));
|
||||
frame.rotate(rotation);
|
||||
frame.translate(Vector::new(Self::ORBIT_RADIUS, 0.0));
|
||||
|
||||
let earth = Path::circle(Point::ORIGIN, Self::EARTH_RADIUS);
|
||||
frame.draw_image(
|
||||
Rectangle::with_radius(Self::EARTH_RADIUS),
|
||||
canvas::Image::new(&self.earth).rotation(-rotation * 20.0),
|
||||
);
|
||||
|
||||
let earth_fill = gradient::Linear::new(
|
||||
Point::new(-Self::EARTH_RADIUS, 0.0),
|
||||
Point::new(Self::EARTH_RADIUS, 0.0),
|
||||
)
|
||||
.add_stop(0.2, Color::from_rgb(0.15, 0.50, 1.0))
|
||||
.add_stop(0.8, Color::from_rgb(0.0, 0.20, 0.47));
|
||||
frame.rotate(rotation * 10.0);
|
||||
frame.translate(Vector::new(0.0, Self::MOON_DISTANCE));
|
||||
|
||||
frame.fill(&earth, earth_fill);
|
||||
|
||||
frame.with_save(|frame| {
|
||||
frame.rotate(rotation * 10.0);
|
||||
frame.translate(Vector::new(0.0, Self::MOON_DISTANCE));
|
||||
|
||||
let moon = Path::circle(Point::ORIGIN, Self::MOON_RADIUS);
|
||||
frame.fill(&moon, Color::WHITE);
|
||||
});
|
||||
});
|
||||
frame.draw_image(
|
||||
Rectangle::with_radius(Self::MOON_RADIUS),
|
||||
&self.moon,
|
||||
);
|
||||
});
|
||||
|
||||
vec![background, system]
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ all-features = true
|
|||
[features]
|
||||
geometry = ["lyon_path"]
|
||||
image = ["dep:image", "kamadak-exif"]
|
||||
svg = []
|
||||
web-colors = []
|
||||
fira-sans = []
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ pub use stroke::{LineCap, LineDash, LineJoin, Stroke};
|
|||
pub use style::Style;
|
||||
pub use text::Text;
|
||||
|
||||
pub use crate::core::{Image, Svg};
|
||||
pub use crate::gradient::{self, Gradient};
|
||||
|
||||
use crate::cache::Cached;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
//! Draw and generate geometry.
|
||||
use crate::core::{Point, Radians, Rectangle, Size, Vector};
|
||||
use crate::geometry::{self, Fill, Path, Stroke, Text};
|
||||
use crate::geometry::{self, Fill, Image, Path, Stroke, Svg, Text};
|
||||
|
||||
/// The region of a surface that can be used to draw geometry.
|
||||
#[allow(missing_debug_implementations)]
|
||||
|
|
@ -75,6 +75,18 @@ where
|
|||
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, bounds: Rectangle, image: impl Into<Image>) {
|
||||
self.raw.draw_image(bounds, image);
|
||||
}
|
||||
|
||||
/// Draws the given [`Svg`] on the [`Frame`] inside the given bounds.
|
||||
#[cfg(feature = "svg")]
|
||||
pub fn draw_svg(&mut self, bounds: Rectangle, svg: impl Into<Svg>) {
|
||||
self.raw.draw_svg(bounds, svg);
|
||||
}
|
||||
|
||||
/// Stores the current transform of the [`Frame`] and executes the given
|
||||
/// drawing operations, restoring the transform afterwards.
|
||||
///
|
||||
|
|
@ -116,8 +128,7 @@ where
|
|||
let mut frame = self.draft(region);
|
||||
|
||||
let result = f(&mut frame);
|
||||
|
||||
self.paste(frame, Point::new(region.x, region.y));
|
||||
self.paste(frame);
|
||||
|
||||
result
|
||||
}
|
||||
|
|
@ -134,8 +145,8 @@ where
|
|||
}
|
||||
|
||||
/// Draws the contents of the given [`Frame`] with origin at the given [`Point`].
|
||||
fn paste(&mut self, frame: Self, at: Point) {
|
||||
self.raw.paste(frame.raw, at);
|
||||
fn paste(&mut self, frame: Self) {
|
||||
self.raw.paste(frame.raw);
|
||||
}
|
||||
|
||||
/// Applies a translation to the current transform of the [`Frame`].
|
||||
|
|
@ -186,7 +197,7 @@ pub trait Backend: Sized {
|
|||
fn scale_nonuniform(&mut self, scale: impl Into<Vector>);
|
||||
|
||||
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>>);
|
||||
|
||||
|
|
@ -199,6 +210,9 @@ pub trait Backend: Sized {
|
|||
fill: impl Into<Fill>,
|
||||
);
|
||||
|
||||
fn draw_image(&mut self, bounds: Rectangle, image: impl Into<Image>);
|
||||
fn draw_svg(&mut self, bounds: Rectangle, svg: impl Into<Svg>);
|
||||
|
||||
fn into_geometry(self) -> Self::Geometry;
|
||||
}
|
||||
|
||||
|
|
@ -231,7 +245,7 @@ impl Backend for () {
|
|||
fn scale_nonuniform(&mut self, _scale: impl Into<Vector>) {}
|
||||
|
||||
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>>) {}
|
||||
|
||||
|
|
@ -245,5 +259,8 @@ impl Backend for () {
|
|||
) {
|
||||
}
|
||||
|
||||
fn draw_image(&mut self, _bounds: Rectangle, _image: impl Into<Image>) {}
|
||||
fn draw_svg(&mut self, _bounds: Rectangle, _svg: impl Into<Svg>) {}
|
||||
|
||||
fn into_geometry(self) -> Self::Geometry {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,57 +2,26 @@
|
|||
#[cfg(feature = "image")]
|
||||
pub use ::image as image_rs;
|
||||
|
||||
use crate::core::{image, svg, Color, Radians, Rectangle};
|
||||
use crate::core::image;
|
||||
use crate::core::svg;
|
||||
use crate::core::Rectangle;
|
||||
|
||||
/// A raster or vector image.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Image {
|
||||
/// A raster image.
|
||||
Raster {
|
||||
/// The handle of a raster image.
|
||||
handle: image::Handle,
|
||||
Raster(image::Image, Rectangle),
|
||||
|
||||
/// The filter method of a raster image.
|
||||
filter_method: image::FilterMethod,
|
||||
|
||||
/// The bounds of the image.
|
||||
bounds: Rectangle,
|
||||
|
||||
/// The rotation of the image.
|
||||
rotation: Radians,
|
||||
|
||||
/// The opacity of the image.
|
||||
opacity: f32,
|
||||
},
|
||||
/// A vector image.
|
||||
Vector {
|
||||
/// The handle of a vector image.
|
||||
handle: svg::Handle,
|
||||
|
||||
/// The [`Color`] filter
|
||||
color: Option<Color>,
|
||||
|
||||
/// The bounds of the image.
|
||||
bounds: Rectangle,
|
||||
|
||||
/// The rotation of the image.
|
||||
rotation: Radians,
|
||||
|
||||
/// The opacity of the image.
|
||||
opacity: f32,
|
||||
},
|
||||
Vector(svg::Svg, Rectangle),
|
||||
}
|
||||
|
||||
impl Image {
|
||||
/// Returns the bounds of the [`Image`].
|
||||
pub fn bounds(&self) -> Rectangle {
|
||||
match self {
|
||||
Image::Raster {
|
||||
bounds, rotation, ..
|
||||
}
|
||||
| Image::Vector {
|
||||
bounds, rotation, ..
|
||||
} => bounds.rotate(*rotation),
|
||||
Image::Raster(image, bounds) => bounds.rotate(image.rotation),
|
||||
Image::Vector(svg, bounds) => bounds.rotate(svg.rotation),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use crate::core::image;
|
|||
use crate::core::renderer;
|
||||
use crate::core::svg;
|
||||
use crate::core::{
|
||||
self, Background, Color, Point, Radians, Rectangle, Size, Transformation,
|
||||
self, Background, Color, Image, Point, Rectangle, Size, Svg, Transformation,
|
||||
};
|
||||
use crate::graphics;
|
||||
use crate::graphics::compositor;
|
||||
|
|
@ -149,25 +149,8 @@ where
|
|||
delegate!(self, renderer, renderer.measure_image(handle))
|
||||
}
|
||||
|
||||
fn draw_image(
|
||||
&mut self,
|
||||
handle: Self::Handle,
|
||||
filter_method: image::FilterMethod,
|
||||
bounds: Rectangle,
|
||||
rotation: Radians,
|
||||
opacity: f32,
|
||||
) {
|
||||
delegate!(
|
||||
self,
|
||||
renderer,
|
||||
renderer.draw_image(
|
||||
handle,
|
||||
filter_method,
|
||||
bounds,
|
||||
rotation,
|
||||
opacity
|
||||
)
|
||||
);
|
||||
fn draw_image(&mut self, image: Image<A::Handle>, bounds: Rectangle) {
|
||||
delegate!(self, renderer, renderer.draw_image(image, bounds));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -180,19 +163,8 @@ where
|
|||
delegate!(self, renderer, renderer.measure_svg(handle))
|
||||
}
|
||||
|
||||
fn draw_svg(
|
||||
&mut self,
|
||||
handle: svg::Handle,
|
||||
color: Option<Color>,
|
||||
bounds: Rectangle,
|
||||
rotation: Radians,
|
||||
opacity: f32,
|
||||
) {
|
||||
delegate!(
|
||||
self,
|
||||
renderer,
|
||||
renderer.draw_svg(handle, color, bounds, rotation, opacity)
|
||||
);
|
||||
fn draw_svg(&mut self, svg: Svg, bounds: Rectangle) {
|
||||
delegate!(self, renderer, renderer.draw_svg(svg, bounds));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -441,9 +413,9 @@ where
|
|||
#[cfg(feature = "geometry")]
|
||||
mod geometry {
|
||||
use super::Renderer;
|
||||
use crate::core::{Point, Radians, Rectangle, Size, Vector};
|
||||
use crate::core::{Point, Radians, Rectangle, Size, Svg, Vector};
|
||||
use crate::graphics::cache::{self, Cached};
|
||||
use crate::graphics::geometry::{self, Fill, Path, Stroke, Text};
|
||||
use crate::graphics::geometry::{self, Fill, Image, Path, Stroke, Text};
|
||||
|
||||
impl<A, B> geometry::Renderer for Renderer<A, B>
|
||||
where
|
||||
|
|
@ -572,6 +544,14 @@ mod geometry {
|
|||
delegate!(self, frame, frame.fill_text(text));
|
||||
}
|
||||
|
||||
fn draw_image(&mut self, bounds: Rectangle, image: impl Into<Image>) {
|
||||
delegate!(self, frame, frame.draw_image(bounds, image));
|
||||
}
|
||||
|
||||
fn draw_svg(&mut self, bounds: Rectangle, svg: impl Into<Svg>) {
|
||||
delegate!(self, frame, frame.draw_svg(bounds, svg));
|
||||
}
|
||||
|
||||
fn push_transform(&mut self) {
|
||||
delegate!(self, frame, frame.push_transform());
|
||||
}
|
||||
|
|
@ -587,13 +567,13 @@ mod geometry {
|
|||
}
|
||||
}
|
||||
|
||||
fn paste(&mut self, frame: Self, at: Point) {
|
||||
fn paste(&mut self, frame: Self) {
|
||||
match (self, frame) {
|
||||
(Self::Primary(target), Self::Primary(source)) => {
|
||||
target.paste(source, at);
|
||||
target.paste(source);
|
||||
}
|
||||
(Self::Secondary(target), Self::Secondary(source)) => {
|
||||
target.paste(source, at);
|
||||
target.paste(source);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ workspace = true
|
|||
|
||||
[features]
|
||||
image = ["iced_graphics/image"]
|
||||
svg = ["resvg"]
|
||||
svg = ["iced_graphics/svg", "resvg"]
|
||||
geometry = ["iced_graphics/geometry"]
|
||||
|
||||
[dependencies]
|
||||
|
|
|
|||
|
|
@ -550,13 +550,7 @@ impl Engine {
|
|||
) {
|
||||
match image {
|
||||
#[cfg(feature = "image")]
|
||||
Image::Raster {
|
||||
handle,
|
||||
filter_method,
|
||||
bounds,
|
||||
rotation,
|
||||
opacity,
|
||||
} => {
|
||||
Image::Raster(raster, bounds) => {
|
||||
let physical_bounds = *bounds * _transformation;
|
||||
|
||||
if !_clip_bounds.intersects(&physical_bounds) {
|
||||
|
|
@ -567,7 +561,7 @@ impl Engine {
|
|||
.then_some(_clip_mask as &_);
|
||||
|
||||
let center = physical_bounds.center();
|
||||
let radians = f32::from(*rotation);
|
||||
let radians = f32::from(raster.rotation);
|
||||
|
||||
let transform = into_transform(_transformation).post_rotate_at(
|
||||
radians.to_degrees(),
|
||||
|
|
@ -576,23 +570,17 @@ impl Engine {
|
|||
);
|
||||
|
||||
self.raster_pipeline.draw(
|
||||
handle,
|
||||
*filter_method,
|
||||
&raster.handle,
|
||||
raster.filter_method,
|
||||
*bounds,
|
||||
*opacity,
|
||||
raster.opacity,
|
||||
_pixels,
|
||||
transform,
|
||||
clip_mask,
|
||||
);
|
||||
}
|
||||
#[cfg(feature = "svg")]
|
||||
Image::Vector {
|
||||
handle,
|
||||
color,
|
||||
bounds,
|
||||
rotation,
|
||||
opacity,
|
||||
} => {
|
||||
Image::Vector(svg, bounds) => {
|
||||
let physical_bounds = *bounds * _transformation;
|
||||
|
||||
if !_clip_bounds.intersects(&physical_bounds) {
|
||||
|
|
@ -603,7 +591,7 @@ impl Engine {
|
|||
.then_some(_clip_mask as &_);
|
||||
|
||||
let center = physical_bounds.center();
|
||||
let radians = f32::from(*rotation);
|
||||
let radians = f32::from(svg.rotation);
|
||||
|
||||
let transform = into_transform(_transformation).post_rotate_at(
|
||||
radians.to_degrees(),
|
||||
|
|
@ -612,10 +600,10 @@ impl Engine {
|
|||
);
|
||||
|
||||
self.vector_pipeline.draw(
|
||||
handle,
|
||||
*color,
|
||||
&svg.handle,
|
||||
svg.color,
|
||||
physical_bounds,
|
||||
*opacity,
|
||||
svg.opacity,
|
||||
_pixels,
|
||||
transform,
|
||||
clip_mask,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
use crate::core::text::LineHeight;
|
||||
use crate::core::{Pixels, Point, Radians, Rectangle, Size, Vector};
|
||||
use crate::core::{self, Pixels, Point, Radians, Rectangle, Size, Svg, Vector};
|
||||
use crate::graphics::cache::{self, Cached};
|
||||
use crate::graphics::geometry::fill::{self, Fill};
|
||||
use crate::graphics::geometry::stroke::{self, Stroke};
|
||||
use crate::graphics::geometry::{self, Path, Style};
|
||||
use crate::graphics::{Gradient, Text};
|
||||
use crate::graphics::{self, Gradient, Image, Text};
|
||||
use crate::Primitive;
|
||||
|
||||
use std::rc::Rc;
|
||||
|
|
@ -13,6 +13,7 @@ use std::rc::Rc;
|
|||
pub enum Geometry {
|
||||
Live {
|
||||
text: Vec<Text>,
|
||||
images: Vec<graphics::Image>,
|
||||
primitives: Vec<Primitive>,
|
||||
clip_bounds: Rectangle,
|
||||
},
|
||||
|
|
@ -22,6 +23,7 @@ pub enum Geometry {
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct Cache {
|
||||
pub text: Rc<[Text]>,
|
||||
pub images: Rc<[graphics::Image]>,
|
||||
pub primitives: Rc<[Primitive]>,
|
||||
pub clip_bounds: Rectangle,
|
||||
}
|
||||
|
|
@ -37,10 +39,12 @@ impl Cached for Geometry {
|
|||
match self {
|
||||
Self::Live {
|
||||
primitives,
|
||||
images,
|
||||
text,
|
||||
clip_bounds,
|
||||
} => Cache {
|
||||
primitives: Rc::from(primitives),
|
||||
images: Rc::from(images),
|
||||
text: Rc::from(text),
|
||||
clip_bounds,
|
||||
},
|
||||
|
|
@ -55,6 +59,7 @@ pub struct Frame {
|
|||
transform: tiny_skia::Transform,
|
||||
stack: Vec<tiny_skia::Transform>,
|
||||
primitives: Vec<Primitive>,
|
||||
images: Vec<graphics::Image>,
|
||||
text: Vec<Text>,
|
||||
}
|
||||
|
||||
|
|
@ -68,6 +73,7 @@ impl Frame {
|
|||
clip_bounds,
|
||||
stack: Vec::new(),
|
||||
primitives: Vec::new(),
|
||||
images: Vec::new(),
|
||||
text: Vec::new(),
|
||||
transform: tiny_skia::Transform::from_translate(
|
||||
clip_bounds.x,
|
||||
|
|
@ -238,7 +244,7 @@ impl geometry::frame::Backend for Frame {
|
|||
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.text.extend(frame.text);
|
||||
}
|
||||
|
|
@ -269,10 +275,63 @@ impl geometry::frame::Backend for Frame {
|
|||
fn into_geometry(self) -> Geometry {
|
||||
Geometry::Live {
|
||||
primitives: self.primitives,
|
||||
images: self.images,
|
||||
text: self.text,
|
||||
clip_bounds: self.clip_bounds,
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_image(&mut self, bounds: Rectangle, image: impl Into<core::Image>) {
|
||||
let mut image = image.into();
|
||||
|
||||
let (bounds, external_rotation) =
|
||||
transform_rectangle(bounds, self.transform);
|
||||
|
||||
image.rotation += external_rotation;
|
||||
|
||||
self.images.push(graphics::Image::Raster(image, bounds));
|
||||
}
|
||||
|
||||
fn draw_svg(&mut self, bounds: Rectangle, svg: impl Into<Svg>) {
|
||||
let mut svg = svg.into();
|
||||
|
||||
let (bounds, external_rotation) =
|
||||
transform_rectangle(bounds, self.transform);
|
||||
|
||||
svg.rotation += external_rotation;
|
||||
|
||||
self.images.push(Image::Vector(svg, bounds));
|
||||
}
|
||||
}
|
||||
|
||||
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> {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::core::renderer::Quad;
|
||||
use crate::core::{
|
||||
image, renderer::Quad, svg, Background, Color, Point, Radians, Rectangle,
|
||||
Transformation,
|
||||
self, Background, Color, Point, Rectangle, Svg, Transformation,
|
||||
};
|
||||
use crate::graphics::damage;
|
||||
use crate::graphics::layer;
|
||||
|
|
@ -72,7 +72,7 @@ impl Layer {
|
|||
|
||||
pub fn draw_text(
|
||||
&mut self,
|
||||
text: crate::core::Text,
|
||||
text: core::Text,
|
||||
position: Point,
|
||||
color: Color,
|
||||
clip_bounds: Rectangle,
|
||||
|
|
@ -115,42 +115,35 @@ impl Layer {
|
|||
.push(Item::Cached(text, clip_bounds, transformation));
|
||||
}
|
||||
|
||||
pub fn draw_image(
|
||||
pub fn draw_image(&mut self, image: Image, transformation: Transformation) {
|
||||
match image {
|
||||
Image::Raster(raster, bounds) => {
|
||||
self.draw_raster(raster, bounds, transformation);
|
||||
}
|
||||
Image::Vector(svg, bounds) => {
|
||||
self.draw_svg(svg, bounds, transformation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw_raster(
|
||||
&mut self,
|
||||
handle: image::Handle,
|
||||
filter_method: image::FilterMethod,
|
||||
image: core::Image,
|
||||
bounds: Rectangle,
|
||||
transformation: Transformation,
|
||||
rotation: Radians,
|
||||
opacity: f32,
|
||||
) {
|
||||
let image = Image::Raster {
|
||||
handle,
|
||||
filter_method,
|
||||
bounds: bounds * transformation,
|
||||
rotation,
|
||||
opacity,
|
||||
};
|
||||
let image = Image::Raster(image, bounds * transformation);
|
||||
|
||||
self.images.push(image);
|
||||
}
|
||||
|
||||
pub fn draw_svg(
|
||||
&mut self,
|
||||
handle: svg::Handle,
|
||||
color: Option<Color>,
|
||||
svg: Svg,
|
||||
bounds: Rectangle,
|
||||
transformation: Transformation,
|
||||
rotation: Radians,
|
||||
opacity: f32,
|
||||
) {
|
||||
let svg = Image::Vector {
|
||||
handle,
|
||||
color,
|
||||
bounds: bounds * transformation,
|
||||
rotation,
|
||||
opacity,
|
||||
};
|
||||
let svg = Image::Vector(svg, bounds * transformation);
|
||||
|
||||
self.images.push(svg);
|
||||
}
|
||||
|
|
@ -293,7 +286,7 @@ impl graphics::Layer for Layer {
|
|||
|
||||
fn flush(&mut self) {}
|
||||
|
||||
fn resize(&mut self, bounds: graphics::core::Rectangle) {
|
||||
fn resize(&mut self, bounds: Rectangle) {
|
||||
self.bounds = bounds;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -178,6 +178,16 @@ impl Renderer {
|
|||
engine::adjust_clip_mask(clip_mask, clip_bounds);
|
||||
}
|
||||
|
||||
for image in &layer.images {
|
||||
self.engine.draw_image(
|
||||
image,
|
||||
Transformation::scale(scale_factor),
|
||||
pixels,
|
||||
clip_mask,
|
||||
clip_bounds,
|
||||
);
|
||||
}
|
||||
|
||||
for group in &layer.text {
|
||||
for text in group.as_slice() {
|
||||
self.engine.draw_text(
|
||||
|
|
@ -190,16 +200,6 @@ impl Renderer {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
for image in &layer.images {
|
||||
self.engine.draw_image(
|
||||
image,
|
||||
Transformation::scale(scale_factor),
|
||||
pixels,
|
||||
clip_mask,
|
||||
clip_bounds,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if !overlay.is_empty() {
|
||||
|
|
@ -330,6 +330,7 @@ impl graphics::geometry::Renderer for Renderer {
|
|||
match geometry {
|
||||
Geometry::Live {
|
||||
primitives,
|
||||
images,
|
||||
text,
|
||||
clip_bounds,
|
||||
} => {
|
||||
|
|
@ -339,6 +340,10 @@ impl graphics::geometry::Renderer for Renderer {
|
|||
transformation,
|
||||
);
|
||||
|
||||
for image in images {
|
||||
layer.draw_image(image, transformation);
|
||||
}
|
||||
|
||||
layer.draw_text_group(text, clip_bounds, transformation);
|
||||
}
|
||||
Geometry::Cache(cache) => {
|
||||
|
|
@ -348,6 +353,10 @@ impl graphics::geometry::Renderer for Renderer {
|
|||
transformation,
|
||||
);
|
||||
|
||||
for image in cache.images.iter() {
|
||||
layer.draw_image(image.clone(), transformation);
|
||||
}
|
||||
|
||||
layer.draw_text_cache(
|
||||
cache.text,
|
||||
cache.clip_bounds,
|
||||
|
|
@ -372,23 +381,9 @@ impl core::image::Renderer for Renderer {
|
|||
self.engine.raster_pipeline.dimensions(handle)
|
||||
}
|
||||
|
||||
fn draw_image(
|
||||
&mut self,
|
||||
handle: Self::Handle,
|
||||
filter_method: core::image::FilterMethod,
|
||||
bounds: Rectangle,
|
||||
rotation: core::Radians,
|
||||
opacity: f32,
|
||||
) {
|
||||
fn draw_image(&mut self, image: core::Image, bounds: Rectangle) {
|
||||
let (layer, transformation) = self.layers.current_mut();
|
||||
layer.draw_image(
|
||||
handle,
|
||||
filter_method,
|
||||
bounds,
|
||||
transformation,
|
||||
rotation,
|
||||
opacity,
|
||||
);
|
||||
layer.draw_raster(image, bounds, transformation);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -401,23 +396,9 @@ impl core::svg::Renderer for Renderer {
|
|||
self.engine.vector_pipeline.viewport_dimensions(handle)
|
||||
}
|
||||
|
||||
fn draw_svg(
|
||||
&mut self,
|
||||
handle: core::svg::Handle,
|
||||
color: Option<Color>,
|
||||
bounds: Rectangle,
|
||||
rotation: core::Radians,
|
||||
opacity: f32,
|
||||
) {
|
||||
fn draw_svg(&mut self, svg: core::Svg, bounds: Rectangle) {
|
||||
let (layer, transformation) = self.layers.current_mut();
|
||||
layer.draw_svg(
|
||||
handle,
|
||||
color,
|
||||
bounds,
|
||||
transformation,
|
||||
rotation,
|
||||
opacity,
|
||||
);
|
||||
layer.draw_svg(svg, bounds, transformation);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ all-features = true
|
|||
[features]
|
||||
geometry = ["iced_graphics/geometry", "lyon"]
|
||||
image = ["iced_graphics/image"]
|
||||
svg = ["resvg/text"]
|
||||
svg = ["iced_graphics/svg", "resvg/text"]
|
||||
web-colors = ["iced_graphics/web-colors"]
|
||||
webgl = ["wgpu/webgl"]
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//! Build and draw geometry.
|
||||
use crate::core::text::LineHeight;
|
||||
use crate::core::{
|
||||
Pixels, Point, Radians, Rectangle, Size, Transformation, Vector,
|
||||
self, Pixels, Point, Radians, Rectangle, Size, Svg, Transformation, Vector,
|
||||
};
|
||||
use crate::graphics::cache::{self, Cached};
|
||||
use crate::graphics::color;
|
||||
|
|
@ -11,7 +11,7 @@ use crate::graphics::geometry::{
|
|||
};
|
||||
use crate::graphics::gradient::{self, Gradient};
|
||||
use crate::graphics::mesh::{self, Mesh};
|
||||
use crate::graphics::{self, Text};
|
||||
use crate::graphics::{Image, Text};
|
||||
use crate::text;
|
||||
use crate::triangle;
|
||||
|
||||
|
|
@ -19,16 +19,22 @@ use lyon::geom::euclid;
|
|||
use lyon::tessellation;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Geometry {
|
||||
Live { meshes: Vec<Mesh>, text: Vec<Text> },
|
||||
Live {
|
||||
meshes: Vec<Mesh>,
|
||||
images: Vec<Image>,
|
||||
text: Vec<Text>,
|
||||
},
|
||||
Cached(Cache),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Cache {
|
||||
pub meshes: Option<triangle::Cache>,
|
||||
pub images: Option<Arc<[Image]>>,
|
||||
pub text: Option<text::Cache>,
|
||||
}
|
||||
|
||||
|
|
@ -45,7 +51,17 @@ impl Cached for Geometry {
|
|||
previous: Option<Self::Cache>,
|
||||
) -> Self::Cache {
|
||||
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(cache) = &mut previous.meshes {
|
||||
cache.update(meshes);
|
||||
|
|
@ -59,10 +75,13 @@ impl Cached for Geometry {
|
|||
previous.text = text::Cache::new(group, text);
|
||||
}
|
||||
|
||||
previous.images = images;
|
||||
|
||||
previous
|
||||
} else {
|
||||
Cache {
|
||||
meshes: triangle::Cache::new(meshes),
|
||||
images,
|
||||
text: text::Cache::new(group, text),
|
||||
}
|
||||
}
|
||||
|
|
@ -78,6 +97,7 @@ pub struct Frame {
|
|||
clip_bounds: Rectangle,
|
||||
buffers: BufferStack,
|
||||
meshes: Vec<Mesh>,
|
||||
images: Vec<Image>,
|
||||
text: Vec<Text>,
|
||||
transforms: Transforms,
|
||||
fill_tessellator: tessellation::FillTessellator,
|
||||
|
|
@ -96,6 +116,7 @@ impl Frame {
|
|||
clip_bounds: bounds,
|
||||
buffers: BufferStack::new(),
|
||||
meshes: Vec::new(),
|
||||
images: Vec::new(),
|
||||
text: Vec::new(),
|
||||
transforms: Transforms {
|
||||
previous: Vec::new(),
|
||||
|
|
@ -270,7 +291,7 @@ impl geometry::frame::Backend for Frame {
|
|||
height: f32::INFINITY,
|
||||
};
|
||||
|
||||
self.text.push(graphics::Text::Cached {
|
||||
self.text.push(Text::Cached {
|
||||
content: text.content,
|
||||
bounds,
|
||||
color: text.color,
|
||||
|
|
@ -335,10 +356,11 @@ impl geometry::frame::Backend for Frame {
|
|||
Frame::with_clip(clip_bounds)
|
||||
}
|
||||
|
||||
fn paste(&mut self, frame: Frame, _at: Point) {
|
||||
fn paste(&mut self, frame: Frame) {
|
||||
self.meshes
|
||||
.extend(frame.buffers.into_meshes(frame.clip_bounds));
|
||||
|
||||
self.images.extend(frame.images);
|
||||
self.text.extend(frame.text);
|
||||
}
|
||||
|
||||
|
|
@ -348,9 +370,32 @@ impl geometry::frame::Backend for Frame {
|
|||
|
||||
Geometry::Live {
|
||||
meshes: self.meshes,
|
||||
images: self.images,
|
||||
text: self.text,
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_image(&mut self, bounds: Rectangle, image: impl Into<core::Image>) {
|
||||
let mut image = image.into();
|
||||
|
||||
let (bounds, external_rotation) =
|
||||
self.transforms.current.transform_rectangle(bounds);
|
||||
|
||||
image.rotation += external_rotation;
|
||||
|
||||
self.images.push(Image::Raster(image, bounds));
|
||||
}
|
||||
|
||||
fn draw_svg(&mut self, bounds: Rectangle, svg: impl Into<Svg>) {
|
||||
let mut svg = svg.into();
|
||||
|
||||
let (bounds, external_rotation) =
|
||||
self.transforms.current.transform_rectangle(bounds);
|
||||
|
||||
svg.rotation += external_rotation;
|
||||
|
||||
self.images.push(Image::Vector(svg, bounds));
|
||||
}
|
||||
}
|
||||
|
||||
enum Buffer {
|
||||
|
|
@ -518,6 +563,21 @@ impl Transform {
|
|||
|
||||
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 {
|
||||
gradient: gradient::Packed,
|
||||
|
|
|
|||
|
|
@ -149,6 +149,8 @@ impl Pipeline {
|
|||
6 => Float32x2,
|
||||
// Layer
|
||||
7 => Sint32,
|
||||
// Snap
|
||||
8 => Uint32,
|
||||
),
|
||||
}],
|
||||
},
|
||||
|
|
@ -212,31 +214,24 @@ impl Pipeline {
|
|||
transformation: Transformation,
|
||||
scale: f32,
|
||||
) {
|
||||
let transformation = transformation * Transformation::scale(scale);
|
||||
|
||||
let nearest_instances: &mut Vec<Instance> = &mut Vec::new();
|
||||
let linear_instances: &mut Vec<Instance> = &mut Vec::new();
|
||||
|
||||
for image in images {
|
||||
match &image {
|
||||
#[cfg(feature = "image")]
|
||||
Image::Raster {
|
||||
handle,
|
||||
filter_method,
|
||||
bounds,
|
||||
rotation,
|
||||
opacity,
|
||||
} => {
|
||||
Image::Raster(image, bounds) => {
|
||||
if let Some(atlas_entry) =
|
||||
cache.upload_raster(device, encoder, handle)
|
||||
cache.upload_raster(device, encoder, &image.handle)
|
||||
{
|
||||
add_instances(
|
||||
[bounds.x, bounds.y],
|
||||
[bounds.width, bounds.height],
|
||||
f32::from(*rotation),
|
||||
*opacity,
|
||||
f32::from(image.rotation),
|
||||
image.opacity,
|
||||
image.snap,
|
||||
atlas_entry,
|
||||
match filter_method {
|
||||
match image.filter_method {
|
||||
crate::core::image::FilterMethod::Nearest => {
|
||||
nearest_instances
|
||||
}
|
||||
|
|
@ -251,23 +246,23 @@ impl Pipeline {
|
|||
Image::Raster { .. } => {}
|
||||
|
||||
#[cfg(feature = "svg")]
|
||||
Image::Vector {
|
||||
handle,
|
||||
color,
|
||||
bounds,
|
||||
rotation,
|
||||
opacity,
|
||||
} => {
|
||||
Image::Vector(svg, bounds) => {
|
||||
let size = [bounds.width, bounds.height];
|
||||
|
||||
if let Some(atlas_entry) = cache.upload_vector(
|
||||
device, encoder, handle, *color, size, scale,
|
||||
device,
|
||||
encoder,
|
||||
&svg.handle,
|
||||
svg.color,
|
||||
size,
|
||||
scale,
|
||||
) {
|
||||
add_instances(
|
||||
[bounds.x, bounds.y],
|
||||
size,
|
||||
f32::from(*rotation),
|
||||
*opacity,
|
||||
f32::from(svg.rotation),
|
||||
svg.opacity,
|
||||
true,
|
||||
atlas_entry,
|
||||
nearest_instances,
|
||||
);
|
||||
|
|
@ -300,6 +295,7 @@ impl Pipeline {
|
|||
nearest_instances,
|
||||
linear_instances,
|
||||
transformation,
|
||||
scale,
|
||||
);
|
||||
|
||||
self.prepare_layer += 1;
|
||||
|
|
@ -375,9 +371,12 @@ impl Layer {
|
|||
nearest_instances: &[Instance],
|
||||
linear_instances: &[Instance],
|
||||
transformation: Transformation,
|
||||
scale_factor: f32,
|
||||
) {
|
||||
let uniforms = Uniforms {
|
||||
transform: transformation.into(),
|
||||
scale_factor,
|
||||
_padding: [0.0; 3],
|
||||
};
|
||||
|
||||
let bytes = bytemuck::bytes_of(&uniforms);
|
||||
|
|
@ -492,6 +491,7 @@ struct Instance {
|
|||
_position_in_atlas: [f32; 2],
|
||||
_size_in_atlas: [f32; 2],
|
||||
_layer: u32,
|
||||
_snap: u32,
|
||||
}
|
||||
|
||||
impl Instance {
|
||||
|
|
@ -502,6 +502,10 @@ impl Instance {
|
|||
#[derive(Debug, Clone, Copy, Zeroable, Pod)]
|
||||
struct Uniforms {
|
||||
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(
|
||||
|
|
@ -509,6 +513,7 @@ fn add_instances(
|
|||
image_size: [f32; 2],
|
||||
rotation: f32,
|
||||
opacity: f32,
|
||||
snap: bool,
|
||||
entry: &atlas::Entry,
|
||||
instances: &mut Vec<Instance>,
|
||||
) {
|
||||
|
|
@ -525,6 +530,7 @@ fn add_instances(
|
|||
image_size,
|
||||
rotation,
|
||||
opacity,
|
||||
snap,
|
||||
allocation,
|
||||
instances,
|
||||
);
|
||||
|
|
@ -554,8 +560,8 @@ fn add_instances(
|
|||
];
|
||||
|
||||
add_instance(
|
||||
position, center, size, rotation, opacity, allocation,
|
||||
instances,
|
||||
position, center, size, rotation, opacity, snap,
|
||||
allocation, instances,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -569,6 +575,7 @@ fn add_instance(
|
|||
size: [f32; 2],
|
||||
rotation: f32,
|
||||
opacity: f32,
|
||||
snap: bool,
|
||||
allocation: &atlas::Allocation,
|
||||
instances: &mut Vec<Instance>,
|
||||
) {
|
||||
|
|
@ -591,6 +598,7 @@ fn add_instance(
|
|||
(height as f32 - 1.0) / atlas::SIZE as f32,
|
||||
],
|
||||
_layer: layer as u32,
|
||||
_snap: snap as u32,
|
||||
};
|
||||
|
||||
instances.push(instance);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::core::{
|
||||
renderer, Background, Color, Point, Radians, Rectangle, Transformation,
|
||||
self, renderer, Background, Color, Point, Rectangle, Svg, Transformation,
|
||||
};
|
||||
use crate::graphics;
|
||||
use crate::graphics::color;
|
||||
|
|
@ -20,8 +20,8 @@ pub struct Layer {
|
|||
pub quads: quad::Batch,
|
||||
pub triangles: triangle::Batch,
|
||||
pub primitives: primitive::Batch,
|
||||
pub text: text::Batch,
|
||||
pub images: image::Batch,
|
||||
pub text: text::Batch,
|
||||
pending_meshes: Vec<Mesh>,
|
||||
pending_text: Vec<Text>,
|
||||
}
|
||||
|
|
@ -112,42 +112,35 @@ impl Layer {
|
|||
self.pending_text.push(text);
|
||||
}
|
||||
|
||||
pub fn draw_image(
|
||||
pub fn draw_image(&mut self, image: Image, transformation: Transformation) {
|
||||
match image {
|
||||
Image::Raster(image, bounds) => {
|
||||
self.draw_raster(image, bounds, transformation);
|
||||
}
|
||||
Image::Vector(svg, bounds) => {
|
||||
self.draw_svg(svg, bounds, transformation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw_raster(
|
||||
&mut self,
|
||||
handle: crate::core::image::Handle,
|
||||
filter_method: crate::core::image::FilterMethod,
|
||||
image: core::Image,
|
||||
bounds: Rectangle,
|
||||
transformation: Transformation,
|
||||
rotation: Radians,
|
||||
opacity: f32,
|
||||
) {
|
||||
let image = Image::Raster {
|
||||
handle,
|
||||
filter_method,
|
||||
bounds: bounds * transformation,
|
||||
rotation,
|
||||
opacity,
|
||||
};
|
||||
let image = Image::Raster(image, bounds * transformation);
|
||||
|
||||
self.images.push(image);
|
||||
}
|
||||
|
||||
pub fn draw_svg(
|
||||
&mut self,
|
||||
handle: crate::core::svg::Handle,
|
||||
color: Option<Color>,
|
||||
svg: Svg,
|
||||
bounds: Rectangle,
|
||||
transformation: Transformation,
|
||||
rotation: Radians,
|
||||
opacity: f32,
|
||||
) {
|
||||
let svg = Image::Vector {
|
||||
handle,
|
||||
color,
|
||||
bounds: bounds * transformation,
|
||||
rotation,
|
||||
opacity,
|
||||
};
|
||||
let svg = Image::Vector(svg, bounds * transformation);
|
||||
|
||||
self.images.push(svg);
|
||||
}
|
||||
|
|
|
|||
101
wgpu/src/lib.rs
101
wgpu/src/lib.rs
|
|
@ -182,19 +182,6 @@ impl Renderer {
|
|||
}
|
||||
}
|
||||
|
||||
if !layer.text.is_empty() {
|
||||
engine.text_pipeline.prepare(
|
||||
device,
|
||||
queue,
|
||||
&self.text_viewport,
|
||||
encoder,
|
||||
&mut self.text_storage,
|
||||
&layer.text,
|
||||
layer.bounds,
|
||||
Transformation::scale(scale_factor),
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "svg", feature = "image"))]
|
||||
if !layer.images.is_empty() {
|
||||
engine.image_pipeline.prepare(
|
||||
|
|
@ -207,6 +194,19 @@ impl Renderer {
|
|||
scale_factor,
|
||||
);
|
||||
}
|
||||
|
||||
if !layer.text.is_empty() {
|
||||
engine.text_pipeline.prepare(
|
||||
device,
|
||||
queue,
|
||||
&self.text_viewport,
|
||||
encoder,
|
||||
&mut self.text_storage,
|
||||
&layer.text,
|
||||
layer.bounds,
|
||||
Transformation::scale(scale_factor),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -359,17 +359,6 @@ impl Renderer {
|
|||
));
|
||||
}
|
||||
|
||||
if !layer.text.is_empty() {
|
||||
text_layer += engine.text_pipeline.render(
|
||||
&self.text_viewport,
|
||||
&self.text_storage,
|
||||
text_layer,
|
||||
&layer.text,
|
||||
scissor_rect,
|
||||
&mut render_pass,
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "svg", feature = "image"))]
|
||||
if !layer.images.is_empty() {
|
||||
engine.image_pipeline.render(
|
||||
|
|
@ -381,6 +370,17 @@ impl Renderer {
|
|||
|
||||
image_layer += 1;
|
||||
}
|
||||
|
||||
if !layer.text.is_empty() {
|
||||
text_layer += engine.text_pipeline.render(
|
||||
&self.text_viewport,
|
||||
&self.text_storage,
|
||||
text_layer,
|
||||
&layer.text,
|
||||
scissor_rect,
|
||||
&mut render_pass,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let _ = ManuallyDrop::into_inner(render_pass);
|
||||
|
|
@ -527,23 +527,9 @@ impl core::image::Renderer for Renderer {
|
|||
self.image_cache.borrow_mut().measure_image(handle)
|
||||
}
|
||||
|
||||
fn draw_image(
|
||||
&mut self,
|
||||
handle: Self::Handle,
|
||||
filter_method: core::image::FilterMethod,
|
||||
bounds: Rectangle,
|
||||
rotation: core::Radians,
|
||||
opacity: f32,
|
||||
) {
|
||||
fn draw_image(&mut self, image: core::Image, bounds: Rectangle) {
|
||||
let (layer, transformation) = self.layers.current_mut();
|
||||
layer.draw_image(
|
||||
handle,
|
||||
filter_method,
|
||||
bounds,
|
||||
transformation,
|
||||
rotation,
|
||||
opacity,
|
||||
);
|
||||
layer.draw_raster(image, bounds, transformation);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -553,23 +539,9 @@ impl core::svg::Renderer for Renderer {
|
|||
self.image_cache.borrow_mut().measure_svg(handle)
|
||||
}
|
||||
|
||||
fn draw_svg(
|
||||
&mut self,
|
||||
handle: core::svg::Handle,
|
||||
color_filter: Option<Color>,
|
||||
bounds: Rectangle,
|
||||
rotation: core::Radians,
|
||||
opacity: f32,
|
||||
) {
|
||||
fn draw_svg(&mut self, svg: core::Svg, bounds: Rectangle) {
|
||||
let (layer, transformation) = self.layers.current_mut();
|
||||
layer.draw_svg(
|
||||
handle,
|
||||
color_filter,
|
||||
bounds,
|
||||
transformation,
|
||||
rotation,
|
||||
opacity,
|
||||
);
|
||||
layer.draw_svg(svg, bounds, transformation);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -593,8 +565,17 @@ impl graphics::geometry::Renderer for Renderer {
|
|||
let (layer, transformation) = self.layers.current_mut();
|
||||
|
||||
match geometry {
|
||||
Geometry::Live { meshes, text } => {
|
||||
Geometry::Live {
|
||||
meshes,
|
||||
images,
|
||||
text,
|
||||
} => {
|
||||
layer.draw_mesh_group(meshes, transformation);
|
||||
|
||||
for image in images {
|
||||
layer.draw_image(image, transformation);
|
||||
}
|
||||
|
||||
layer.draw_text_group(text, transformation);
|
||||
}
|
||||
Geometry::Cached(cache) => {
|
||||
|
|
@ -602,6 +583,12 @@ impl graphics::geometry::Renderer for Renderer {
|
|||
layer.draw_mesh_cache(meshes, transformation);
|
||||
}
|
||||
|
||||
if let Some(images) = cache.images {
|
||||
for image in images.iter().cloned() {
|
||||
layer.draw_image(image, transformation);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(text) = cache.text {
|
||||
layer.draw_text_cache(text, transformation);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
struct Globals {
|
||||
transform: mat4x4<f32>,
|
||||
scale_factor: f32,
|
||||
}
|
||||
|
||||
@group(0) @binding(0) var<uniform> globals: Globals;
|
||||
|
|
@ -16,6 +17,7 @@ struct VertexInput {
|
|||
@location(5) atlas_pos: vec2<f32>,
|
||||
@location(6) atlas_scale: vec2<f32>,
|
||||
@location(7) layer: i32,
|
||||
@location(8) snap: u32,
|
||||
}
|
||||
|
||||
struct VertexOutput {
|
||||
|
|
@ -38,7 +40,7 @@ fn vs_main(input: VertexInput) -> VertexOutput {
|
|||
out.opacity = input.opacity;
|
||||
|
||||
// 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
|
||||
let cos_rot = cos(input.rotation);
|
||||
|
|
@ -51,7 +53,13 @@ fn vs_main(input: VertexInput) -> VertexOutput {
|
|||
);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ pub use program::Program;
|
|||
|
||||
pub use crate::graphics::cache::Group;
|
||||
pub use crate::graphics::geometry::{
|
||||
fill, gradient, path, stroke, Fill, Gradient, LineCap, LineDash, LineJoin,
|
||||
Path, Stroke, Style, Text,
|
||||
fill, gradient, path, stroke, Fill, Gradient, Image, LineCap, LineDash,
|
||||
LineJoin, Path, Stroke, Style, Text,
|
||||
};
|
||||
|
||||
use crate::core;
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ pub struct Image<Handle> {
|
|||
|
||||
impl<Handle> Image<Handle> {
|
||||
/// Creates a new [`Image`] with the given path.
|
||||
pub fn new<T: Into<Handle>>(handle: T) -> Self {
|
||||
pub fn new(handle: impl Into<Handle>) -> Self {
|
||||
Image {
|
||||
handle: handle.into(),
|
||||
width: Length::Shrink,
|
||||
|
|
@ -181,11 +181,14 @@ pub fn draw<Renderer, Handle>(
|
|||
|
||||
let render = |renderer: &mut Renderer| {
|
||||
renderer.draw_image(
|
||||
handle.clone(),
|
||||
filter_method,
|
||||
image::Image {
|
||||
handle: handle.clone(),
|
||||
filter_method,
|
||||
rotation: rotation.radians(),
|
||||
opacity,
|
||||
snap: true,
|
||||
},
|
||||
drawing_bounds,
|
||||
rotation.radians(),
|
||||
opacity,
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ use crate::core::mouse;
|
|||
use crate::core::renderer;
|
||||
use crate::core::widget::tree::{self, Tree};
|
||||
use crate::core::{
|
||||
Clipboard, ContentFit, Element, Layout, Length, Pixels, Point, Radians,
|
||||
Rectangle, Shell, Size, Vector, Widget,
|
||||
Clipboard, ContentFit, Element, Image, Layout, Length, Pixels, Point,
|
||||
Radians, Rectangle, Shell, Size, Vector, Widget,
|
||||
};
|
||||
|
||||
/// A frame that displays an image with the ability to zoom in/out and pan.
|
||||
|
|
@ -349,11 +349,14 @@ where
|
|||
let render = |renderer: &mut Renderer| {
|
||||
renderer.with_translation(translation, |renderer| {
|
||||
renderer.draw_image(
|
||||
self.handle.clone(),
|
||||
self.filter_method,
|
||||
Image {
|
||||
handle: self.handle.clone(),
|
||||
filter_method: self.filter_method,
|
||||
rotation: Radians(0.0),
|
||||
opacity: 1.0,
|
||||
snap: true,
|
||||
},
|
||||
drawing_bounds,
|
||||
Radians(0.0),
|
||||
1.0,
|
||||
);
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -211,11 +211,13 @@ where
|
|||
|
||||
let render = |renderer: &mut Renderer| {
|
||||
renderer.draw_svg(
|
||||
self.handle.clone(),
|
||||
style.color,
|
||||
svg::Svg {
|
||||
handle: self.handle.clone(),
|
||||
color: style.color,
|
||||
rotation: self.rotation.radians(),
|
||||
opacity: self.opacity,
|
||||
},
|
||||
drawing_bounds,
|
||||
self.rotation.radians(),
|
||||
self.opacity,
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue