1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// Distributed under The MIT License (MIT)
//
// Copyright (c) 2019 The `image-rs` developers
use core::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd};
use core::marker::PhantomData;
use core::{fmt, mem};

use zerocopy::{AsBytes, FromBytes};

/// Marker struct to denote a pixel type.
///
/// Can be constructed only for types that have expected alignment and no byte invariants. It
/// always implements `Copy` and `Clone`, regardless of the underlying type and is zero-sized.
pub struct Pixel<P: ?Sized>(PhantomData<P>);

/// Describes a type which can represent a `Pixel`.
pub trait AsPixel {
    /// Get the pixel struct for this type.
    ///
    /// The naive implementation of merely unwraping the result of `Pixel::for_type` **panics** on
    /// any invalid type. This trait should only be implemented when you know for sure that the
    /// type is correct.
    fn pixel() -> Pixel<Self>;
}

pub(crate) const MAX_ALIGN: usize = 16;

/// A byte-like-type that is aligned to the required max alignment.
///
/// This type does not contain padding and implements `FromBytes`.
#[derive(Clone, Copy, AsBytes, FromBytes)]
#[repr(align(16))]
#[repr(C)]
pub struct MaxAligned(pub(crate) [u8; 16]);

pub(crate) mod constants {
    use super::{AsPixel, MaxAligned, Pixel};

    macro_rules! constant_pixels {
        ($(($name:ident, $type:ty)),*) => {
            $(pub const $name: Pixel<$type> = Pixel(core::marker::PhantomData) ;
              impl AsPixel for $type {
                  fn pixel() -> Pixel<Self> {
                      $name
                  }
              }
              )*
        }
    }

    constant_pixels!(
        (I8, i8),
        (U8, u8),
        (I16, i16),
        (U16, u16),
        (I32, i32),
        (U32, u32),
        (F32, f32),
        (I64, i64),
        (U64, u64),
        (F64, f64),
        (RGB, [u8; 3]),
        (RGBA, [u8; 4]),
        (MAX, MaxAligned)
    );
}

impl<P: AsBytes + FromBytes> Pixel<P> {
    /// Try to construct an instance of the marker.
    ///
    /// If successful, you can freely use it to access the image buffers.
    pub fn for_type() -> Option<Self> {
        if mem::align_of::<P>() <= MAX_ALIGN && !mem::needs_drop::<P>() {
            Some(Pixel(PhantomData))
        } else {
            None
        }
    }
}

impl<P> Pixel<P> {
    /// Proxy of `core::mem::align_of`.
    pub fn align(self) -> usize {
        mem::align_of::<P>()
    }

    /// Proxy of `core::mem::size_of`.
    pub fn size(self) -> usize {
        mem::size_of::<P>()
    }
}

/// This is a pure marker type.
impl<P> Clone for Pixel<P> {
    fn clone(&self) -> Self {
        Pixel(PhantomData)
    }
}

impl<P> PartialEq for Pixel<P> {
    fn eq(&self, _: &Self) -> bool {
        true
    }
}

impl<P> Eq for Pixel<P> {}

impl<P> PartialOrd for Pixel<P> {
    fn partial_cmp(&self, _: &Self) -> Option<Ordering> {
        Some(Ordering::Equal)
    }
}

impl<P> Ord for Pixel<P> {
    fn cmp(&self, _: &Self) -> Ordering {
        Ordering::Equal
    }
}

/// This is a pure marker type.
impl<P> Copy for Pixel<P> {}

impl<P> fmt::Debug for Pixel<P> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Pixel")
            .field("size", &self.size())
            .field("align", &self.align())
            .finish()
    }
}