Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Display multiple images implementation, some minor fixes #307

Merged
merged 26 commits into from Jan 22, 2019
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
b5b931c
updating dependencies and features and added window module
arthmis Jan 8, 2019
e9d20ef
added entire field for sdl2
arthmis Jan 8, 2019
eaaf59f
fixed some issues in cargo.toml and removed some useless code in wind…
arthmis Jan 8, 2019
7c7ea87
fixed typo in toml
arthmis Jan 8, 2019
1ca16d8
made dispay_image public
arthmis Jan 8, 2019
9bea1e0
tidied up window module and cargo.toml
arthmis Jan 8, 2019
b9ba803
display_image can now only take positive window dimension arguments
arthmis Jan 8, 2019
cf75fef
fixed some type issues and logical errors with window dimensions in e…
arthmis Jan 8, 2019
2714ada
added cfg for feature display-window
arthmis Jan 8, 2019
090d561
added some doc comments, code comments, and cleaned up some unnecessa…
arthmis Jan 8, 2019
a4d75f2
fixed comment concerning get_output_image
arthmis Jan 8, 2019
8dc6fc9
accidentally erased canvas.present(), now added it back to resize event
arthmis Jan 9, 2019
f567d4f
trying to simplify the code
arthmis Jan 10, 2019
4ffa6d3
fixing some problems
arthmis Jan 10, 2019
d49dbf6
fixed a brackets issue
arthmis Jan 10, 2019
cfbd0f5
fixed up the code
arthmis Jan 10, 2019
9661f14
added suggested review fixes
arthmis Jan 13, 2019
e67b3eb
ran rustfmt
arthmis Jan 13, 2019
87039b2
mentioned minimum window size in docs
arthmis Jan 13, 2019
74c81de
adopted changes from upstream
arthmis Jan 15, 2019
5c3cff2
attempting to display multiple windows, currently giving signa SIGSEG…
arthmis Jan 16, 2019
e56f099
display multiple images
arthmis Jan 21, 2019
e81301a
implemented display_image using display_multiple_images
arthmis Jan 21, 2019
ebe4c86
added suggested fixes, renamed multiple_image_displays.rs to display_…
arthmis Jan 22, 2019
008f4ac
removed multiple_image_displays.rs
arthmis Jan 22, 2019
1bf46ca
updated doc comments for display_multiple_images.rs
arthmis Jan 22, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/display_image.rs
Expand Up @@ -21,7 +21,7 @@ fn main() {
.expect("No image found at provided path")
.to_rgba();

display_image("", &image, 10, 10);
display_image("", &image, 500, 500);
}

#[cfg(not(feature = "display-window"))]
Expand Down
41 changes: 41 additions & 0 deletions examples/display_multiple_images.rs
@@ -0,0 +1,41 @@
//! An example of displaying an image in a window using the display_image function.
arthmis marked this conversation as resolved.
Show resolved Hide resolved
//! Run this example from your root directory, enabled the display_image feature and
//! provide a path to an image file as an argument.
//!
//! `cargo run --release --features display-window --example display_image examples/wrench.jpg`

#[cfg(feature = "display-window")]
fn main() {
use imageproc::window::display_multiple_images;
use std::env;

let first_image_path = match env::args().nth(1) {
Some(path) => path,
None => {
println!("No path provided for first image. Using default image.");
"examples/wrench.jpg".to_owned()
}
};

let second_image_path = match env::args().nth(2) {
Some(path) => path,
None => {
println!("No path provided for second image. Using default image.");
"examples/empire-state-building.jpg".to_owned()
}
};

let first_image = image::open(&first_image_path)
.expect("No image found at provided path")
.to_rgba();
let second_image = image::open(&second_image_path)
.expect("No image found at provided path")
.to_rgba();

display_multiple_images("", &vec![&first_image, &second_image], 500, 500);
}

#[cfg(not(feature = "display-window"))]
fn main() {
panic!("Displaying images is only supported if the display-window feature is enabled.");
}
Binary file added examples/empire-state-building.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
41 changes: 41 additions & 0 deletions examples/multiple_image_displays.rs
@@ -0,0 +1,41 @@
//! An example of displaying an image in a window using the display_image function.
arthmis marked this conversation as resolved.
Show resolved Hide resolved
//! Run this example from your root directory, enabled the display_image feature and
//! provide a path to an image file as an argument.
//!
//! `cargo run --release --features display-window --example display_image examples/wrench.jpg`

#[cfg(feature = "display-window")]
fn main() {
use imageproc::window::display_multiple_images;
use std::env;

let first_image_path = match env::args().nth(1) {
Some(path) => path,
None => {
println!("No path provided for first image. Using default image.");
"examples/wrench.jpg".to_owned()
}
};

let second_image_path = match env::args().nth(2) {
Some(path) => path,
None => {
println!("No path provided for second image. Using default image.");
"examples/empire-state-building.jpg".to_owned()
}
};

let first_image = image::open(&first_image_path)
.expect("No image found at provided path")
.to_rgba();
let second_image = image::open(&second_image_path)
.expect("No image found at provided path")
.to_rgba();

display_multiple_images("", &vec![&first_image, &second_image], 500, 500);
}

#[cfg(not(feature = "display-window"))]
fn main() {
panic!("Displaying images is only supported if the display-window feature is enabled.");
}
217 changes: 173 additions & 44 deletions src/window.rs
@@ -1,19 +1,38 @@
//! Displays an image in a window created by sdl2.

use image::{RgbaImage, imageops::resize};
use image::{imageops::resize, RgbaImage};
use sdl2::{
event::{Event, WindowEvent},
keyboard::Keycode,
pixels::{Color, PixelFormatEnum},
rect::Rect,
surface::Surface
render::{Canvas, TextureCreator, WindowCanvas},
surface::Surface,
video::{Window, WindowContext},
};

/// Displays the provided RGBA image in a new window.
///
/// The minimum window width or height is 150 pixels - input values less than this
/// will be rounded up to the minimum.
pub fn display_image(title: &str, image: &RgbaImage, window_width: u32, window_height: u32) {
display_multiple_images(title, &vec![image], window_width, window_height);
}

/// Displays the provided RGBA images in new windows.
///
/// The minimum window width or height is 150 pixels - input values less than this
/// will be rounded up to the minimum.
pub fn display_multiple_images(
title: &str,
images: &[&RgbaImage],
window_width: u32,
window_height: u32,
) {
if images.len() == 0 {
return;
}

// Enforce minimum window size
const MIN_WINDOW_DIMENSION: u32 = 150;
let window_width = window_width.max(MIN_WINDOW_DIMENSION);
Expand All @@ -23,53 +42,118 @@ pub fn display_image(title: &str, image: &RgbaImage, window_width: u32, window_h
let sdl = sdl2::init().expect("couldn't create sdl2 context");
let video_subsystem = sdl.video().expect("couldn't create video subsystem");

let mut window = video_subsystem
.window(title, window_width, window_height)
.position_centered()
.resizable()
.build()
.expect("couldn't create window");
let mut windows: Vec<Window> = Vec::with_capacity(images.len());
let mut window_visibility: Vec<bool> = Vec::with_capacity(images.len());
for _ in 0..images.len() {
let mut window = video_subsystem
.window(title, window_width, window_height)
.resizable()
.allow_highdpi()
.build()
.expect("couldn't create window");

arthmis marked this conversation as resolved.
Show resolved Hide resolved
window
.set_minimum_size(MIN_WINDOW_DIMENSION, MIN_WINDOW_DIMENSION)
.expect("invalid minimum size for window");

windows.push(window);
window_visibility.push(true);
}

window
.set_minimum_size(MIN_WINDOW_DIMENSION, MIN_WINDOW_DIMENSION)
.expect("invalid minimum size for window");
{
use sdl2::video::WindowPos::Positioned;

let (base_position_x, base_position_y) = windows[0].position();
for (i, window) in windows.iter_mut().enumerate() {
let multiplier = 1.0 + i as f32 / 20.0;
window.set_position(
Positioned((base_position_x as f32 * multiplier) as i32),
Positioned(base_position_y),
);

let (window_pos_x, _window_pos_y) = window.position();
let display_bounds = video_subsystem
.display_bounds(0)
.expect("No bounds found for that display.");
let screen_width = display_bounds.w;
if window_pos_x + window_width as i32 > screen_width {
window.set_position(
Positioned(screen_width - window_width as i32),
Positioned(base_position_y),
);
}
}
}

let mut canvas = window.into_canvas()
.build()
.expect("couldn't create canvas");
let mut canvases: Vec<WindowCanvas> = Vec::with_capacity(images.len());
for window in windows.into_iter() {
let canvas = window
.into_canvas()
.software()
.build()
.expect("couldn't create canvas");
canvases.push(canvas);
}

let texture_creator = canvas.texture_creator();
let mut texture_creators: Vec<TextureCreator<WindowContext>> = Vec::with_capacity(images.len());
for canvas in canvases.iter() {
let texture_creator = canvas.texture_creator();
texture_creators.push(texture_creator);
}

// Shrinks input image to fit if required and renders to the sdl canvas
let mut render_image_to_canvas = |image, window_width, window_height| {
let scaled_image = resize_to_fit(image, window_width, window_height);
let (image_width, image_height) = scaled_image.dimensions();

let mut buffer = scaled_image.into_raw();
const CHANNEL_COUNT: u32 = 4;

let surface = Surface::from_data(
&mut buffer,
image_width,
image_height,
image_width * CHANNEL_COUNT,
PixelFormatEnum::ABGR8888 // sdl2 expects bits from highest to lowest
).expect("couldn't create surface");

let texture = texture_creator
.create_texture_from_surface(surface)
.expect("couldn't create texture from surface");

canvas.set_draw_color(Color::RGB(255, 255, 255));
canvas.clear();

let left = ((window_width - image_width) as f32 / 2f32) as i32;
let top = ((window_height - image_height) as f32 / 2f32) as i32;
canvas.copy(&texture, None, Rect::new(left, top, image_width, image_height)).unwrap();
canvas.present();
};
let render_image_to_canvas =
|image,
window_width,
window_height,
canvas: &mut Canvas<Window>,
texture_creator: &TextureCreator<WindowContext>| {
let scaled_image = resize_to_fit(image, window_width, window_height);

let (image_width, image_height) = scaled_image.dimensions();
let mut buffer = scaled_image.into_raw();
const CHANNEL_COUNT: u32 = 4;
let surface = Surface::from_data(
&mut buffer,
image_width,
image_height,
image_width * CHANNEL_COUNT,
PixelFormatEnum::ABGR8888, // sdl2 expects bits from highest to lowest
)
.expect("couldn't create surface");

let texture = texture_creator
.create_texture_from_surface(surface)
.expect("couldn't create texture from surface");

canvas.set_draw_color(Color::RGB(255, 255, 255));
canvas.clear();

let left = ((window_width - image_width) as f32 / 2f32) as i32;
let top = ((window_height - image_height) as f32 / 2f32) as i32;
canvas
.copy(
&texture,
None,
Rect::new(left, top, image_width, image_height),
)
.unwrap();
canvas.present();
};

for (i, (canvas, texture_creator)) in
canvases.iter_mut().zip(texture_creators.iter()).enumerate()
{
render_image_to_canvas(
images[i],
window_width,
window_height,
canvas,
texture_creator,
);
}

render_image_to_canvas(image, window_width, window_height);
let mut hidden_count = 0;

// Create and start event loop to keep window open until Esc
let mut event_pump = sdl.event_pump().unwrap();
Expand All @@ -82,11 +166,56 @@ pub fn display_image(title: &str, image: &RgbaImage, window_width: u32, window_h
keycode: Some(Keycode::Escape),
..
} => break 'running,
Event::KeyDown {
keycode: Some(Keycode::Q),
window_id,
..
} => {
for (i, canvas) in canvases.iter_mut().enumerate() {
if window_id == canvas.window().id() {
canvas.window_mut().hide();
window_visibility[i] = false;
hidden_count += 1;
}
if hidden_count == images.len() {
break 'running;
}
}
}
Event::Window {
win_event: WindowEvent::Close,
window_id,
..
} => {
for (i, canvas) in canvases.iter_mut().enumerate() {
if window_id == canvas.window().id() {
canvas.window_mut().hide();
window_visibility[i] = false;
hidden_count += 1;
}
if hidden_count == images.len() {
break 'running;
}
}
}
Event::Window {
win_event: WindowEvent::Resized(w, h),
window_id,
..
} => {
render_image_to_canvas(image, w as u32, h as u32);
for (i, (canvas, texture_creator)) in
canvases.iter_mut().zip(texture_creators.iter()).enumerate()
{
if window_id == canvas.window().id() {
render_image_to_canvas(
images[i],
w as u32,
h as u32,
canvas,
texture_creator,
);
}
}
}
_ => {}
}
Expand Down