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

Upstream from master #1131

Merged
merged 14 commits into from Feb 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions CHANGES.md
Expand Up @@ -56,6 +56,12 @@ version.
- Introduced `PixelDensity` and `PixelDensityUnit` to store DPI information in
formats that support encoding this form of metadata (e.g. in `jpeg`).

### Version 0.22.5

- Added `GenericImage::copy_within`, specialized for `ImageBuffer`
- Fixed decoding of interlaced `gif` files
- Prepare for future compatibility of array `IntoIterator` in example code

### Version 0.22.4

- Added in-place variants for flip and rotate operations.
Expand Down
2 changes: 1 addition & 1 deletion examples/scaledown/main.rs
Expand Up @@ -34,7 +34,7 @@ fn main() {
("cmr", FilterType::CatmullRom),
("gauss", FilterType::Gaussian),
("lcz2", FilterType::Lanczos3),
].into_iter()
].iter()
{
let timer = Instant::now();
let scaled = img.resize(400, 400, filter);
Expand Down
2 changes: 1 addition & 1 deletion examples/scaleup/main.rs
Expand Up @@ -34,7 +34,7 @@ fn main() {
("xcmr", FilterType::CatmullRom),
("ygauss", FilterType::Gaussian),
("zlcz2", FilterType::Lanczos3),
].into_iter()
].iter()
{
let timer = Instant::now();
let scaled = tiny.resize(32, 32, filter);
Expand Down
143 changes: 142 additions & 1 deletion src/buffer.rs
Expand Up @@ -9,6 +9,7 @@ use crate::flat::{FlatSamples, SampleLayout};
use crate::dynimage::{save_buffer, save_buffer_with_format};
use crate::error::ImageResult;
use crate::image::{GenericImage, GenericImageView, ImageFormat};
use crate::math::Rect;
use crate::traits::{EncodableLayout, Primitive};
use crate::utils::expand_packed;

Expand Down Expand Up @@ -923,11 +924,61 @@ where
self.get_pixel_mut(x, y).blend(&p)
}

fn copy_within(&mut self, source: Rect, x: u32, y: u32) -> bool {
let Rect { x: sx, y: sy, width, height } = source;
let dx = x;
let dy = y;
assert!(sx < self.width() && dx < self.width());
assert!(sy < self.height() && dy < self.height());
if self.width() - dx.max(sx) < width || self.height() - dy.max(sy) < height {
return false;
}

if sy < dy {
for y in (0..height).rev() {
let sy = sy + y;
let dy = dy + y;
let Range { start, .. } = self.pixel_indices_unchecked(sx, sy);
let Range { end, .. } = self.pixel_indices_unchecked(sx + width - 1, sy);
let dst = self.pixel_indices_unchecked(dx, dy).start;
slice_copy_within(self, start..end, dst);
}
} else {
for y in 0..height {
let sy = sy + y;
let dy = dy + y;
let Range { start, .. } = self.pixel_indices_unchecked(sx, sy);
let Range { end, .. } = self.pixel_indices_unchecked(sx + width - 1, sy);
let dst = self.pixel_indices_unchecked(dx, dy).start;
slice_copy_within(self, start..end, dst);
}
}
true
}

fn inner_mut(&mut self) -> &mut Self::InnerImage {
self
}
}

// FIXME non-generic `core::slice::copy_within` implementation used by `ImageBuffer::copy_within`. The implementation is rewritten
// here due to minimum rust version support(MSRV). Image has a MSRV of 1.34 as of writing this while `core::slice::copy_within`
// has been stabilized in 1.37.
#[inline(always)]
fn slice_copy_within<T: Copy>(slice: &mut [T], Range { start: src_start, end: src_end }: Range<usize>, dest: usize) {
assert!(src_start <= src_end, "src end is before src start");
assert!(src_end <= slice.len(), "src is out of bounds");
let count = src_end - src_start;
assert!(dest <= slice.len() - count, "dest is out of bounds");
unsafe {
std::ptr::copy(
slice.as_ptr().add(src_start),
slice.as_mut_ptr().add(dest),
count,
);
}
}

// concrete implementation for `Vec`-backed buffers
// TODO: I think that rustc does not "see" this impl any more: the impl with
// Container meets the same requirements. At least, I got compile errors that
Expand Down Expand Up @@ -1091,8 +1142,10 @@ pub(crate) type GrayAlpha16Image = ImageBuffer<LumaA<u16>, Vec<u16>>;
#[cfg(test)]
mod test {

use super::{ImageBuffer, RgbImage};
use super::{GrayImage, ImageBuffer, RgbImage};
use crate::image::GenericImage;
use crate::color;
use crate::math::Rect;
#[cfg(feature = "benchmarks")]
use test;

Expand Down Expand Up @@ -1204,4 +1257,92 @@ mod test {

b.bytes = 1000 * 1000 * 3;
}

#[test]
fn test_image_buffer_copy_within_oob() {
let mut image: GrayImage = ImageBuffer::from_raw(4, 4, vec![0u8; 16]).unwrap();
assert!(!image.copy_within(Rect { x: 0, y: 0, width: 5, height: 4 }, 0, 0));
assert!(!image.copy_within(Rect { x: 0, y: 0, width: 4, height: 5 }, 0, 0));
assert!(!image.copy_within(Rect { x: 1, y: 0, width: 4, height: 4 }, 0, 0));
assert!(!image.copy_within(Rect { x: 0, y: 0, width: 4, height: 4 }, 1, 0));
assert!(!image.copy_within(Rect { x: 0, y: 1, width: 4, height: 4 }, 0, 0));
assert!(!image.copy_within(Rect { x: 0, y: 0, width: 4, height: 4 }, 0, 1));
assert!(!image.copy_within(Rect { x: 1, y: 1, width: 4, height: 4 }, 0, 0));
}

#[test]
fn test_image_buffer_copy_within_tl() {
let data = &[
00, 01, 02, 03,
04, 05, 06, 07,
08, 09, 10, 11,
12, 13, 14, 15
];
let expected = [
00, 01, 02, 03,
04, 00, 01, 02,
08, 04, 05, 06,
12, 08, 09, 10,
];
let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap();
assert!(image.copy_within(Rect { x: 0, y: 0, width: 3, height: 3 }, 1, 1));
assert_eq!(&image.into_raw(), &expected);
}

#[test]
fn test_image_buffer_copy_within_tr() {
let data = &[
00, 01, 02, 03,
04, 05, 06, 07,
08, 09, 10, 11,
12, 13, 14, 15
];
let expected = [
00, 01, 02, 03,
01, 02, 03, 07,
05, 06, 07, 11,
09, 10, 11, 15
];
let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap();
assert!(image.copy_within(Rect { x: 1, y: 0, width: 3, height: 3 }, 0, 1));
assert_eq!(&image.into_raw(), &expected);
}

#[test]
fn test_image_buffer_copy_within_bl() {
let data = &[
00, 01, 02, 03,
04, 05, 06, 07,
08, 09, 10, 11,
12, 13, 14, 15
];
let expected = [
00, 04, 05, 06,
04, 08, 09, 10,
08, 12, 13, 14,
12, 13, 14, 15
];
let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap();
assert!(image.copy_within(Rect { x: 0, y: 1, width: 3, height: 3 }, 1, 0));
assert_eq!(&image.into_raw(), &expected);
}

#[test]
fn test_image_buffer_copy_within_br() {
let data = &[
00, 01, 02, 03,
04, 05, 06, 07,
08, 09, 10, 11,
12, 13, 14, 15
];
let expected = [
05, 06, 07, 03,
09, 10, 11, 07,
13, 14, 15, 11,
12, 13, 14, 15
];
let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap();
assert!(image.copy_within(Rect { x: 1, y: 1, width: 3, height: 3 }, 0, 0));
assert_eq!(&image.into_raw(), &expected);
}
}
2 changes: 1 addition & 1 deletion src/gif.rs
Expand Up @@ -200,7 +200,7 @@ impl<R: Read> Iterator for GifFrameIterator<R> {
}

let mut vec = vec![0; self.reader.buffer_size()];
if let Err(err) = self.reader.fill_buffer(&mut vec) {
if let Err(err) = self.reader.read_into_buffer(&mut vec) {
return Some(Err(ImageError::from_gif(err)));
}

Expand Down
140 changes: 137 additions & 3 deletions src/image.rs
Expand Up @@ -2,12 +2,13 @@
use std::convert::TryFrom;
use std::io;
use std::io::Read;
use std::path::Path;
use std::ops::{Deref, DerefMut};
use std::path::Path;

use crate::buffer::{ImageBuffer, Pixel};
use crate::color::{ColorType, ExtendedColorType};
use crate::error::{ImageError, ImageResult};
use crate::math::Rect;

use crate::animation::Frames;

Expand Down Expand Up @@ -640,6 +641,48 @@ pub trait GenericImage: GenericImageView {
Ok(())
}

/// Copies all of the pixels from one part of this image to another part of this image.
///
/// The destination rectangle of the copy is specified with the top-left corner placed at (x, y).
///
/// # Returns
/// `true` if the copy was successful, `false` if the image could not
/// be copied due to size constraints.
fn copy_within(&mut self, source: Rect, x: u32, y: u32) -> bool {
let Rect { x: sx, y: sy, width, height } = source;
let dx = x;
let dy = y;
assert!(sx < self.width() && dx < self.width());
assert!(sy < self.height() && dy < self.height());
if self.width() - dx.max(sx) < width || self.height() - dy.max(sy) < height {
return false;
}
// since `.rev()` creates a new dype we would either have to go with dynamic dispatch for the ranges
// or have quite a lot of code bloat. A macro gives us static dispatch with less visible bloat.
macro_rules! copy_within_impl_ {
($xiter:expr, $yiter:expr) => {
for y in $yiter {
let sy = sy + y;
let dy = dy + y;
for x in $xiter {
let sx = sx + x;
let dx = dx + x;
let pixel = self.get_pixel(sx, sy);
self.put_pixel(dx, dy, pixel);
}
}
};
}
// check how target and source rectangles relate to each other so we dont overwrite data before we copied it.
match (sx < dx, sy < dy) {
(true, true) => copy_within_impl_!((0..width).rev(), (0..height).rev()),
(true, false) => copy_within_impl_!((0..width).rev(), 0..height),
(false, true) => copy_within_impl_!(0..width, (0..height).rev()),
(false, false) => copy_within_impl_!(0..width, 0..height),
}
true
}

/// Returns a mutable reference to the underlying image.
fn inner_mut(&mut self) -> &mut Self::InnerImage;

Expand Down Expand Up @@ -794,8 +837,9 @@ mod tests {
use std::path::Path;

use super::{ColorType, ImageDecoder, ImageResult, GenericImage, GenericImageView, load_rect, ImageFormat};
use crate::buffer::ImageBuffer;
use crate::buffer::{GrayImage, ImageBuffer};
use crate::color::Rgba;
use crate::math::Rect;

#[test]
/// Test that alpha blending works as expected
Expand Down Expand Up @@ -887,7 +931,9 @@ mod tests {
}
fn read_scanline(m: &mut MockDecoder, buf: &mut [u8]) -> io::Result<usize> {
let bytes_read = m.scanline_number * m.scanline_bytes;
if bytes_read >= 25 { return Ok(0); }
if bytes_read >= 25 {
return Ok(0);
}

let len = m.scanline_bytes.min(25 - bytes_read);
buf[..(len as usize)].copy_from_slice(&DATA[(bytes_read as usize)..][..(len as usize)]);
Expand Down Expand Up @@ -951,4 +997,92 @@ mod tests {
assert!(from_path("./a.txt").is_err());
assert!(from_path("./a").is_err());
}

#[test]
fn test_generic_image_copy_within_oob() {
let mut image: GrayImage = ImageBuffer::from_raw(4, 4, vec![0u8; 16]).unwrap();
assert!(!image.sub_image(0, 0, 4, 4).copy_within(Rect { x: 0, y: 0, width: 5, height: 4 }, 0, 0));
assert!(!image.sub_image(0, 0, 4, 4).copy_within(Rect { x: 0, y: 0, width: 4, height: 5 }, 0, 0));
assert!(!image.sub_image(0, 0, 4, 4).copy_within(Rect { x: 1, y: 0, width: 4, height: 4 }, 0, 0));
assert!(!image.sub_image(0, 0, 4, 4).copy_within(Rect { x: 0, y: 0, width: 4, height: 4 }, 1, 0));
assert!(!image.sub_image(0, 0, 4, 4).copy_within(Rect { x: 0, y: 1, width: 4, height: 4 }, 0, 0));
assert!(!image.sub_image(0, 0, 4, 4).copy_within(Rect { x: 0, y: 0, width: 4, height: 4 }, 0, 1));
assert!(!image.sub_image(0, 0, 4, 4).copy_within(Rect { x: 1, y: 1, width: 4, height: 4 }, 0, 0));
}

#[test]
fn test_generic_image_copy_within_tl() {
let data = &[
00, 01, 02, 03,
04, 05, 06, 07,
08, 09, 10, 11,
12, 13, 14, 15
];
let expected = [
00, 01, 02, 03,
04, 00, 01, 02,
08, 04, 05, 06,
12, 08, 09, 10,
];
let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap();
assert!(image.sub_image(0, 0, 4, 4).copy_within(Rect { x: 0, y: 0, width: 3, height: 3 }, 1, 1));
assert_eq!(&image.into_raw(), &expected);
}

#[test]
fn test_generic_image_copy_within_tr() {
let data = &[
00, 01, 02, 03,
04, 05, 06, 07,
08, 09, 10, 11,
12, 13, 14, 15
];
let expected = [
00, 01, 02, 03,
01, 02, 03, 07,
05, 06, 07, 11,
09, 10, 11, 15
];
let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap();
assert!(image.sub_image(0, 0, 4, 4).copy_within(Rect { x: 1, y: 0, width: 3, height: 3 }, 0, 1));
assert_eq!(&image.into_raw(), &expected);
}

#[test]
fn test_generic_image_copy_within_bl() {
let data = &[
00, 01, 02, 03,
04, 05, 06, 07,
08, 09, 10, 11,
12, 13, 14, 15
];
let expected = [
00, 04, 05, 06,
04, 08, 09, 10,
08, 12, 13, 14,
12, 13, 14, 15
];
let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap();
assert!(image.sub_image(0, 0, 4, 4).copy_within(Rect { x: 0, y: 1, width: 3, height: 3 }, 1, 0));
assert_eq!(&image.into_raw(), &expected);
}

#[test]
fn test_generic_image_copy_within_br() {
let data = &[
00, 01, 02, 03,
04, 05, 06, 07,
08, 09, 10, 11,
12, 13, 14, 15
];
let expected = [
05, 06, 07, 03,
09, 10, 11, 07,
13, 14, 15, 11,
12, 13, 14, 15
];
let mut image: GrayImage = ImageBuffer::from_raw(4, 4, Vec::from(&data[..])).unwrap();
assert!(image.sub_image(0, 0, 4, 4).copy_within(Rect { x: 1, y: 1, width: 3, height: 3 }, 0, 0));
assert_eq!(&image.into_raw(), &expected);
}
}