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
21 changes: 2 additions & 19 deletions src/imageops/fast_blur.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use num_traits::Bounded;

use crate::imageops::filter_1d::{SafeAdd, SafeMul};
use crate::primitive_sealed::NearestFrom;
use crate::{ImageBuffer, Pixel, Primitive};

/// Approximation of Gaussian blur.
Expand Down Expand Up @@ -136,26 +137,8 @@ fn ceil_to_odd(x: usize) -> usize {
}

#[inline]
#[allow(clippy::manual_clamp)]
fn rounding_saturating_mul<T: Primitive>(v: f32, w: f32) -> T {
// T::DEFAULT_MAX_VALUE is equal to 1.0 only in cases where storage type if `f32/f64`,
// that means it should be safe to round here.
if T::DEFAULT_MAX_VALUE.to_f32().unwrap() != 1.0 {
T::from(
(v * w)
.round()
.min(T::DEFAULT_MAX_VALUE.to_f32().unwrap())
.max(T::DEFAULT_MIN_VALUE.to_f32().unwrap()),
)
.unwrap()
} else {
T::from(
(v * w)
.min(T::DEFAULT_MAX_VALUE.to_f32().unwrap())
.max(T::DEFAULT_MIN_VALUE.to_f32().unwrap()),
)
.unwrap()
}
NearestFrom::nearest_from(v * w)
}

fn box_blur_horizontal_pass_strategy<T, P: Primitive>(
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ mod images;
/// This is going to be internal.
pub mod io;
pub mod metadata;
mod primitive_sealed;
//TODO delete this module after a few releases
mod traits;
mod utils;
Expand Down
74 changes: 74 additions & 0 deletions src/primitive_sealed.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//! Module for crate-private traits implemented for all primitive types.

/// Crate-private trait to seal the [`Primitive`](crate::Primitive) trait.
///
/// This trait is `pub` but not exported, so it cannot be implemented outside
/// this crate.
#[allow(private_bounds)]
pub trait PrimitiveSealed: Sized + NearestFrom<f32> {}

impl PrimitiveSealed for usize {}
impl PrimitiveSealed for u8 {}
impl PrimitiveSealed for u16 {}
impl PrimitiveSealed for u32 {}
impl PrimitiveSealed for u64 {}
impl PrimitiveSealed for isize {}
impl PrimitiveSealed for i8 {}
impl PrimitiveSealed for i16 {}
impl PrimitiveSealed for i32 {}
impl PrimitiveSealed for i64 {}
impl PrimitiveSealed for f32 {}
impl PrimitiveSealed for f64 {}

/// Returns the nearest value of `Self` to a given value.
///
/// Properties:
/// - For a float -> int conversion:
/// - The float is rounded to the nearest integer.
/// (An implementation may use a fast approximation instead of a precise rounding method.)
/// - NaN is mapped to 0.
/// - Values outside the range of the integer type are clamped to the min or max value.
/// - For a float -> float conversion:
/// - The float is clamped to the range `[0.0, 1.0]`.
/// - NaN is mapped to 0.0.
pub(crate) trait NearestFrom<T> {
fn nearest_from(value: T) -> Self;
}

impl NearestFrom<f32> for u8 {
fn nearest_from(value: f32) -> Self {
// Approximate rounding using the well-known + 0.5 trick.
// This does not handle certain cases correctly. E.g. `0.5_f32.nextdown()`
// is incorrectly rounded to 1 instead of 0. However, this isn't typically
// an issue in practice.
(value + 0.5) as u8
}
}
impl NearestFrom<f32> for u16 {
fn nearest_from(value: f32) -> Self {
(value + 0.5) as u16
}
}
impl NearestFrom<f32> for f32 {
#[allow(clippy::manual_clamp)] // to map NaN to 0.0
fn nearest_from(value: f32) -> Self {
value.max(0.0).min(1.0)
}
}
impl NearestFrom<f32> for f64 {
#[allow(clippy::manual_clamp)] // to map NaN to 0.0
fn nearest_from(value: f32) -> Self {
value.max(0.0).min(1.0) as f64
}
}

macro_rules! impl_nearest_from_f32_for_ints {
($($t:ty),+) => { $(
impl NearestFrom<f32> for $t {
fn nearest_from(value: f32) -> Self {
value.round() as $t
}
}
)+ };
}
impl_nearest_from_f32_for_ints!(u32, u64, usize, i8, i16, i32, i64, isize);
20 changes: 2 additions & 18 deletions src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use num_traits::{Bounded, Num, NumCast};
use std::ops::AddAssign;

use crate::color::{Luma, LumaA, Rgb, Rgba};
use crate::primitive_sealed::PrimitiveSealed;
use crate::ExtendedColorType;

/// Types which are safe to treat as an immutable byte slice in a pixel layout
Expand Down Expand Up @@ -33,27 +34,10 @@ impl EncodableLayout for [f32] {
}
}

mod sealed {
pub trait PrimitiveSealed: Sized {}
}

impl sealed::PrimitiveSealed for usize {}
impl sealed::PrimitiveSealed for u8 {}
impl sealed::PrimitiveSealed for u16 {}
impl sealed::PrimitiveSealed for u32 {}
impl sealed::PrimitiveSealed for u64 {}
impl sealed::PrimitiveSealed for isize {}
impl sealed::PrimitiveSealed for i8 {}
impl sealed::PrimitiveSealed for i16 {}
impl sealed::PrimitiveSealed for i32 {}
impl sealed::PrimitiveSealed for i64 {}
impl sealed::PrimitiveSealed for f32 {}
impl sealed::PrimitiveSealed for f64 {}

/// The type of each channel in a pixel. For example, this can be `u8`, `u16`, `f32`.
// TODO rename to `PixelComponent`? Split up into separate traits? Seal?
pub trait Primitive:
Copy + NumCast + Num + PartialOrd<Self> + Clone + Bounded + sealed::PrimitiveSealed
Copy + NumCast + Num + PartialOrd<Self> + Clone + Bounded + PrimitiveSealed
{
/// The maximum value for this type of primitive within the context of color.
/// For floats, the maximum is `1.0`, whereas the integer types inherit their usual maximum values.
Expand Down
Loading