Skip to content

Commit

Permalink
Fixed implementation of TypedCroppedImage, TypedCroppedImageMut, …
Browse files Browse the repository at this point in the history
…`CroppedImage` and `CroppedImageMut`.
  • Loading branch information
Cykooz committed May 13, 2024
1 parent ee2ec47 commit f87f80e
Show file tree
Hide file tree
Showing 9 changed files with 413 additions and 96 deletions.
12 changes: 9 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,22 @@

- Added Gaussian filter for convolution algorithm.
- Method `PixelType::size()` was made public.
- Added new image containers:
- `ImageRef`
- `TypedImageRef`
- `TypedImage`
- `TypedCroppedImage`
- `TypedCroppedImageMut`
- `CroppedImage`
- `CroppedImageMut`

### Fixed

- Fixed dividing image by alpha channel.

### Changed

A lot of breaking changes have been done in this release:
A lot of **breaking changes** have been done in this release:

- Structures `ImageView` and `ImageViewMut` have been removed. They always
did unnecessary memory allocation to store references to image rows.
Expand Down Expand Up @@ -40,8 +48,6 @@ A lot of breaking changes have been done in this release:
Now you can create and use zero-sized images.
- `Image` (embedded implementation of image container) moved from root of
the crate into module `images`.
- Added new image containers: `ImageRef`, `TypedImageRef`, `TypedImage`,
`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)
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ codegen-units = 1

[profile.test]
opt-level = 3
incremental = true


[package.metadata.release]
Expand Down
137 changes: 62 additions & 75 deletions src/images/cropped_image.rs
Original file line number Diff line number Diff line change
@@ -1,73 +1,37 @@
use crate::{CropBoxError, ImageView, ImageViewMut};
use crate::images::{check_crop_box, TypedCroppedImage, TypedCroppedImageMut};
use crate::{
CropBoxError, ImageView, ImageViewMut, IntoImageView, IntoImageViewMut, PixelTrait, PixelType,
};

fn check_crop_box(
image_view: &impl ImageView,
/// It is a wrapper that provides [IntoImageView] for part of wrapped image.
pub struct CroppedImage<'a, V: IntoImageView> {
image: &'a V,
left: u32,
top: u32,
width: u32,
height: u32,
) -> Result<(), CropBoxError> {
let img_width = image_view.width();
let img_height = image_view.height();

if left >= img_width || top >= img_height {
return Err(CropBoxError::PositionIsOutOfImageBoundaries);
}
let right = left + width;
let bottom = top + height;
if right > img_width || bottom > img_height {
return Err(CropBoxError::SizeIsOutOfImageBoundaries);
}
Ok(())
}

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 {
self.width
}

fn height(&self) -> u32 {
self.height
}

fn iter_rows(&self, start_row: u32) -> impl Iterator<Item = &[Self::Pixel]> {
let left = self.left as usize;
let right = left + self.width as usize;
self.image_view
.iter_rows(self.top + start_row)
.take((self.height - start_row) as usize)
// SAFETY: correct values of the left and the right
// are guaranteed by new() method.
.map(move |row| unsafe { row.get_unchecked(left..right) })
}
}
};
}

/// It is a typed wrapper that provides [ImageView] for part of wrapped image.
pub struct TypedCroppedImage<'a, V: ImageView> {
image_view: &'a V,
/// It is a wrapper that provides [IntoImageView] and [IntoImageViewMut] for part of wrapped image.
pub struct CroppedImageMut<'a, V: IntoImageView> {
image: &'a mut V,
left: u32,
top: u32,
width: u32,
height: u32,
}

impl<'a, V: ImageView> TypedCroppedImage<'a, V> {
impl<'a, V: IntoImageView> CroppedImage<'a, V> {
pub fn new(
image_view: &'a V,
image: &'a V,
left: u32,
top: u32,
width: u32,
height: u32,
) -> Result<Self, CropBoxError> {
check_crop_box(image_view, left, top, width, height)?;
check_crop_box(image.width(), image.height(), left, top, width, height)?;
Ok(Self {
image_view,
image,
left,
top,
width,
Expand All @@ -76,27 +40,17 @@ impl<'a, V: ImageView> TypedCroppedImage<'a, V> {
}
}

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> {
impl<'a, V: IntoImageView> CroppedImageMut<'a, V> {
pub fn new(
image_view: &'a mut V,
image: &'a mut V,
left: u32,
top: u32,
width: u32,
height: u32,
) -> Result<Self, CropBoxError> {
check_crop_box(image_view, left, top, width, height)?;
check_crop_box(image.width(), image.height(), left, top, width, height)?;
Ok(Self {
image_view,
image,
left,
top,
width,
Expand All @@ -105,17 +59,50 @@ impl<'a, V: ImageViewMut> TypedCroppedImageMut<'a, V> {
}
}

image_view_impl!(TypedCroppedImageMut<ImageViewMut>);
impl<'a, V: IntoImageView> IntoImageView for CroppedImage<'a, V> {
fn pixel_type(&self) -> Option<PixelType> {
self.image.pixel_type()
}

fn width(&self) -> u32 {
self.width
}

fn height(&self) -> u32 {
self.height
}

fn image_view<P: PixelTrait>(&self) -> Option<impl ImageView<Pixel = P>> {
self.image.image_view().map(|v| {
TypedCroppedImage::new(v, self.left, self.top, self.width, self.height).unwrap()
})
}
}

impl<'a, V: IntoImageView> IntoImageView for CroppedImageMut<'a, V> {
fn pixel_type(&self) -> Option<PixelType> {
self.image.pixel_type()
}

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;
self.image_view
.iter_rows_mut(self.top + start_row)
.take((self.height - start_row) as usize)
// SAFETY: correct values of the left and the right
// are guaranteed by new() method.
.map(move |row| unsafe { row.get_unchecked_mut(left..right) })
fn width(&self) -> u32 {
self.width
}

fn height(&self) -> u32 {
self.height
}

fn image_view<P: PixelTrait>(&self) -> Option<impl ImageView<Pixel = P>> {
self.image.image_view().map(|v| {
TypedCroppedImage::new(v, self.left, self.top, self.width, self.height).unwrap()
})
}
}

impl<'a, V: IntoImageViewMut> IntoImageViewMut for CroppedImageMut<'a, V> {
fn image_view_mut<P: PixelTrait>(&mut self) -> Option<impl ImageViewMut<Pixel = P>> {
self.image.image_view_mut().map(|v| {
TypedCroppedImageMut::new(v, self.left, self.top, self.width, self.height).unwrap()
})
}
}
18 changes: 15 additions & 3 deletions src/images/dyn_image.rs → src/images/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,18 @@ impl<'a> ImageRef<'a> {
})
}

pub fn from_pixels<P: PixelTrait>(
width: u32,
height: u32,
pixels: &'a [P],
) -> Result<Self, ImageBufferError> {
let (head, buffer, _) = unsafe { pixels.align_to::<u8>() };
if !head.is_empty() {
return Err(ImageBufferError::InvalidBufferAlignment);
}
Self::new(width, height, buffer, P::pixel_type())
}

#[inline]
pub fn pixel_type(&self) -> PixelType {
self.pixel_type
Expand Down Expand Up @@ -86,7 +98,7 @@ impl<'a> IntoImageView for ImageRef<'a> {
self.height
}

fn image_view<P: InnerPixel>(&self) -> Option<impl ImageView<Pixel = P>> {
fn image_view<P: PixelTrait>(&self) -> Option<impl ImageView<Pixel = P>> {
self.typed_image()
}
}
Expand Down Expand Up @@ -210,7 +222,7 @@ impl<'a> Image<'a> {
}
}

/// Get typed version of the image.
/// Get the typed version of the image.
pub fn typed_image<P: InnerPixel>(&self) -> Option<TypedImageRef<P>> {
if P::pixel_type() != self.pixel_type {
return None;
Expand All @@ -220,7 +232,7 @@ impl<'a> Image<'a> {
Some(typed_image)
}

/// Get typed mutable version of the image.
/// Get the typed mutable version of the image.
pub fn typed_image_mut<P: InnerPixel>(&mut self) -> Option<TypedImage<P>> {
if P::pixel_type() != self.pixel_type {
return None;
Expand Down
7 changes: 3 additions & 4 deletions src/images/image_crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ use image::DynamicImage;

use crate::image_view::try_pixel_type;
use crate::images::{TypedImage, TypedImageRef};
use crate::pixels::InnerPixel;
use crate::{ImageView, ImageViewMut, IntoImageView, IntoImageViewMut, PixelType};
use crate::{ImageView, ImageViewMut, IntoImageView, IntoImageViewMut, PixelTrait, PixelType};

impl IntoImageView for DynamicImage {
fn pixel_type(&self) -> Option<PixelType> {
Expand All @@ -31,7 +30,7 @@ impl IntoImageView for DynamicImage {
self.height()
}

fn image_view<P: InnerPixel>(&self) -> Option<impl ImageView<Pixel = P>> {
fn image_view<P: PixelTrait>(&self) -> Option<impl ImageView<Pixel = P>> {
if let Ok(pixel_type) = try_pixel_type(self) {
if P::pixel_type() == pixel_type {
return TypedImageRef::<P>::from_buffer(
Expand All @@ -47,7 +46,7 @@ impl IntoImageView for DynamicImage {
}

impl IntoImageViewMut for DynamicImage {
fn image_view_mut<P: InnerPixel>(&mut self) -> Option<impl ImageViewMut<Pixel = P>> {
fn image_view_mut<P: PixelTrait>(&mut self) -> Option<impl ImageViewMut<Pixel = P>> {
if let Ok(pixel_type) = try_pixel_type(self) {
if P::pixel_type() == pixel_type {
return TypedImage::<P>::from_buffer(
Expand Down
6 changes: 4 additions & 2 deletions src/images/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
use std::fmt::Debug;

pub use cropped_image::*;
pub use dyn_image::*;
pub use image::*;
pub use typed_cropped_image::*;
pub use typed_image::*;

mod cropped_image;
mod dyn_image;
mod image;
mod typed_cropped_image;
mod typed_image;

#[cfg(feature = "image")]
Expand Down
Loading

0 comments on commit f87f80e

Please sign in to comment.