use crate::color::{Color, ColorChannel, ColorChannelModel};
use image_texel::image::{Coord, ImageRef};
use image_texel::layout::{
Decay, Layout as ImageLayout, MatrixBytes, Raster, SliceLayout, StrideSpec, StridedBytes,
Strides, TexelLayout,
};
use crate::shader::ChunkSpec;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub(crate) struct ByteLayout {
pub(crate) width: u32,
pub(crate) height: u32,
pub(crate) bytes_per_row: u32,
}
#[derive(Clone, Debug, PartialEq)]
pub struct CanvasLayout {
pub(crate) bytes: ByteLayout,
pub(crate) texel: Texel,
pub(crate) offset: usize,
pub(crate) color: Option<Color>,
pub(crate) planes: Box<[Plane]>,
}
#[derive(Clone, Debug, PartialEq)]
pub(crate) struct Plane {
pub(crate) bytes_per_row: u32,
pub(crate) texel: Texel,
}
#[derive(Clone, Debug, PartialEq)]
pub struct ChannelSpec {
pub channels: u8,
pub channel_stride: usize,
pub height: u32,
pub height_stride: usize,
pub width: u32,
pub width_stride: usize,
}
#[derive(Clone, PartialEq)]
pub struct ChannelBytes {
pub(crate) inner: StridedBytes,
pub(crate) texel: Texel,
pub(crate) channel_stride: usize,
pub(crate) channels: u8,
}
#[derive(Clone, PartialEq)]
pub struct ChannelLayout<T> {
pub(crate) channel: image_texel::Texel<T>,
pub(crate) inner: ChannelBytes,
}
#[derive(Clone, PartialEq)]
pub struct PlaneBytes {
texel: Texel,
width: u32,
height: u32,
matrix: StridedBytes,
}
#[derive(Clone, PartialEq)]
pub struct PlanarLayout<T> {
texel: Texel,
matrix: Strides<T>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct RowLayoutDescription {
pub width: u32,
pub height: u32,
pub row_stride: u64,
pub texel: Texel,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Texel {
pub block: Block,
pub bits: SampleBits,
pub parts: SampleParts,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
#[repr(u8)]
pub enum Block {
Pixel = 0,
Sub1x2 = 1,
Sub1x4 = 2,
Sub2x2 = 3,
Sub2x4 = 4,
Sub4x4 = 5,
Pack1x2,
Pack1x4,
Pack1x8,
Yuv422,
Yuy2,
Yuv411,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SampleParts {
pub(crate) parts: [Option<ColorChannel>; 4],
pub(crate) color_index: u8,
}
macro_rules! sample_parts {
( $($(#[$attr:meta])* $color:ident: $name:ident = $($ch:path),+;)* ) => {
$(sample_parts! { @$color: $(#[$attr])* $name = $($ch),* })*
impl SampleParts {
$(sample_parts! { @$color: $(#[$attr])* $name = $($ch),* })*
}
};
(@$color:ident: $(#[$attr:meta])* $name:ident = $ch0:path) => {
$(#[$attr])*
pub const $name: SampleParts = SampleParts {
parts: [Some($ch0), None, None, None],
color_index: (
$ch0.canonical_index_in_surely(ColorChannelModel::$color)
),
};
};
(@$color:ident: $(#[$attr:meta])* $name:ident = $ch0:path,$ch1:path) => {
$(#[$attr])*
pub const $name: SampleParts = SampleParts {
parts: [Some($ch0), Some($ch1), None, None],
color_index: (
$ch0.canonical_index_in_surely(ColorChannelModel::$color)
| $ch1.canonical_index_in_surely(ColorChannelModel::$color) << 2
),
};
};
(@$color:ident: $(#[$attr:meta])* $name:ident = $ch0:path,$ch1:path,$ch2:path) => {
$(#[$attr])*
pub const $name: SampleParts = SampleParts {
parts: [Some($ch0), Some($ch1), Some($ch2), None],
color_index: (
$ch0.canonical_index_in_surely(ColorChannelModel::$color)
| $ch1.canonical_index_in_surely(ColorChannelModel::$color) << 2
| $ch2.canonical_index_in_surely(ColorChannelModel::$color) << 4
),
};
};
(@$color:ident: $(#[$attr:meta])* $name:ident = $ch0:path,$ch1:path,$ch2:path,$ch3:path) => {
$(#[$attr])*
pub const $name: SampleParts = SampleParts {
parts: [Some($ch0), Some($ch1), Some($ch2), Some($ch3)],
color_index: (
$ch0.canonical_index_in_surely(ColorChannelModel::$color)
| $ch1.canonical_index_in_surely(ColorChannelModel::$color) << 2
| $ch2.canonical_index_in_surely(ColorChannelModel::$color) << 4
| $ch3.canonical_index_in_surely(ColorChannelModel::$color) << 6
),
};
};
}
#[allow(non_upper_case_globals)]
#[allow(unused)]
mod sample_parts {
type Cc = super::ColorChannel;
use super::ColorChannelModel;
use super::SampleParts;
sample_parts! {
Rgb: A = Cc::Alpha;
Rgb: R = Cc::R;
Rgb: G = Cc::G;
Rgb: B = Cc::B;
Yuv: Luma = Cc::Luma;
Yuv: LumaA = Cc::Luma,Cc::Alpha;
Rgb: Rgb = Cc::R,Cc::G,Cc::B;
Rgb: RgbA = Cc::R,Cc::G,Cc::B,Cc::Alpha;
Rgb: ARgb = Cc::Alpha,Cc::R,Cc::G,Cc::B;
Rgb: Bgr = Cc::B,Cc::G,Cc::R;
Rgb: BgrA = Cc::B,Cc::G,Cc::R,Cc::Alpha;
Rgb: ABgr = Cc::Alpha,Cc::B,Cc::G,Cc::R;
Yuv: Yuv = Cc::Luma,Cc::Cb,Cc::Cr;
Yuv: YuvA = Cc::Luma,Cc::Cb,Cc::Cr,Cc::Alpha;
Lab: Lab = Cc::L,Cc::LABa,Cc::LABb;
Lab: LabA = Cc::L,Cc::LABa,Cc::LABb,Cc::Alpha;
Lab: Lch = Cc::L,Cc::C,Cc::LABh;
Lab: LchA = Cc::L,Cc::C,Cc::LABh,Cc::Alpha;
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
#[allow(non_camel_case_types)]
#[repr(u8)]
pub enum SampleBits {
Int8,
UInt8,
UInt1x8,
UInt2x4,
UInt332,
UInt233,
Int16,
UInt16,
UInt4x2,
UInt4x4,
UInt4x6,
UInt_444,
UInt444_,
UInt565,
UInt8x2,
UInt8x3,
UInt8x4,
UInt8x6,
Int8x2,
Int8x3,
Int8x4,
UInt16x2,
UInt16x3,
UInt16x4,
Int16x2,
Int16x3,
Int16x4,
UInt16x6,
UInt1010102,
UInt2101010,
UInt101010_,
UInt_101010,
Float16x4,
Float32,
Float32x2,
Float32x3,
Float32x4,
Float32x6,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum BitEncoding {
Opaque,
UInt,
Int,
Float,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct LayoutError {
inner: LayoutErrorInner,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum LayoutErrorInner {
NoInfo,
NoPlanes,
NoModel,
TooManyPlanes(usize),
WidthError(core::num::TryFromIntError),
HeightError(core::num::TryFromIntError),
StrideError,
NoChannelIndex(ColorChannel),
ValidationError(u32),
}
impl Texel {
pub fn new_u8(parts: SampleParts) -> Self {
use SampleBits::*;
Self::pixel_from_bits(parts, [UInt8, UInt8x2, UInt8x3, UInt8x4])
}
pub fn new_i8(parts: SampleParts) -> Self {
use SampleBits::*;
Self::pixel_from_bits(parts, [Int8, Int8x2, Int8x3, Int8x4])
}
pub fn new_u16(parts: SampleParts) -> Self {
use SampleBits::*;
Self::pixel_from_bits(parts, [UInt16, UInt16x2, UInt16x3, UInt16x4])
}
pub fn new_i16(parts: SampleParts) -> Self {
use SampleBits::*;
Self::pixel_from_bits(parts, [Int16, Int16x2, Int16x3, Int16x4])
}
pub fn new_f32(parts: SampleParts) -> Self {
use SampleBits::*;
Self::pixel_from_bits(parts, [Float32, Float32x2, Float32x3, Float32x4])
}
fn pixel_from_bits(parts: SampleParts, bits: [SampleBits; 4]) -> Self {
Texel {
block: Block::Pixel,
bits: bits[(parts.num_components() - 1) as usize],
parts,
}
}
pub fn channel_texel(&self, channel: ColorChannel) -> Option<Texel> {
use sample_parts::*;
use Block::*;
use SampleBits::*;
#[allow(non_upper_case_globals)]
let parts = match self.parts {
Rgb => match channel {
ColorChannel::R => R,
ColorChannel::G => G,
ColorChannel::B => B,
_ => return None,
},
RgbA | BgrA | ABgr | ARgb => match channel {
ColorChannel::R => R,
ColorChannel::G => G,
ColorChannel::B => B,
ColorChannel::Alpha => A,
_ => return None,
},
_ => return None,
};
let bits = match self.bits {
UInt8 | UInt8x3 | UInt8x4 => UInt8,
Int8 | Int8x3 | Int8x4 => Int8,
UInt16 | UInt16x3 | UInt16x4 => UInt16,
Int16 | Int16x3 | Int16x4 => Int16,
_ => return None,
};
let block = match self.block {
Yuv422 | Yuy2 | Yuv411 => return None,
_ => self.block,
};
Some(Texel { bits, parts, block })
}
}
impl ColorChannel {
pub fn in_model(self, model: ColorChannelModel) -> bool {
self.canonical_index_in(model).is_some()
}
const fn canonical_index_in(self, model: ColorChannelModel) -> Option<u8> {
use ColorChannel::*;
use ColorChannelModel::*;
Some(match (self, model) {
(R | X, Rgb) => 0,
(G | Y, Rgb) => 1,
(B | Z, Rgb) => 2,
(Luma, Yuv) => 0,
(Cb, Yuv) => 1,
(Cr, Yuv) => 2,
(L, Lab) => 0,
(LABa | C, Lab) => 1,
(LABb | LABh, Lab) => 2,
(Alpha, _) => 3,
_ => return None,
})
}
const fn canonical_index_in_surely(self, model: ColorChannelModel) -> u8 {
match Self::canonical_index_in(self, model) {
Some(idx) => idx,
None => 0,
}
}
}
impl SampleBits {
pub(crate) const MAX_COMPONENTS: usize = 8;
pub fn bytes(self) -> u16 {
use SampleBits::*;
#[allow(non_upper_case_globals)]
match self {
Int8 | UInt8 | UInt1x8 | UInt2x4 | UInt332 | UInt233 | UInt4x2 => 1,
Int8x2 | UInt8x2 | Int16 | UInt16 | UInt565 | UInt4x4 | UInt444_ | UInt_444 => 2,
Int8x3 | UInt8x3 | UInt4x6 => 3,
Int8x4 | UInt8x4 | Int16x2 | UInt16x2 | UInt1010102 | UInt2101010 | UInt101010_
| UInt_101010 | Float32 => 4,
UInt8x6 | Int16x3 | UInt16x3 => 6,
Int16x4 | UInt16x4 | Float16x4 | Float32x2 => 8,
UInt16x6 | Float32x3 => 12,
Float32x4 => 16,
Float32x6 => 24,
}
}
fn as_array(self) -> Option<(TexelLayout, u8)> {
use image_texel::AsTexel;
use SampleBits::*;
Some(match self {
UInt8 | UInt8x2 | UInt8x3 | UInt8x4 | UInt8x6 => {
(u8::texel().into(), self.bytes() as u8)
}
Int8 | Int8x2 | Int8x3 | Int8x4 => (i8::texel().into(), self.bytes() as u8),
UInt16 | UInt16x2 | UInt16x3 | UInt16x4 | UInt16x6 => {
(u16::texel().into(), self.bytes() as u8 / 2)
}
Int16 | Int16x2 | Int16x3 | Int16x4 => (i16::texel().into(), self.bytes() as u8 / 2),
Float32 | Float32x2 | Float32x3 | Float32x4 | Float32x6 => {
(u32::texel().into(), self.bytes() as u8 / 4)
}
_ => return None,
})
}
fn layout(self) -> TexelLayout {
use crate::shader::{GenericTexelAction, TexelKind};
struct ToLayout;
impl GenericTexelAction<TexelLayout> for ToLayout {
fn run<T>(self, texel: image_texel::Texel<T>) -> TexelLayout {
texel.into()
}
}
TexelKind::from(self).action(ToLayout)
}
pub(crate) fn bit_encoding(self) -> ([BitEncoding; Self::MAX_COMPONENTS], u8) {
const M: usize = SampleBits::MAX_COMPONENTS;
use SampleBits::*;
match self {
UInt8 | UInt8x2 | UInt8x3 | UInt8x4 | UInt8x6 => {
([BitEncoding::UInt; M], self.bytes() as u8)
}
UInt1x8 => ([BitEncoding::UInt; M], 8),
UInt2x4 => ([BitEncoding::UInt; M], 4),
Int8 | Int8x2 | Int8x3 | Int8x4 => ([BitEncoding::Int; M], self.bytes() as u8),
UInt16 | UInt16x2 | UInt16x3 | UInt16x4 | UInt16x6 => {
([BitEncoding::UInt; M], self.bytes() as u8 / 2)
}
Int16 | Int16x2 | Int16x3 | Int16x4 => ([BitEncoding::Int; M], self.bytes() as u8 / 2),
Float32 | Float32x2 | Float32x3 | Float32x4 | Float32x6 => {
([BitEncoding::Float; M], self.bytes() as u8 / 4)
}
UInt332 | UInt233 | UInt565 => ([BitEncoding::UInt; M], 3),
UInt4x2 => ([BitEncoding::UInt; M], 2),
UInt4x4 => ([BitEncoding::UInt; M], 4),
UInt4x6 => ([BitEncoding::UInt; M], 6),
UInt_444 | SampleBits::UInt444_ => ([BitEncoding::UInt; M], 3),
UInt101010_ | UInt_101010 => ([BitEncoding::Float; M], 3),
UInt1010102 | UInt2101010 => ([BitEncoding::Float; M], 4),
Float16x4 => ([BitEncoding::Float; M], 4),
}
}
}
impl SampleParts {
pub fn new(parts: [Option<ColorChannel>; 4], model: ColorChannelModel) -> Option<Self> {
let color_index = Self::color_index(&parts, model)?;
Some(SampleParts { parts, color_index })
}
pub fn with_channel(&self, ch: ColorChannel) -> Option<Self> {
let pos = self.parts.iter().position(|part| *part == Some(ch))?;
let mut parts = [None; 4];
parts[0] = self.parts[pos];
let color_index = (self.color_index >> (2 * pos)) & 0x3;
Some(SampleParts { parts, color_index })
}
pub fn contains(&self, ch: ColorChannel) -> bool {
self.with_channel(ch).is_some()
}
pub fn color_channels(&self) -> [Option<ColorChannel>; 4] {
self.parts
}
fn color_index(parts: &[Option<ColorChannel>; 4], model: ColorChannelModel) -> Option<u8> {
let mut unused = [true; 4];
let mut color_index = [0; 4];
for (part, pos) in parts.into_iter().zip(&mut color_index) {
if let Some(p) = part {
let idx = p.canonical_index_in(model)?;
if !core::mem::take(&mut unused[idx as usize]) {
return None;
}
*pos = idx;
}
}
let color_index = color_index
.into_iter()
.enumerate()
.fold(0u8, |acc, (idx, pos)| acc | pos << (2 * idx));
Some(color_index)
}
pub fn with_yuv_422(
parts: [Option<ColorChannel>; 3],
model: ColorChannelModel,
) -> Option<Self> {
let parts = [parts[0], parts[1], parts[2], None];
let color_index = Self::color_index(&parts, model)?;
Some(SampleParts { parts, color_index })
}
pub fn with_yuv_411(
parts: [Option<ColorChannel>; 3],
model: ColorChannelModel,
) -> Option<Self> {
let parts = [parts[0], parts[1], parts[2], None];
let color_index = Self::color_index(&parts, model)?;
Some(SampleParts { parts, color_index })
}
pub fn num_components(self) -> u8 {
self.parts.iter().map(|ch| u8::from(ch.is_some())).sum()
}
pub fn has_alpha(self) -> bool {
self.parts
.iter()
.any(|c| matches!(c, Some(ColorChannel::Alpha)))
}
pub(crate) fn channels(&self) -> impl '_ + Iterator<Item = (Option<ColorChannel>, u8)> {
(0..4).map(|i| (self.parts[i], (self.color_index >> (2 * i)) & 0x3))
}
}
impl Block {
pub fn width(&self) -> u32 {
use Block::*;
match self {
Pixel => 1,
Pack1x2 | Sub1x2 | Sub2x2 | Yuv422 | Yuy2 => 2,
Pack1x4 | Sub1x4 | Sub2x4 | Sub4x4 | Yuv411 => 4,
Pack1x8 => 8,
}
}
pub fn height(&self) -> u32 {
use Block::*;
match self {
Pixel | Sub1x2 | Sub1x4 | Yuv422 | Yuy2 | Yuv411 => 1,
Pack1x2 | Pack1x4 | Pack1x8 => 1,
Sub2x2 | Sub2x4 => 2,
Sub4x4 => 3,
}
}
pub(crate) fn block_width(&self, pixels: u32) -> u32 {
let div = self.width();
pixels / div + if pixels % div == 0 { 0 } else { 1 }
}
pub(crate) fn block_height(&self, pixels: u32) -> u32 {
let div = self.height();
pixels / div + if pixels % div == 0 { 0 } else { 1 }
}
}
impl CanvasLayout {
pub fn with_plane(bytes: PlaneBytes) -> Self {
CanvasLayout::from(&bytes)
}
pub fn with_planes(layers: &[PlaneBytes], texel: Texel) -> Result<Self, LayoutError> {
if layers.len() == 0 {
return Err(LayoutError::NO_PLANES);
}
if layers.len() > 1 {
return Err(LayoutError::bad_planes(layers.len()));
}
let spec = layers[0].matrix.spec();
let width: u32 = spec.width.try_into().map_err(LayoutError::width_error)?;
let min_height_stride = spec.width_stride as u32 * width;
let height_stride = spec
.height_stride
.try_into()
.map_err(LayoutError::height_error)?;
if min_height_stride > height_stride {
return Err(LayoutError::bad_planes(0));
}
Self::validate(CanvasLayout {
bytes: ByteLayout {
width: layers[0].width,
height: layers[0].height,
bytes_per_row: height_stride,
},
planes: Box::default(),
offset: 0,
texel,
color: None,
})
}
pub fn with_row_layout(rows: &RowLayoutDescription) -> Result<Self, LayoutError> {
let bytes_per_texel = rows.texel.bits.bytes();
let bytes_per_row = usize::try_from(rows.row_stride).map_err(LayoutError::width_error)?;
let stride = StrideSpec {
offset: 0,
width: rows.texel.block.block_width(rows.width) as usize,
height: rows.texel.block.block_height(rows.height) as usize,
element: rows.texel.bits.layout(),
height_stride: bytes_per_row,
width_stride: bytes_per_texel.into(),
};
let bytes = PlaneBytes {
texel: rows.texel.clone(),
width: rows.width,
height: rows.height,
matrix: StridedBytes::new(stride).map_err(LayoutError::stride_error)?,
};
Self::with_planes(&[bytes], rows.texel.clone())
}
pub fn with_texel(texel: &Texel, width: u32, height: u32) -> Result<Self, LayoutError> {
let texel_stride = u64::from(texel.bits.bytes());
let width_sub = texel.block.block_width(width);
Self::with_row_layout(&RowLayoutDescription {
width,
height,
row_stride: u64::from(width_sub) * texel_stride,
texel: texel.clone(),
})
}
pub fn texel(&self) -> &Texel {
&self.texel
}
pub fn color(&self) -> Option<&Color> {
self.color.as_ref()
}
pub fn texel_index(&self, x: u32, y: u32) -> u64 {
let bytes_per_texel = self.texel.bits.bytes();
let byte_index = u64::from(x) * u64::from(self.bytes.bytes_per_row)
+ u64::from(y) * u64::from(bytes_per_texel);
byte_index / u64::from(bytes_per_texel)
}
pub(crate) fn fill_texel_indices_impl(
&self,
idx: &mut [usize],
iter: &[[u32; 2]],
chunk: ChunkSpec,
) {
if self.texel.bits.bytes() == 0 {
unreachable!("No texel with zero bytes");
}
if self.bytes.bytes_per_row % self.texel.bits.bytes() as u32 == 0 {
let pitch = self.bytes.bytes_per_row / self.texel.bits.bytes() as u32;
return Self::fill_indices_constant_size(idx, iter, pitch, chunk);
}
for (&[x, y], idx) in iter.iter().zip(idx) {
*idx = self.texel_index(x, y) as usize;
}
}
fn fill_indices_constant_size(
idx: &mut [usize],
iter: &[[u32; 2]],
pitch: u32,
spec: ChunkSpec,
) {
let pitch = u64::from(pitch);
let mut index_chunks = idx.chunks_mut(spec.chunk_size);
let mut iter = iter.chunks(spec.chunk_size);
for _ in &mut spec.chunks[..] {
let (idx, iter) = match (index_chunks.next(), iter.next()) {
(Some(idx), Some(iter)) => (idx, iter),
_ => break,
};
for (&[x, y], idx) in iter.iter().zip(&mut idx[..]) {
let texindex = u64::from(x) * pitch + u64::from(y);
*idx = texindex as usize;
}
}
if spec.should_defer_texel_ops {
for (idx, chunk_spec) in idx.chunks_mut(spec.chunk_size).zip(spec.chunks) {
let mut contig = true;
for wnd in idx.windows(2) {
if wnd[1].saturating_sub(wnd[0]) != 1 {
contig = false;
}
}
let contiguous_start = idx[0];
if contig {
*chunk_spec = [contiguous_start, idx.len()];
}
}
}
}
pub fn as_row_layout(&self) -> RowLayoutDescription {
RowLayoutDescription {
width: self.bytes.width,
height: self.bytes.height,
texel: self.texel.clone(),
row_stride: u64::from(self.bytes.bytes_per_row),
}
}
pub fn width(&self) -> u32 {
self.bytes.width
}
pub fn height(&self) -> u32 {
self.bytes.height
}
pub fn u64_len(&self) -> u64 {
u64::from(self.bytes.bytes_per_row) * u64::from(self.bytes.height)
}
pub fn byte_len(&self) -> usize {
(self.bytes.bytes_per_row as usize) * (self.bytes.height as usize)
}
pub fn set_color(&mut self, color: Color) -> Result<(), LayoutError> {
let model = color.model().ok_or(LayoutError::NO_MODEL)?;
for (channel, idx) in self.texel.parts.channels() {
if let Some(channel) = channel {
let other_idx = match channel.canonical_index_in(model) {
Some(idx) => idx,
None => return Err(LayoutError::no_index(channel)),
};
if other_idx != idx {
return Err(LayoutError::NO_INFO);
}
}
}
self.color = Some(color);
Ok(())
}
pub(crate) fn plane(&self, idx: u8) -> Option<PlaneBytes> {
if !self.planes.is_empty() {
return None;
}
if idx != 0 {
return None;
}
let matrix = StridedBytes::with_row_major(
MatrixBytes::from_width_height(
self.texel.bits.layout(),
self.texel.block.block_width(self.bytes.width) as usize,
self.texel.block.block_height(self.bytes.height) as usize,
)
.unwrap(),
);
Some(PlaneBytes {
texel: self.texel.clone(),
width: self.bytes.width,
height: self.bytes.height,
matrix,
})
}
pub(crate) fn num_planes(&self) -> u8 {
if self.planes.is_empty() {
1
} else {
self.planes.len() as u8
}
}
pub fn as_plane(&self) -> Option<PlaneBytes> {
if !self.planes.is_empty() {
return None;
}
self.plane(0)
}
fn validate(this: Self) -> Result<Self, LayoutError> {
let mut start = this.offset;
for plane in 0..this.num_planes() {
let plane = this.plane(plane).ok_or(LayoutError::validation(line!()))?;
let spec = plane.matrix.spec();
let offset = plane.matrix.spec().offset;
let texel_offset = plane
.offset_in_texels()
.checked_mul(spec.element.size())
.ok_or(LayoutError::validation(line!()))?;
if texel_offset != offset {
return Err(LayoutError::validation(line!()));
}
if texel_offset % 256 != 0 {
return Err(LayoutError::validation(line!()));
}
let plane_end = offset
.checked_add(plane.matrix.byte_len())
.ok_or(LayoutError::validation(line!()))?;
let texel_layout = plane.texel.bits.layout();
if !spec.element.superset_of(texel_layout) {
return Err(LayoutError::validation(line!()));
}
if start > offset {
return Err(LayoutError::validation(line!()));
}
start = plane_end;
}
let lines = usize::try_from(this.bytes.width).map_err(LayoutError::width_error)?;
let height = usize::try_from(this.bytes.height).map_err(LayoutError::height_error)?;
let ok = height
.checked_mul(lines)
.map_or(false, |len| len < isize::MAX as usize);
if ok {
Ok(this)
} else {
Err(LayoutError::validation(line!()))
}
}
}
impl PlaneBytes {
pub fn texel(&self) -> &Texel {
&self.texel
}
pub(crate) fn sub_offset(&mut self, offset: usize) {
let mut spec = self.matrix.spec();
assert!(offset % spec.element.size() == 0);
assert!(offset % 256 == 0);
spec.offset = spec.offset.saturating_sub(offset);
self.matrix = StridedBytes::new(spec).unwrap();
}
pub(crate) fn as_channel_bytes(&self) -> Option<ChannelBytes> {
let (channel_layout, channels) = self.texel.bits.as_array()?;
Some(ChannelBytes {
channel_stride: channel_layout.size(),
channels,
inner: self.matrix.clone(),
texel: self.texel.clone(),
})
}
pub(crate) fn is_compatible<T>(&self, texel: image_texel::Texel<T>) -> Option<PlanarLayout<T>> {
use image_texel::layout::TryMend;
Some(PlanarLayout {
texel: self.texel.clone(),
matrix: texel.try_mend(&self.matrix).ok()?,
})
}
pub(crate) fn offset_in_texels(&self) -> usize {
self.matrix.spec().offset / self.matrix.spec().element.size()
}
}
impl<T> PlanarLayout<T> {
pub fn texel(&self) -> &Texel {
&self.texel
}
pub(crate) fn offset_in_texels(&self) -> usize {
self.matrix.spec().offset / self.matrix.spec().element.size()
}
}
impl ChannelBytes {
pub fn spec(&self) -> ChannelSpec {
let StrideSpec {
width,
width_stride,
height,
height_stride,
..
} = self.inner.spec();
ChannelSpec {
channels: self.channels,
channel_stride: self.channel_stride,
height: height as u32,
height_stride,
width: width as u32,
width_stride,
}
}
pub(crate) fn is_compatible<T>(
&self,
texel: image_texel::Texel<T>,
) -> Option<ChannelLayout<T>> {
if self.channel_stride == texel.size() {
Some(ChannelLayout {
channel: texel,
inner: self.clone(),
})
} else {
None
}
}
}
impl<T> ChannelLayout<T> {
pub fn texel(&self) -> &Texel {
&self.inner.texel
}
pub fn spec(&self) -> ChannelSpec {
self.inner.spec()
}
fn from_planar_assume_u8<const N: usize>(from: PlanarLayout<[T; N]>) -> Self {
let channel = from.matrix.texel().array_element();
let inner = StridedBytes::decay(from.matrix);
ChannelLayout {
channel,
inner: ChannelBytes {
texel: from.texel,
channels: N as u8,
channel_stride: channel.size(),
inner,
},
}
}
}
impl LayoutError {
const NO_INFO: Self = LayoutError {
inner: LayoutErrorInner::NoInfo,
};
const NO_PLANES: Self = LayoutError {
inner: LayoutErrorInner::NoPlanes,
};
const NO_MODEL: Self = LayoutError {
inner: LayoutErrorInner::NoModel,
};
fn validation(num: u32) -> Self {
LayoutError {
inner: LayoutErrorInner::ValidationError(num),
}
}
fn bad_planes(num: usize) -> Self {
LayoutError {
inner: LayoutErrorInner::TooManyPlanes(num),
}
}
fn width_error(err: core::num::TryFromIntError) -> Self {
LayoutError {
inner: LayoutErrorInner::WidthError(err),
}
}
fn height_error(err: core::num::TryFromIntError) -> Self {
LayoutError {
inner: LayoutErrorInner::HeightError(err),
}
}
fn stride_error(_: image_texel::layout::BadStrideError) -> Self {
LayoutError {
inner: LayoutErrorInner::StrideError,
}
}
fn no_index(ch: ColorChannel) -> Self {
LayoutError {
inner: LayoutErrorInner::NoChannelIndex(ch),
}
}
}
impl ImageLayout for CanvasLayout {
fn byte_len(&self) -> usize {
CanvasLayout::byte_len(self)
}
}
impl Decay<PlaneBytes> for CanvasLayout {
fn decay(from: PlaneBytes) -> Self {
CanvasLayout::from(&from)
}
}
impl ImageLayout for PlaneBytes {
fn byte_len(&self) -> usize {
self.matrix.byte_len()
}
}
impl<T> ImageLayout for PlanarLayout<T> {
fn byte_len(&self) -> usize {
self.matrix.byte_len()
}
}
impl<T> SliceLayout for PlanarLayout<T> {
type Sample = T;
fn sample(&self) -> image_texel::Texel<Self::Sample> {
self.matrix.texel()
}
}
impl<T> Raster<T> for PlanarLayout<T> {
fn dimensions(&self) -> Coord {
let StrideSpec { width, height, .. } = self.matrix.spec();
debug_assert!(u32::try_from(width).is_ok(), "Invalid dimension: {}", width);
debug_assert!(
u32::try_from(height).is_ok(),
"Invalid dimension: {}",
height
);
Coord(width as u32, height as u32)
}
fn get(from: ImageRef<&Self>, at: Coord) -> Option<T> {
let (x, y) = at.xy();
let layout = from.layout();
let matrix = &layout.matrix;
let texel = matrix.texel();
let StrideSpec { width_stride, .. } = matrix.spec();
debug_assert!(
width_stride % texel.size() == 0,
"Invalid stride: {} not valid for {:?}",
width_stride,
texel
);
let idx = y as usize * (width_stride / texel.size()) + x as usize;
let slice = from.as_texels(texel);
let value = slice.get(layout.offset_in_texels()..)?.get(idx)?;
Some(texel.copy_val(value))
}
}
impl<T> Decay<PlanarLayout<T>> for PlaneBytes {
fn decay(from: PlanarLayout<T>) -> Self {
let spec = from.matrix.spec();
PlaneBytes {
texel: from.texel,
width: spec.width as u32,
height: spec.height as u32,
matrix: StridedBytes::decay(from.matrix),
}
}
}
impl ImageLayout for ChannelBytes {
fn byte_len(&self) -> usize {
self.inner.byte_len()
}
}
impl<T> ImageLayout for ChannelLayout<T> {
fn byte_len(&self) -> usize {
self.inner.byte_len()
}
}
impl<T> SliceLayout for ChannelLayout<T> {
type Sample = T;
fn sample(&self) -> image_texel::Texel<Self::Sample> {
self.channel
}
}
impl<T> Decay<PlanarLayout<[T; 1]>> for ChannelLayout<T> {
fn decay(from: PlanarLayout<[T; 1]>) -> Self {
ChannelLayout::from_planar_assume_u8(from)
}
}
impl<T> Decay<PlanarLayout<[T; 2]>> for ChannelLayout<T> {
fn decay(from: PlanarLayout<[T; 2]>) -> Self {
ChannelLayout::from_planar_assume_u8(from)
}
}
impl<T> Decay<PlanarLayout<[T; 3]>> for ChannelLayout<T> {
fn decay(from: PlanarLayout<[T; 3]>) -> Self {
ChannelLayout::from_planar_assume_u8(from)
}
}
impl<T> Decay<PlanarLayout<[T; 4]>> for ChannelLayout<T> {
fn decay(from: PlanarLayout<[T; 4]>) -> Self {
ChannelLayout::from_planar_assume_u8(from)
}
}
impl<T> Decay<ChannelLayout<T>> for ChannelBytes {
fn decay(from: ChannelLayout<T>) -> Self {
from.inner
}
}
impl From<&'_ PlaneBytes> for CanvasLayout {
fn from(plane: &PlaneBytes) -> Self {
let StrideSpec {
width: _,
height: _,
width_stride: _,
height_stride,
element: _,
offset,
} = plane.matrix.spec();
CanvasLayout {
bytes: ByteLayout {
width: plane.width,
height: plane.height,
bytes_per_row: height_stride as u32,
},
texel: plane.texel.clone(),
offset,
color: None,
planes: Box::default(),
}
}
}