Skip to content

Commit

Permalink
Merge pull request #1131 from HeroicKatora/upstream-master
Browse files Browse the repository at this point in the history
Upstream from master
  • Loading branch information
HeroicKatora committed Feb 7, 2020
2 parents 9dd1c44 + e3ab70d commit 140f4ea
Show file tree
Hide file tree
Showing 10 changed files with 303 additions and 7 deletions.
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);
}
}

0 comments on commit 140f4ea

Please sign in to comment.