Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ members = [".", "examples/split-chart"]
[dependencies]
plotters = { version = "0.3", default-features = false }
plotters-backend = "0.3"
iced_widget = { version = "0.13", features = ["canvas"] }
iced_widget = { version = "0.13", features = ["canvas", "image"] }
iced_graphics = "0.13"
once_cell = "1"

Expand All @@ -29,11 +29,13 @@ plotters = { version = "0.3", default-features = false, features = [
"area_series",
"line_series",
"point_series",
"bitmap_backend"
] }
iced = { version = "0.13", features = ["canvas", "tokio"] }
iced = { version = "0.13", features = ["canvas", "tokio", "image"] }
chrono = { version = "0.4", default-features = false }
rand = "0.8"
tokio = { version = "1", features = ["rt"], default-features = false }
image = "0.25.2"

[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
sysinfo = { version = "0.30", default-features = false }
sysinfo = { version = "0.30", default-features = false }
137 changes: 137 additions & 0 deletions examples/image.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
extern crate iced;
extern crate plotters;
extern crate rand;
extern crate tokio;

use std::{fs::File, io::BufReader};

use iced::{
widget::{
canvas::{Cache, Frame, Geometry},
Column, Container, Text,
},
Alignment, Element, Length, Size, Task,
};

use image::{DynamicImage, GenericImageView, ImageFormat};
use plotters::prelude::ChartBuilder;
use plotters_backend::DrawingBackend;
use plotters_iced::{Chart, ChartWidget, Renderer};

fn main() {
iced::application("Image Example", State::update, State::view)
.antialiasing(true)
.run_with(State::new)
.unwrap();
}

#[derive(Debug)]
enum Message {
ImageLoaded(DynamicImage),
}

struct State {
chart: Option<ExampleChart>,
}

impl State {
fn new() -> (Self, Task<Message>) {
(
Self { chart: None },
Task::batch([Task::perform(
tokio::task::spawn_blocking(load_image),
|data| Message::ImageLoaded(data.unwrap()),
)]),
)
}

fn update(&mut self, message: Message) -> Task<Message> {
match message {
Message::ImageLoaded(data) => {
self.chart = Some(ExampleChart::new(data));
Task::none()
}
}
}

fn view(&self) -> Element<'_, Message> {
let content = Column::new()
.spacing(20)
.align_x(Alignment::Start)
.width(Length::Fill)
.height(Length::Fill)
.push(match self.chart {
Some(ref chart) => chart.view(),
None => Text::new("Loading...").into(),
});

Container::new(content)
.padding(5)
.center_x(Length::Fill)
.center_y(Length::Fill)
.into()
}
}

struct ExampleChart {
cache: Cache,
image: DynamicImage,
}

impl ExampleChart {
fn new(image: DynamicImage) -> Self {
Self {
cache: Cache::new(),
image,
}
}

fn view(&self) -> Element<Message> {
let chart = ChartWidget::new(self)
.width(Length::Fill)
.height(Length::Fill);

chart.into()
}
}

impl Chart<Message> for ExampleChart {
type State = ();

#[inline]
fn draw<R: Renderer, F: Fn(&mut Frame)>(
&self,
renderer: &R,
bounds: Size,
draw_fn: F,
) -> Geometry {
renderer.draw_cache(&self.cache, bounds, draw_fn)
}

fn build_chart<DB: DrawingBackend>(&self, _state: &Self::State, mut builder: ChartBuilder<DB>) {
use plotters::prelude::*;

let mut chart = builder
.build_cartesian_2d(0.0..1.0, 0.0..1.0)
.expect("failed to build chart");

let (w, h) = self.image.dimensions();
let bitmap = BitMapElement::with_ref((0.5, 0.5), (w, h), self.image.as_rgba8().unwrap());

chart
.draw_series(bitmap)
.expect("failed to draw chart data");
}
}

fn load_image() -> DynamicImage {
let file_name = format!("./examples/images/rustacean-orig-noshadow.png");

let image = image::load(
BufReader::new(File::open(file_name).expect("couldn't open file")),
ImageFormat::Png,
)
.expect("loading image failed");

return image;
}
Binary file added examples/images/rustacean-orig-noshadow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 11 additions & 7 deletions src/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
// Copyright: 2022, Joylei <leingliu@gmail.com>
// License: MIT

use std::collections::HashSet;

use iced_graphics::core::text::Paragraph;
use iced_widget::core::Rectangle;
use iced_widget::{
canvas,
core::{
alignment::{Horizontal, Vertical},
font, text, Font, Size,
},
image,
text::Shaping,
};
use once_cell::unsync::Lazy;
Expand All @@ -28,6 +28,7 @@ use plotters_backend::{
FontFamily,
FontStyle,
};
use std::collections::HashSet;

use crate::error::Error;
use crate::utils::{cvt_color, cvt_stroke, CvtPoint};
Expand Down Expand Up @@ -293,13 +294,16 @@ where
#[inline]
fn blit_bitmap(
&mut self,
_pos: BackendCoord,
(_iw, _ih): (u32, u32),
_src: &[u8],
(x, y): BackendCoord,
(iw, ih): (u32, u32),
src: &[u8],
) -> Result<(), DrawingErrorKind<Self::ErrorType>> {
// Not supported yet (rendering ignored)
// Notice: currently Iced has limitations, because widgets are not rendered in the order of creation, and different primitives go to different render pipelines.
let image = image::Handle::from_rgba(iw, ih, src.to_owned());

let pos = (x - iw as i32 / 2, y - ih as i32 / 2) as BackendCoord;

let bounds = Rectangle::new(pos.cvt_point(), Size::new(iw as f32, ih as f32));
self.frame.draw_image(bounds, &image);
Ok(())
}
}
Expand Down