- Renamed CroppedImage into TypedCroppedImage.

- Renamed `CroppedImageMut` into `TypedCroppedImageMut`.
This commit is contained in:
Kirill Kuzminykh
2024-05-07 00:10:14 +03:00
parent 568af7f889
commit ee2ec47e3c
7 changed files with 157 additions and 73 deletions
+1 -1
View File
@@ -41,7 +41,7 @@ A lot of breaking changes have been done in this release:
- `Image` (embedded implementation of image container) moved from root of
the crate into module `images`.
- Added new image containers: `ImageRef`, `TypedImageRef`, `TypedImage`,
`CroppedImage` and `CroppedImageMut`.
`TypedCroppedImage` and `TypedCroppedImageMut`.
- Added optional feature "image".
It adds implementation of traits `IntoImageView` and `IntoImageViewMut` for the
[DynamicImage](https://docs.rs/image/latest/image/enum.DynamicImage.html)
+2 -2
View File
@@ -8,8 +8,8 @@ pub enum ImageError {
}
#[derive(Error, Debug, Clone, Copy)]
#[error("Size of slice with pixels is smaller than required")]
pub struct InvalidPixelsSliceSize;
#[error("Size of container with pixels is smaller than required")]
pub struct InvalidPixelsSize;
#[derive(Error, Debug, Clone, Copy, PartialEq, Eq)]
pub enum ImageBufferError {
+62 -40
View File
@@ -21,37 +21,9 @@ fn check_crop_box(
Ok(())
}
macro_rules! cropped_image_impl {
($wrapper_name:ident<$view_trait:ident>, $doc:expr) => {
#[doc = $doc]
pub struct $wrapper_name<V: $view_trait + Sized> {
image_view: V,
left: u32,
top: u32,
width: u32,
height: u32,
}
impl<V: $view_trait + Sized> $wrapper_name<V> {
pub fn new(
image_view: V,
left: u32,
top: u32,
width: u32,
height: u32,
) -> Result<Self, CropBoxError> {
check_crop_box(&image_view, left, top, width, height)?;
Ok(Self {
image_view,
left,
top,
width,
height,
})
}
}
unsafe impl<V: $view_trait> ImageView for $wrapper_name<V> {
macro_rules! image_view_impl {
($wrapper_name:ident<$view_trait:ident>) => {
unsafe impl<'a, V: $view_trait> ImageView for $wrapper_name<'a, V> {
type Pixel = V::Pixel;
fn width(&self) -> u32 {
@@ -76,16 +48,66 @@ macro_rules! cropped_image_impl {
};
}
cropped_image_impl!(
CroppedImage<ImageView>,
"It is wrapper that provides [ImageView] for part of wrapped image."
);
cropped_image_impl!(
CroppedImageMut<ImageViewMut>,
"It is wrapper that provides [ImageViewMut] for part of wrapped image."
);
/// It is a typed wrapper that provides [ImageView] for part of wrapped image.
pub struct TypedCroppedImage<'a, V: ImageView> {
image_view: &'a V,
left: u32,
top: u32,
width: u32,
height: u32,
}
unsafe impl<V: ImageViewMut> ImageViewMut for CroppedImageMut<V> {
impl<'a, V: ImageView> TypedCroppedImage<'a, V> {
pub fn new(
image_view: &'a V,
left: u32,
top: u32,
width: u32,
height: u32,
) -> Result<Self, CropBoxError> {
check_crop_box(image_view, left, top, width, height)?;
Ok(Self {
image_view,
left,
top,
width,
height,
})
}
}
image_view_impl!(TypedCroppedImage<ImageView>);
/// It is a typed wrapper that provides [ImageView] and [ImageViewMut] for part of wrapped image.
pub struct TypedCroppedImageMut<'a, V: ImageViewMut> {
image_view: &'a mut V,
left: u32,
top: u32,
width: u32,
height: u32,
}
impl<'a, V: ImageViewMut> TypedCroppedImageMut<'a, V> {
pub fn new(
image_view: &'a mut V,
left: u32,
top: u32,
width: u32,
height: u32,
) -> Result<Self, CropBoxError> {
check_crop_box(image_view, left, top, width, height)?;
Ok(Self {
image_view,
left,
top,
width,
height,
})
}
}
image_view_impl!(TypedCroppedImageMut<ImageViewMut>);
unsafe impl<'a, V: ImageViewMut> ImageViewMut for TypedCroppedImageMut<'a, V> {
fn iter_rows_mut(&mut self, start_row: u32) -> impl Iterator<Item = &mut [Self::Pixel]> {
let left = self.left as usize;
let right = left + self.width as usize;
+28 -7
View File
@@ -2,7 +2,7 @@ use std::fmt::Debug;
use crate::images::BufferContainer;
use crate::pixels::InnerPixel;
use crate::{ImageBufferError, ImageView, ImageViewMut, InvalidPixelsSliceSize};
use crate::{ImageBufferError, ImageView, ImageViewMut, InvalidPixelsSize};
/// Generic reference to image data that provides [ImageView].
#[derive(Debug)]
@@ -13,10 +13,10 @@ pub struct TypedImageRef<'a, P> {
}
impl<'a, P> TypedImageRef<'a, P> {
pub fn new(width: u32, height: u32, pixels: &'a [P]) -> Result<Self, InvalidPixelsSliceSize> {
pub fn new(width: u32, height: u32, pixels: &'a [P]) -> Result<Self, InvalidPixelsSize> {
let pixels_count = width as usize * height as usize;
if pixels.len() < pixels_count {
return Err(InvalidPixelsSliceSize);
return Err(InvalidPixelsSize);
}
Ok(Self {
width,
@@ -105,14 +105,26 @@ impl<P: Default + Copy + Debug> TypedImage<'static, P> {
}
impl<'a, P: InnerPixel> TypedImage<'a, P> {
pub fn from_pixels(
pub fn from_pixels(width: u32, height: u32, pixels: Vec<P>) -> Result<Self, InvalidPixelsSize> {
let pixels_count = width as usize * height as usize;
if pixels.len() < pixels_count {
return Err(InvalidPixelsSize);
}
Ok(Self {
width,
height,
pixels: BufferContainer::Owned(pixels),
})
}
pub fn from_pixels_slice(
width: u32,
height: u32,
pixels: &'a mut [P],
) -> Result<Self, InvalidPixelsSliceSize> {
) -> Result<Self, InvalidPixelsSize> {
let pixels_count = width as usize * height as usize;
if pixels.len() < pixels_count {
return Err(InvalidPixelsSliceSize);
return Err(InvalidPixelsSize);
}
Ok(Self {
width,
@@ -131,7 +143,16 @@ impl<'a, P: InnerPixel> TypedImage<'a, P> {
return Err(ImageBufferError::InvalidBufferSize);
}
let pixels = align_buffer_to_mut(buffer)?;
Self::from_pixels(width, height, pixels).map_err(|_| ImageBufferError::InvalidBufferSize)
Self::from_pixels_slice(width, height, pixels)
.map_err(|_| ImageBufferError::InvalidBufferSize)
}
pub fn pixels(&self) -> &[P] {
self.pixels.borrow()
}
pub fn pixels_mut(&mut self) -> &mut [P] {
self.pixels.borrow_mut()
}
}
+1 -1
View File
@@ -508,7 +508,7 @@ fn get_temp_image_from_buffer<P: PixelTrait>(
buffer.resize(buf_size, 0);
}
let pixels = unsafe { buffer.align_to_mut::<P>().1 };
TypedImage::from_pixels(width, height, &mut pixels[0..pixels_count]).unwrap()
TypedImage::from_pixels_slice(width, height, &mut pixels[0..pixels_count]).unwrap()
}
fn resample_nearest<P: InnerPixel>(
+2 -2
View File
@@ -84,7 +84,7 @@ fn mul_div_alpha_test<P: PixelTrait>(
let mut dst_pixels = src_pixels.clone();
let src_image = TypedImageRef::new(width, height, &src_pixels).unwrap();
let mut dst_image = TypedImage::from_pixels(width, height, &mut dst_pixels).unwrap();
let mut dst_image = TypedImage::from_pixels_slice(width, height, &mut dst_pixels).unwrap();
let mut alpha_mul_div: MulDiv = Default::default();
unsafe {
@@ -125,7 +125,7 @@ fn mul_div_alpha_test<P: PixelTrait>(
// Inplace
let mut src_pixels_clone = src_pixels.clone();
let mut image = TypedImage::from_pixels(width, height, &mut src_pixels_clone).unwrap();
let mut image = TypedImage::from_pixels_slice(width, height, &mut src_pixels_clone).unwrap();
match oper {
Oper::Mul => alpha_mul_div
+61 -20
View File
@@ -1,5 +1,7 @@
use fast_image_resize as fr;
use fast_image_resize::images::{CroppedImageMut, Image, ImageRef, TypedImage, TypedImageRef};
use fast_image_resize::images::{
Image, ImageRef, TypedCroppedImage, TypedCroppedImageMut, TypedImage, TypedImageRef,
};
use fast_image_resize::pixels::{U8x4, U8};
use fast_image_resize::{ImageView, ResizeOptions};
@@ -46,7 +48,7 @@ fn create_type_image_ref_from_small_buffer() {
let buffer = vec![U8::new(0); 64 * 30];
let res = TypedImageRef::<U8>::new(width, height, &buffer);
assert!(matches!(res, Err(fr::InvalidPixelsSliceSize)));
assert!(matches!(res, Err(fr::InvalidPixelsSize)));
}
#[test]
@@ -76,16 +78,55 @@ fn create_typed_image_from_big_buffer() {
}
#[test]
fn crop_view_mut() {
// White source image
let src_image =
Image::from_vec_u8(64, 32, vec![255; 64 * 32 * 4], fr::PixelType::U8x4).unwrap();
let src_image = src_image.typed_image::<U8x4>().unwrap();
// Black destination image
let mut dst_image = Image::new(64, 32, fr::PixelType::U8x4);
fn typed_cropped_image() {
const BLACK: U8x4 = U8x4::new([0; 4]);
const WHITE: U8x4 = U8x4::new([255; 4]);
let mut cropped_dst_image =
CroppedImageMut::new(dst_image.typed_image_mut::<U8x4>().unwrap(), 10, 10, 44, 12).unwrap();
let mut source_pixels = Vec::with_capacity(64 * 64);
source_pixels.extend((0..64 * 64).map(|i| {
let y = i / 64;
if (10..54).contains(&y) {
let x = i % 64;
if (10..54).contains(&x) {
return WHITE;
}
}
BLACK
}));
// Black source image with white square inside
let src_image = TypedImage::<U8x4>::from_pixels(64, 64, source_pixels).unwrap();
// Black destination image
let mut dst_image = TypedImage::<U8x4>::new(40, 40);
let cropped_src_image = TypedCroppedImage::new(&src_image, 10, 10, 44, 44).unwrap();
assert_eq!(cropped_src_image.width(), 44);
assert_eq!(cropped_src_image.height(), 44);
let mut resizer = fr::Resizer::new();
resizer
.resize_typed(
&cropped_src_image,
&mut dst_image,
&ResizeOptions::new().resize_alg(fr::ResizeAlg::Nearest),
)
.unwrap();
let white_block = vec![WHITE; 40 * 40];
assert_eq!(dst_image.pixels(), white_block);
}
#[test]
fn typed_cropped_image_mut() {
const BLACK: U8x4 = U8x4::new([0; 4]);
const WHITE: U8x4 = U8x4::new([255; 4]);
// White source image
let src_image = TypedImage::from_pixels(64, 32, vec![WHITE; 64 * 32]).unwrap();
// Black destination image
let mut dst_image = TypedImage::<U8x4>::new(64, 32);
let mut cropped_dst_image = TypedCroppedImageMut::new(&mut dst_image, 10, 10, 44, 12).unwrap();
assert_eq!(cropped_dst_image.width(), 44);
assert_eq!(cropped_dst_image.height(), 12);
@@ -98,21 +139,21 @@ fn crop_view_mut() {
)
.unwrap();
let row_size: usize = 64 * 4;
let dst_buffer = dst_image.buffer();
let dst_pixels = dst_image.pixels();
let black_block = vec![0u8; 10 * row_size];
let row_size: usize = 64;
let black_block = vec![BLACK; 10 * row_size];
// Top border
assert_eq!(dst_buffer[0..10 * row_size], black_block);
assert_eq!(dst_pixels[0..10 * row_size], black_block);
// Middle rows
let mut middle_row = vec![0u8; 10 * 4];
middle_row.extend(vec![255u8; 44 * 4]);
middle_row.extend(vec![0u8; 10 * 4]);
for row in dst_buffer.chunks_exact(row_size).skip(10 * 4).take(12 * 4) {
let mut middle_row = vec![BLACK; 10];
middle_row.extend(vec![WHITE; 44]);
middle_row.extend(vec![BLACK; 10]);
for row in dst_pixels.chunks_exact(row_size).skip(10).take(12) {
assert_eq!(row, middle_row);
}
// Bottom border
assert_eq!(dst_buffer[22 * row_size..], black_block);
assert_eq!(dst_pixels[22 * row_size..], black_block);
}