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
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ criterion = "^0.4"
image = "^0"
imageproc = "^0.23"
rusttype = "^0.9"
lazy_static = "^1.4"

[[bench]]
path = "benches/invert.rs"
Expand Down
12 changes: 9 additions & 3 deletions src/encodings/webp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ impl WebPEncoder {
config.lossless = self.lossless as _;
config.quality = self.quality;

let res = libwebp::WebPEncode(std::ptr::addr_of!(config), std::ptr::addr_of_mut!(picture));
let res =
libwebp::WebPEncode(std::ptr::addr_of!(config), std::ptr::addr_of_mut!(picture));
if res == 0 {
free(picture);
return Err(Error::EncodingError("WebP encoding error".to_string()));
Expand Down Expand Up @@ -163,7 +164,9 @@ impl Encoder for WebPEncoder {
let mut final_image = std::mem::zeroed::<libwebp::WebPData>();
let mut encoded_frames = Vec::new();

let free = |mut final_image: libwebp::WebPData, encoded_frames: Vec<libwebp::WebPData>, mux: *mut libwebp::WebPMux| {
let free = |mut final_image: libwebp::WebPData,
encoded_frames: Vec<libwebp::WebPData>,
mux: *mut libwebp::WebPMux| {
libwebp::WebPDataClear(std::ptr::addr_of_mut!(final_image));
for mut f in encoded_frames {
libwebp::WebPDataClear(std::ptr::addr_of_mut!(f));
Expand Down Expand Up @@ -222,7 +225,10 @@ impl Encoder for WebPEncoder {
}
i32::MIN..=-5_i32 | 2_i32..=i32::MAX => {
free(final_image, encoded_frames, mux);
return Err(Error::EncodingError(format!("WebP mux error {}", mux_error)));
return Err(Error::EncodingError(format!(
"WebP mux error {}",
mux_error
)));
}
};

Expand Down
57 changes: 48 additions & 9 deletions src/image.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
#![allow(clippy::wildcard_imports)]

use crate::{
draw::Draw,
error::{
Error::{self, InvalidExtension},
Result,
},
pixel::*,
Dynamic, DynamicFrameIterator,
};
use crate::{draw::Draw, error::{
Error::{self, InvalidExtension},
Result,
}, pixel::*, Dynamic, DynamicFrameIterator, Paste};

#[cfg(feature = "gif")]
use crate::encodings::gif;
Expand Down Expand Up @@ -901,6 +896,50 @@ impl<P: Pixel> Image<P> {
self
}

/// Pads this image in place on each side with the given value.
///
/// # Panics
/// * The new width or height would exceed [`u32::MAX`].
/// * The padding on either side would exceed [`u32::MAX`].
pub fn pad(&mut self, x1: u32, y1: u32, x2: u32, y2: u32, padding: P) {
let h_padding = x1
.checked_add(x2)
.expect("new horizontal padding overflowed u32");
let v_padding = y1
.checked_add(y2)
.expect("new vertical padding overflowed u32");
// Strange syntax to avoid if let, as it would bump the MSRV
let new_width =
if let Some(new) = self.width().checked_add(h_padding) {new} else {
panic!("new width overflowed u32")
};
let new_height =
if let Some(new) = self.height().checked_add(v_padding) {new} else {
panic!("new height overflowed u32")
};
let mut output = Self::new(new_width, new_height, padding);
output.draw(
&Paste::new(self)
.with_overlay_mode(OverlayMode::Replace)
.with_position(x1, y1)
);
// Only copy some fields, to preserve metadata
self.data = output.data;
// These both are guaranteed to be non-zero as long as the values didn't overflow,
// which we checked for earlier
unsafe {
self.width = NonZeroU32::new_unchecked(new_width);
self.height = NonZeroU32::new_unchecked(new_height);
}
}

/// Takes this image and pads it on each side. Useful for method chaining.
#[must_use]
pub fn padded(mut self, x1: u32, y1: u32, x2: u32, y2: u32, padding: P) -> Self {
self.pad(x1, y1, x2, y2, padding);
self
}

/// Mirrors, or flips this image horizontally (about the y-axis) in place.
pub fn mirror(&mut self) {
let width = self.width();
Expand Down
Binary file added tests/sample.bin
Binary file not shown.
43 changes: 43 additions & 0 deletions tests/test_pad.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use lazy_static::lazy_static;
use ril::prelude::*;

lazy_static! {
static ref TEST_IMAGE: Image<Rgba> = Image::from_fn(
256, 256, |x, y|
Rgba::new(x as u8, y as u8, 255, 255)
);
}

//noinspection RsAssertEqual
#[test]
fn test_padding() -> ril::Result<()> {
let mut image = TEST_IMAGE.clone();
image.pad(64, 32, 64, 32, Default::default());
assert_eq!(image.dimensions(), (384, 320));
// Using include_bytes here to prevent having to test with a feature enabled
let bytes = image.data
.iter().flat_map(|pixel| pixel.as_bytes())
.collect::<Box<[u8]>>();
// Not using assert_eq here, as it causes a gigantic error message
// with the representations of each value
assert!(
bytes.as_ref() == include_bytes!("sample.bin"),
"padded image was not identical to sample"
);

Ok(())
}

#[test]
#[should_panic(expected = "width overflowed")]
fn test_overflow_width_check() {
let mut image: Image<Rgba> = TEST_IMAGE.clone();
image.pad(u32::MAX, 0, 0, 0, Default::default());
}

#[test]
#[should_panic(expected = "padding overflowed")]
fn test_overflow_pad_check() {
let mut image: Image<Rgba> = TEST_IMAGE.clone();
image.pad(u32::MAX, 0, 1, 0, Default::default());
}