use core::ops::Range;
use image_texel::image::{ImageMut, ImageRef};
use image_texel::{AsTexel, Texel, TexelBuffer};
use crate::arch::ShuffleOps;
use crate::bits::FromBits;
use crate::layout::{
BitEncoding, Block, CanvasLayout, SampleBits, SampleParts, Texel as TexelBits,
};
use crate::Canvas;
pub struct Converter {
chunk: usize,
chunk_count: usize,
super_blocks: TexelBuffer<[u32; 2]>,
in_texels: TexelBuffer,
in_coords: TexelBuffer<[u32; 2]>,
in_index_list: Vec<usize>,
in_slices: TexelBuffer<[usize; 2]>,
out_texels: TexelBuffer,
out_coords: TexelBuffer<[u32; 2]>,
out_index_list: Vec<usize>,
out_slices: TexelBuffer<[usize; 2]>,
pixel_in_buffer: TexelBuffer,
neutral_color_buffer: TexelBuffer,
pixel_out_buffer: TexelBuffer,
}
struct Info {
in_layout: CanvasLayout,
out_layout: CanvasLayout,
common_pixel: CommonPixel,
#[allow(unused)]
common_color: CommonColor,
common_blocks: CommonPixelOrder,
in_kind: TexelKind,
out_kind: TexelKind,
}
#[derive(Clone, Copy, Debug)]
pub enum TexelKind {
U8,
U8x2,
U8x3,
U8x4,
U8x6,
U16,
U16x2,
U16x3,
U16x4,
U16x6,
F32,
F32x2,
F32x3,
F32x4,
F32x6,
}
pub(crate) trait GenericTexelAction<R = ()> {
fn run<T>(self, texel: Texel<T>) -> R;
}
#[derive(Clone, Copy, Debug)]
enum CommonPixel {
F32x4,
}
#[derive(Clone, Copy, Debug)]
enum CommonColor {
CieXyz,
}
#[derive(Clone, Copy, Debug)]
enum CommonPixelOrder {
PixelsInRowOrder,
}
type PlaneSource<'data, 'layout> = ImageRef<'data, &'layout CanvasLayout>;
type PlaneTarget<'data, 'layout> = ImageMut<'data, &'layout mut CanvasLayout>;
struct ConvertOps {
fill_in_index: fn(&Info, &[[u32; 2]], &mut [usize], ChunkSpec),
fill_out_index: fn(&Info, &[[u32; 2]], &mut [usize], ChunkSpec),
expand: fn(&Info, &ConvertOps, &TexelBuffer, &mut TexelBuffer, &mut [PlaneSource]),
recolor: Option<RecolorOps>,
join: fn(&Info, &ConvertOps, &TexelBuffer, &mut TexelBuffer, &mut [PlaneTarget]),
shuffle: ShuffleOps,
int_shuffle: Option<IntShuffleOps>,
}
struct TexelConvertWith<'lt> {
ops: &'lt mut dyn FnMut(&mut Converter, &mut [PlaneSource], &mut [PlaneTarget]),
should_defer_texel_read: bool,
should_defer_texel_write: bool,
}
struct RecolorOps {
from: fn(&Info, &TexelBuffer, &mut TexelBuffer),
into: fn(&Info, &TexelBuffer, &mut TexelBuffer),
}
struct IntShuffleOps {
call: fn(&mut Converter, &ConvertOps, [u8; 4], &[PlaneSource], &mut [PlaneTarget]),
shuffle: [u8; 4],
should_defer_texel_read: bool,
should_defer_texel_write: bool,
}
#[derive(Debug)]
struct SuperTexel {
blocks: Range<u32>,
in_super: u32,
out_super: u32,
}
pub(crate) struct ChunkSpec<'ch> {
pub chunks: &'ch mut [[usize; 2]],
pub chunk_size: usize,
pub should_defer_texel_ops: bool,
}
impl Converter {
pub fn new() -> Self {
Converter {
chunk: 1024,
chunk_count: 1,
super_blocks: TexelBuffer::default(),
in_texels: TexelBuffer::default(),
in_coords: TexelBuffer::default(),
in_index_list: vec![],
in_slices: TexelBuffer::default(),
out_texels: TexelBuffer::default(),
out_coords: TexelBuffer::default(),
out_index_list: vec![],
out_slices: TexelBuffer::default(),
pixel_in_buffer: TexelBuffer::default(),
neutral_color_buffer: TexelBuffer::default(),
pixel_out_buffer: TexelBuffer::default(),
}
}
fn recolor_ops(lhs: &CanvasLayout, rhs: &CanvasLayout) -> Option<RecolorOps> {
match (lhs.color.as_ref()?, rhs.color.as_ref()?) {
(c0, c1) if c0 == c1 => None,
(_, _) => Some(RecolorOps {
from: CommonColor::cie_xyz_from_info,
into: CommonColor::cie_xyz_into_info,
}),
}
}
pub fn run_on(&mut self, frame_in: &Canvas, frame_out: &mut Canvas) {
let info = Info {
in_layout: frame_in.layout().clone(),
out_layout: frame_out.layout().clone(),
common_pixel: CommonPixel::F32x4,
common_color: CommonColor::CieXyz,
common_blocks: CommonPixelOrder::PixelsInRowOrder,
in_kind: TexelKind::from(frame_in.layout().texel.bits),
out_kind: TexelKind::from(frame_out.layout().texel.bits),
};
let recolor = Self::recolor_ops(frame_in.layout(), frame_out.layout());
let int_shuffle = self
.convert_intbuf_with_nocolor_ops(&info)
.filter(|_| recolor.is_none());
let ops = ConvertOps {
fill_in_index: Self::index_from_in_info,
fill_out_index: Self::index_from_out_info,
expand: CommonPixel::expand_from_info,
recolor,
join: CommonPixel::join_from_info,
shuffle: ShuffleOps::default().with_arch(),
int_shuffle,
};
let mut convert_texelbuf_with_ops;
let mut convert_with_intshuffle;
let convert_with: TexelConvertWith = {
if let Some(int_ops) = &ops.int_shuffle {
convert_with_intshuffle =
|that: &mut Self, fi: &mut [PlaneSource], fo: &mut [PlaneTarget]| {
(int_ops.call)(that, &ops, int_ops.shuffle, fi, fo)
};
TexelConvertWith {
ops: &mut convert_with_intshuffle,
should_defer_texel_read: int_ops.should_defer_texel_read,
should_defer_texel_write: int_ops.should_defer_texel_write,
}
} else {
convert_texelbuf_with_ops =
|that: &mut Self, fi: &mut [PlaneSource], fo: &mut [PlaneTarget]| {
that.convert_texelbuf_with_ops(&info, &ops, fi, fo)
};
TexelConvertWith {
ops: &mut convert_texelbuf_with_ops,
should_defer_texel_read: false,
should_defer_texel_write: false,
}
}
};
self.with_filled_texels(convert_with, &info, &ops, frame_in, frame_out)
}
fn convert_texelbuf_with_ops(
&mut self,
info: &Info,
ops: &ConvertOps,
frame_in: &mut [PlaneSource],
frame_out: &mut [PlaneTarget],
) {
(ops.expand)(
&info,
ops,
&self.in_texels,
&mut self.pixel_in_buffer,
frame_in,
);
let pixel_out = if let Some(ref recolor) = ops.recolor {
(recolor.from)(&info, &self.pixel_in_buffer, &mut self.neutral_color_buffer);
(recolor.into)(
&info,
&self.neutral_color_buffer,
&mut self.pixel_out_buffer,
);
&self.pixel_out_buffer
} else {
&self.pixel_in_buffer
};
(ops.join)(&info, ops, pixel_out, &mut self.out_texels, frame_out);
}
fn convert_intbuf_with_nocolor_ops(&mut self, info: &Info) -> Option<IntShuffleOps> {
fn determine_shuffle(inp: SampleParts, outp: SampleParts) -> Option<[u8; 4]> {
let mut ch_from_common = [0x80u8; 4];
let mut ch_from_input = [0x80u8; 4];
for ((ch, common_pos), idx) in inp.channels().zip(0..4) {
if ch.is_some() {
ch_from_input[common_pos as usize] = idx;
}
}
for ((ch, common_pos), idx) in outp.channels().zip(0..4) {
if ch.is_some() {
ch_from_common[idx] = ch_from_input[common_pos as usize];
}
}
Some(ch_from_common)
}
let in_texel = &info.in_layout.texel;
let out_texel = &info.out_layout.texel;
if in_texel.block != Block::Pixel || out_texel.block != Block::Pixel {
return None;
}
if info.in_layout.color != info.out_layout.color {
return None;
}
let shuffle = determine_shuffle(in_texel.parts, out_texel.parts)?;
trait Shuffle<T, const N: usize, const M: usize> {
fn run(_: &ConvertOps, _: &[[T; N]], _: &mut [[T; M]], _: [u8; 4]);
}
fn shuffle_with_texel<T, S: Shuffle<T, N, M>, const N: usize, const M: usize>(
that: &mut Converter,
ops: &ConvertOps,
shuffle: [u8; 4],
source: &[PlaneSource],
target: &mut [PlaneTarget],
) where
T: AsTexel,
{
let in_texel = T::texel().array::<N>();
let out_texel = T::texel().array::<M>();
let source_texels = source[0].as_texels(in_texel);
let target_texels = target[0].as_mut_texels(out_texel);
let in_texels = that.in_texels.as_texels(in_texel);
let out_texels = that.out_texels.as_mut_texels(out_texel);
let in_slices = that.in_slices.iter_mut();
let out_slices = that.out_slices.iter_mut();
let chunks = (0..in_texels.len()).step_by(that.chunk);
for ((islice, oslice), chunk_start) in in_slices.zip(out_slices).zip(chunks) {
let length = in_texels[chunk_start..].len().min(that.chunk);
let input_slice = if islice[1] > 0 {
debug_assert!(length == islice[1]);
let length = core::mem::replace(&mut islice[1], 0);
&source_texels[islice[0]..][..length]
} else {
&in_texels[chunk_start..][..length]
};
let output_slice = if oslice[1] > 0 {
debug_assert!(length == oslice[1]);
let length = core::mem::replace(&mut oslice[1], 0);
&mut target_texels[oslice[0]..][..length]
} else {
&mut out_texels[chunk_start..][..length]
};
S::run(ops, input_slice, output_slice, shuffle)
}
}
struct ShuffleInt8;
struct ShuffleInt16;
impl Shuffle<u8, 4, 4> for ShuffleInt8 {
fn run(ops: &ConvertOps, inp: &[[u8; 4]], outp: &mut [[u8; 4]], shuffle: [u8; 4]) {
outp.copy_from_slice(inp);
(ops.shuffle.shuffle_u8x4)(outp, shuffle);
}
}
impl Shuffle<u8, 3, 4> for ShuffleInt8 {
fn run(ops: &ConvertOps, inp: &[[u8; 3]], outp: &mut [[u8; 4]], shuffle: [u8; 4]) {
(ops.shuffle.shuffle_u8x3_to_u8x4)(inp, outp, shuffle);
}
}
impl Shuffle<u8, 4, 3> for ShuffleInt8 {
fn run(ops: &ConvertOps, inp: &[[u8; 4]], outp: &mut [[u8; 3]], shuffle: [u8; 4]) {
let shuffle = [shuffle[0], shuffle[1], shuffle[2]];
(ops.shuffle.shuffle_u8x4_to_u8x3)(inp, outp, shuffle);
}
}
impl Shuffle<u16, 4, 4> for ShuffleInt16 {
fn run(ops: &ConvertOps, inp: &[[u16; 4]], outp: &mut [[u16; 4]], shuffle: [u8; 4]) {
outp.copy_from_slice(inp);
(ops.shuffle.shuffle_u16x4)(outp, shuffle);
}
}
impl Shuffle<u16, 3, 4> for ShuffleInt16 {
fn run(ops: &ConvertOps, inp: &[[u16; 3]], outp: &mut [[u16; 4]], shuffle: [u8; 4]) {
(ops.shuffle.shuffle_u16x3_to_u16x4)(inp, outp, shuffle);
}
}
impl Shuffle<u16, 4, 3> for ShuffleInt16 {
fn run(ops: &ConvertOps, inp: &[[u16; 4]], outp: &mut [[u16; 3]], shuffle: [u8; 4]) {
let shuffle = [shuffle[0], shuffle[1], shuffle[2]];
(ops.shuffle.shuffle_u16x4_to_u16x3)(inp, outp, shuffle);
}
}
Some(match (in_texel.bits, out_texel.bits) {
(SampleBits::UInt8x4, SampleBits::UInt8x4)
| (SampleBits::Int8x4, SampleBits::Int8x4) => IntShuffleOps {
call: shuffle_with_texel::<u8, ShuffleInt8, 4, 4>,
shuffle,
should_defer_texel_read: true,
should_defer_texel_write: true,
},
(SampleBits::UInt8x3, SampleBits::UInt8x4)
| (SampleBits::Int8x3, SampleBits::Int8x4) => IntShuffleOps {
call: shuffle_with_texel::<u8, ShuffleInt8, 3, 4>,
shuffle,
should_defer_texel_read: true,
should_defer_texel_write: true,
},
(SampleBits::UInt8x4, SampleBits::UInt8x3)
| (SampleBits::Int8x4, SampleBits::Int8x3) => IntShuffleOps {
call: shuffle_with_texel::<u8, ShuffleInt8, 4, 3>,
shuffle,
should_defer_texel_read: true,
should_defer_texel_write: true,
},
(SampleBits::UInt16x4, SampleBits::UInt16x4)
| (SampleBits::Int16x4, SampleBits::Int16x4) => IntShuffleOps {
call: shuffle_with_texel::<u16, ShuffleInt16, 4, 4>,
shuffle,
should_defer_texel_read: true,
should_defer_texel_write: true,
},
(SampleBits::UInt16x3, SampleBits::UInt16x4)
| (SampleBits::Int16x3, SampleBits::Int16x4) => IntShuffleOps {
call: shuffle_with_texel::<u16, ShuffleInt16, 3, 4>,
shuffle,
should_defer_texel_read: true,
should_defer_texel_write: true,
},
(SampleBits::UInt16x4, SampleBits::UInt16x3)
| (SampleBits::Int16x4, SampleBits::Int16x3) => IntShuffleOps {
call: shuffle_with_texel::<u16, ShuffleInt16, 4, 3>,
shuffle,
should_defer_texel_read: true,
should_defer_texel_write: true,
},
_ => return None,
})
}
fn with_filled_texels(
&mut self,
texel_conversion: TexelConvertWith,
info: &Info,
ops: &ConvertOps,
frame_in: &Canvas,
frame_out: &mut Canvas,
) {
use core::slice::from_mut;
let (sb_x, sb_y) = self.super_texel(info);
let mut blocks = Self::blocks(sb_x.blocks.clone(), sb_y.blocks.clone());
loop {
let at_once = self.chunk * self.chunk_count;
self.super_blocks.resize(at_once);
let actual = blocks(self.super_blocks.as_mut_slice());
self.super_blocks.resize(actual);
if self.super_blocks.is_empty() {
break;
}
self.generate_coords(info, ops, &texel_conversion, &sb_x, &sb_y);
self.reserve_buffers(info, ops);
self.read_texels(info, ops, &texel_conversion, frame_in.as_ref());
let mut frame_in = frame_in.as_ref();
let mut frame_out = frame_out.as_mut();
(texel_conversion.ops)(self, from_mut(&mut frame_in), from_mut(&mut frame_out));
self.write_texels(info, ops, &texel_conversion, frame_out);
}
}
fn super_texel(&self, info: &Info) -> (SuperTexel, SuperTexel) {
let b0 = info.in_layout.texel.block;
let b1 = info.out_layout.texel.block;
let super_width = core::cmp::max(b0.width(), b1.width());
let super_height = core::cmp::max(b0.height(), b1.height());
assert!(super_width % b0.width() == 0);
assert!(super_width % b1.width() == 0);
assert!(super_height % b0.height() == 0);
assert!(super_height % b1.height() == 0);
let sampled_with = |w, bs| w / bs + if w % bs == 0 { 0 } else { 1 };
let sb_width = sampled_with(info.in_layout.bytes.width, super_width);
let sb_height = sampled_with(info.in_layout.bytes.height, super_height);
(
SuperTexel {
blocks: 0..sb_height,
in_super: super_height / b0.height(),
out_super: super_height / b1.height(),
},
SuperTexel {
blocks: 0..sb_width,
in_super: super_width / b0.width(),
out_super: super_width / b1.width(),
},
)
}
fn generate_coords(
&mut self,
info: &Info,
ops: &ConvertOps,
converter: &TexelConvertWith,
sb_x: &SuperTexel,
sb_y: &SuperTexel,
) {
fn is_trivial_super(sup: &SuperTexel) -> bool {
sup.in_super == 1 && sup.out_super == 1
}
self.in_coords.resize(0);
self.out_coords.resize(0);
if is_trivial_super(sb_x) && is_trivial_super(sb_y) {
self.in_coords.resize(self.super_blocks.len());
self.out_coords.resize(self.super_blocks.len());
self.in_coords
.as_mut_slice()
.copy_from_slice(&self.super_blocks);
self.out_coords
.as_mut_slice()
.copy_from_slice(&self.super_blocks);
} else {
let in_chunk_len = (sb_x.in_super * sb_y.in_super) as usize;
self.in_coords
.resize(self.super_blocks.len() * in_chunk_len);
let out_chunk_len = (sb_x.out_super * sb_y.out_super) as usize;
self.out_coords
.resize(self.super_blocks.len() * out_chunk_len);
let mut in_chunks = self.in_coords.as_mut_slice().chunks_exact_mut(in_chunk_len);
let mut out_chunks = self
.out_coords
.as_mut_slice()
.chunks_exact_mut(out_chunk_len);
for &[bx, by] in self.super_blocks.as_slice().iter() {
let (sx, sy) = (bx * sb_x.in_super, by * sb_y.in_super);
if let Some(chunk) = in_chunks.next() {
Self::blocks(0..sb_x.in_super, 0..sb_y.in_super)(chunk);
for p in chunk.iter_mut() {
let [ix, iy] = *p;
*p = [sx + ix, sy + iy];
}
}
let (sx, sy) = (bx * sb_x.out_super, by * sb_y.out_super);
if let Some(chunk) = out_chunks.next() {
Self::blocks(0..sb_x.out_super, 0..sb_y.out_super)(chunk);
for p in chunk.iter_mut() {
let [ox, oy] = *p;
*p = [sx + ox, sy + oy];
}
}
}
}
self.in_index_list.resize_with(self.in_coords.len(), || 0);
self.out_index_list.resize_with(self.out_coords.len(), || 0);
self.in_slices.resize(self.chunk_count);
self.out_slices.resize(self.chunk_count);
let in_chunk = ChunkSpec {
chunks: self.in_slices.as_mut_slice(),
chunk_size: self.chunk,
should_defer_texel_ops: converter.should_defer_texel_read,
};
let out_chunk = ChunkSpec {
chunks: self.out_slices.as_mut_slice(),
chunk_size: self.chunk,
should_defer_texel_ops: converter.should_defer_texel_write,
};
(ops.fill_in_index)(
&info,
self.in_coords.as_slice(),
&mut self.in_index_list,
in_chunk,
);
(ops.fill_out_index)(
&info,
self.out_coords.as_slice(),
&mut self.out_index_list,
out_chunk,
);
}
fn reserve_buffers(&mut self, info: &Info, ops: &ConvertOps) {
struct ResizeAction<'data>(&'data mut TexelBuffer, usize);
impl GenericTexelAction for ResizeAction<'_> {
fn run<T>(self, texel: Texel<T>) {
self.0.resize_for_texel(self.1, texel)
}
}
let num_in_texels = self.in_coords.len();
let in_block = info.in_layout.texel.block;
let in_pixels = (in_block.width() * in_block.height()) as usize * num_in_texels;
info.in_kind
.action(ResizeAction(&mut self.in_texels, num_in_texels));
let num_out_texels = self.out_coords.len();
let out_block = info.out_layout.texel.block;
let out_pixels = (out_block.width() * out_block.height()) as usize * num_out_texels;
info.out_kind
.action(ResizeAction(&mut self.out_texels, num_out_texels));
debug_assert!(
in_pixels == out_pixels,
"Mismatching in super block layout: {} {}",
in_pixels,
out_pixels
);
let pixels = in_pixels.max(out_pixels);
info.common_pixel
.action(ResizeAction(&mut self.pixel_in_buffer, pixels));
if let Some(_) = ops.recolor {
info.common_pixel
.action(ResizeAction(&mut self.neutral_color_buffer, pixels));
info.common_pixel
.action(ResizeAction(&mut self.pixel_out_buffer, pixels));
}
}
fn read_texels(
&mut self,
info: &Info,
_: &ConvertOps,
converter: &TexelConvertWith,
from: PlaneSource,
) {
fn fetch_from_texel_array<T>(
from: &PlaneSource,
idx: &[usize],
into: &mut TexelBuffer,
range: Range<usize>,
texel: Texel<T>,
) {
into.resize_for_texel(idx.len(), texel);
let idx = idx[range.clone()].iter();
let texels = &mut into.as_mut_texels(texel)[range];
let texel_slice = from.as_texels(texel);
for (&index, into) in idx.zip(texels) {
if let Some(from) = texel_slice.get(index) {
*into = texel.copy_val(from);
}
}
}
struct ReadUnit<'plane, 'data, 'layout> {
from: &'plane PlaneSource<'data, 'layout>,
idx: &'plane [usize],
into: &'plane mut TexelBuffer,
range: Range<usize>,
}
impl GenericTexelAction for ReadUnit<'_, '_, '_> {
fn run<T>(self, texel: Texel<T>) {
fetch_from_texel_array(self.from, self.idx, self.into, self.range, texel)
}
}
if converter.should_defer_texel_read {
let chunks = self.in_slices.as_mut_slice();
let indexes = self.in_index_list.chunks(self.chunk);
let range = (0..self.in_index_list.len()).step_by(self.chunk);
for (chunk, (indexes, start)) in chunks.iter_mut().zip(indexes.zip(range)) {
let [_, available] = chunk;
if *available == indexes.len() {
continue;
}
*available = 0;
info.in_kind.action(ReadUnit {
from: &from,
idx: &self.in_index_list,
into: &mut self.in_texels,
range: start..start + indexes.len(),
});
}
} else {
info.in_kind.action(ReadUnit {
from: &from,
idx: &self.in_index_list,
into: &mut self.in_texels,
range: 0..self.in_index_list.len(),
})
}
}
fn write_texels(
&mut self,
info: &Info,
_: &ConvertOps,
converter: &TexelConvertWith,
mut into: PlaneTarget,
) {
fn write_from_texel_array<T>(
into: &mut PlaneTarget,
idx: &[usize],
from: &TexelBuffer,
range: Range<usize>,
texel: Texel<T>,
) {
let idx = idx[range.clone()].iter();
let texels = &from.as_texels(texel)[range];
let texel_slice = into.as_mut_texels(texel);
for (&index, from) in idx.zip(texels) {
if let Some(into) = texel_slice.get_mut(index) {
*into = texel.copy_val(from);
}
}
}
struct WriteUnit<'plane, 'data, 'layout> {
into: &'plane mut PlaneTarget<'data, 'layout>,
idx: &'plane [usize],
from: &'plane TexelBuffer,
range: Range<usize>,
}
impl GenericTexelAction for WriteUnit<'_, '_, '_> {
fn run<T>(self, texel: Texel<T>) {
write_from_texel_array(self.into, self.idx, self.from, self.range, texel)
}
}
if converter.should_defer_texel_write {
let chunks = self.out_slices.as_slice();
let indexes = self.out_index_list.chunks(self.chunk);
let range = (0..self.out_index_list.len()).step_by(self.chunk);
for (&chunk, (indexes, start)) in chunks.iter().zip(indexes.zip(range)) {
let [_, unwritten] = chunk;
debug_assert!(unwritten <= indexes.len());
if unwritten > indexes.len() {
continue;
}
if unwritten == 0 {
continue;
}
let offset = indexes.len() - unwritten;
info.out_kind.action(WriteUnit {
into: &mut into,
idx: &self.out_index_list,
from: &self.out_texels,
range: start + offset..start + indexes.len(),
});
}
} else {
info.out_kind.action(WriteUnit {
into: &mut into,
idx: &self.out_index_list,
from: &self.out_texels,
range: 0..self.out_index_list.len(),
});
}
}
fn blocks(mut x: Range<u32>, mut y: Range<u32>) -> impl FnMut(&mut [[u32; 2]]) -> usize {
#[inline(never)]
move |buffer| {
let maximum = buffer.len();
if x.start == x.end {
return 0;
}
if y.end == 0 {
return 0;
}
let lines_left = x.end - x.start;
let line_len = y.end;
let pix_left = u64::from(lines_left) * u64::from(line_len) - u64::from(y.start);
let actual = pix_left.min(maximum as u64);
for p in buffer[..actual as usize].iter_mut() {
let cx = x.start;
let cy = y.start;
*p = [cx, cy];
y.start += 1;
if y.start >= y.end {
y.start = 0;
x.start += 1;
}
}
return actual as usize;
}
}
fn index_from_in_info(info: &Info, texel: &[[u32; 2]], idx: &mut [usize], chunks: ChunkSpec) {
Self::index_from_layer(&info.in_layout, texel, idx, chunks)
}
fn index_from_out_info(info: &Info, texel: &[[u32; 2]], idx: &mut [usize], chunks: ChunkSpec) {
Self::index_from_layer(&info.out_layout, texel, idx, chunks)
}
fn index_from_layer(
info: &CanvasLayout,
texel: &[[u32; 2]],
idx: &mut [usize],
chunks: ChunkSpec,
) {
info.fill_texel_indices_impl(idx, texel, chunks)
}
}
trait ExpandYuvLike<const IN: usize, const OUT: usize> {
fn expand<T: Copy>(_: [T; IN], fill: T) -> [[T; 4]; OUT];
}
impl CommonPixel {
fn expand_from_info(
info: &Info,
_: &ConvertOps,
in_texel: &TexelBuffer,
pixel_buf: &mut TexelBuffer,
_: &mut [PlaneSource],
) {
let TexelBits { bits, parts, block } = info.in_layout.texel;
match block {
Block::Pixel => Self::expand_bits(
info,
[FromBits::for_pixel(bits, parts)],
in_texel,
pixel_buf,
),
Block::Pack1x2 => {
let bits = FromBits::for_pixels::<2>(bits, parts);
Self::expand_bits(info, bits, in_texel, pixel_buf)
}
Block::Pack1x4 => {
let bits = FromBits::for_pixels::<4>(bits, parts);
Self::expand_bits(info, bits, in_texel, pixel_buf)
}
Block::Pack1x8 => {
let bits = FromBits::for_pixels::<8>(bits, parts);
Self::expand_bits(info, bits, in_texel, pixel_buf)
}
Block::Sub1x2 | Block::Sub1x4 | Block::Sub2x2 | Block::Sub2x4 | Block::Sub4x4 => {
Self::expand_bits(
info,
[FromBits::for_pixel(bits, parts)],
in_texel,
pixel_buf,
);
Self::expand_sub_blocks(pixel_buf, info, info.common_blocks);
}
Block::Yuv422 => {
debug_assert!(matches!(info.in_layout.texel.block, Block::Sub1x2));
debug_assert!(matches!(info.in_layout.texel.parts.num_components(), 3));
Self::expand_yuv422(info, in_texel, pixel_buf);
}
Block::Yuv411 => {
debug_assert!(matches!(info.in_layout.texel.block, Block::Sub1x4));
debug_assert!(matches!(info.in_layout.texel.parts.num_components(), 3));
Self::expand_yuv411(info, in_texel, pixel_buf);
}
other => {
debug_assert!(false, "{:?}", other);
}
}
}
fn expand_bits<const N: usize>(
info: &Info,
bits: [[FromBits; 4]; N],
in_texel: &TexelBuffer,
pixel_buf: &mut TexelBuffer,
) {
const M: usize = SampleBits::MAX_COMPONENTS;
let (encoding, len) = info.in_layout.texel.bits.bit_encoding();
if encoding[..len as usize] == [BitEncoding::UInt; M][..len as usize] {
return Self::expand_ints::<N>(info, bits, in_texel, pixel_buf);
} else if encoding[..len as usize] == [BitEncoding::Float; M][..len as usize] {
return Self::expand_floats(info, bits[0], in_texel, pixel_buf);
} else {
debug_assert!(false, "{:?}", &encoding[..len as usize]);
}
}
fn expand_sub_blocks(pixel_buf: &mut TexelBuffer, info: &Info, order: CommonPixelOrder) {
debug_assert!(matches!(order, CommonPixelOrder::PixelsInRowOrder));
let block = info.in_layout.texel.block;
let (bwidth, bheight) = (block.width(), block.height());
let pixels = pixel_buf.as_mut_texels(<[f32; 4]>::texel());
let texlen = pixels.len() / (bwidth as usize) / (bheight as usize);
let block = bwidth as usize * bheight as usize;
for i in (0..texlen).rev() {
let source = pixels[i];
for target in &mut pixels[block * i..][..block] {
*target = source;
}
}
}
fn expand_ints<const N: usize>(
info: &Info,
bits: [[FromBits; 4]; N],
in_texel: &TexelBuffer,
pixel_buf: &mut TexelBuffer,
) {
struct ExpandAction<'data, T, const N: usize> {
expand: Texel<T>,
expand_fn: fn([u32; 4], &[FromBits; 4]) -> T,
bits: [[FromBits; 4]; N],
in_texel: &'data TexelBuffer,
pixel_buf: &'data mut TexelBuffer,
}
impl<Expanded, const N: usize> GenericTexelAction<()> for ExpandAction<'_, Expanded, N> {
fn run<T>(self, texel: Texel<T>) -> () {
let texel_slice = self.in_texel.as_texels(texel);
let pixel_slice = self.pixel_buf.as_mut_texels(self.expand.array::<N>());
for (texbits, expand) in texel_slice.iter().zip(pixel_slice) {
let pixels = self.bits.map(|bits| {
(self.expand_fn)(bits.map(|b| b.extract_as_lsb(texel, texbits)), &bits)
});
*expand = pixels;
}
}
}
match info.common_pixel {
CommonPixel::F32x4 => info.in_kind.action(ExpandAction {
expand: <[f32; 4]>::texel(),
expand_fn: |num, bits| {
[0, 1, 2, 3].map(|idx| {
let max_val = bits[idx].mask() as u64;
num[idx] as f32 / max_val as f32
})
},
bits,
in_texel,
pixel_buf,
}),
}
let expanded = <[f32; 4]>::texel().array::<N>();
for (pixel_idx, bits) in bits.into_iter().enumerate() {
for (idx, component) in (0..4).zip(bits) {
if component.len > 0 {
continue;
}
let default = if idx == 3 { 1.0 } else { 0.0 };
for pix in pixel_buf.as_mut_texels(expanded) {
pix[pixel_idx][idx] = default;
}
}
}
}
fn expand_floats(
info: &Info,
bits: [FromBits; 4],
in_texel: &TexelBuffer,
pixel_buf: &mut TexelBuffer,
) {
debug_assert!(
matches!(info.common_pixel, CommonPixel::F32x4),
"Improper common choices {:?}",
info.common_pixel
);
let destination = pixel_buf.as_mut_texels(<[f32; 4]>::texel());
let pitch = info.in_kind.size() / 4;
for (&ch, ch_idx) in bits.iter().zip(0..4) {
match ch.len {
0 => continue,
32 => {}
_ => continue,
}
let position = ch.begin / 32;
let texels = in_texel.as_texels(<f32>::texel());
let pitched = texels[position..].chunks(pitch);
for (pix, texel) in destination.iter_mut().zip(pitched) {
pix[ch_idx] = texel[0];
}
}
}
fn expand_yuv422(info: &Info, in_texel: &TexelBuffer, pixel_buf: &mut TexelBuffer) {
struct ExpandYuv422;
impl ExpandYuvLike<4, 2> for ExpandYuv422 {
fn expand<T: Copy>(yuyv: [T; 4], fill: T) -> [[T; 4]; 2] {
let [u, y1, v, y2] = yuyv;
[[y1, u, v, fill], [y2, u, v, fill]]
}
}
Self::expand_yuv_like::<ExpandYuv422, 4, 2>(
info,
in_texel,
pixel_buf,
<[u8; 4]>::texel(),
<[u16; 4]>::texel(),
<[f32; 4]>::texel(),
)
}
fn expand_yuy2(info: &Info, in_texel: &TexelBuffer, pixel_buf: &mut TexelBuffer) {
struct ExpandYuy2;
impl ExpandYuvLike<4, 2> for ExpandYuy2 {
fn expand<T: Copy>(yuyv: [T; 4], fill: T) -> [[T; 4]; 2] {
let [y1, u, y2, v] = yuyv;
[[y1, u, v, fill], [y2, u, v, fill]]
}
}
Self::expand_yuv_like::<ExpandYuy2, 4, 2>(
info,
in_texel,
pixel_buf,
<[u8; 4]>::texel(),
<[u16; 4]>::texel(),
<[f32; 4]>::texel(),
)
}
fn expand_yuv411(info: &Info, in_texel: &TexelBuffer, pixel_buf: &mut TexelBuffer) {
struct ExpandYuv411;
impl ExpandYuvLike<6, 4> for ExpandYuv411 {
fn expand<T: Copy>(yuyv: [T; 6], fill: T) -> [[T; 4]; 4] {
let [u, y1, y2, v, y3, y4] = yuyv;
[
[y1, u, v, fill],
[y2, u, v, fill],
[y3, u, v, fill],
[y4, u, v, fill],
]
}
}
Self::expand_yuv_like::<ExpandYuv411, 6, 4>(
info,
in_texel,
pixel_buf,
<[u8; 6]>::texel(),
<[u16; 6]>::texel(),
<[f32; 6]>::texel(),
)
}
fn expand_yuv_like<F, const N: usize, const M: usize>(
info: &Info,
in_texel: &TexelBuffer,
pixel_buf: &mut TexelBuffer,
tex_u8: Texel<[u8; N]>,
tex_u16: Texel<[u16; N]>,
tex_f32: Texel<[f32; N]>,
) where
F: ExpandYuvLike<N, M>,
{
match info.in_layout.texel.bits {
SampleBits::UInt8x4 => {
let texels = in_texel.as_texels(tex_u8).iter();
match info.common_pixel {
CommonPixel::F32x4 => {
let pixels = pixel_buf
.as_mut_texels(<[f32; 4]>::texel())
.chunks_exact_mut(M);
debug_assert!(pixels.len() == texels.len());
for (texel, pixel_chunk) in texels.zip(pixels) {
let pixels: &mut [_; M] = pixel_chunk.try_into().unwrap();
let expand = F::expand(*texel, u8::MAX);
let remap = |v: u8| (v as f32) / 255.0f32;
*pixels = expand.map(|v| v.map(remap));
}
}
}
}
SampleBits::UInt16x4 => {
let texels = in_texel.as_texels(tex_u16).iter();
match info.common_pixel {
CommonPixel::F32x4 => {
let pixels = pixel_buf
.as_mut_texels(<[f32; 4]>::texel())
.chunks_exact_mut(M);
debug_assert!(pixels.len() == texels.len());
for (texel, pixel_chunk) in texels.zip(pixels) {
let pixels: &mut [_; M] = pixel_chunk.try_into().unwrap();
let expand = F::expand(*texel, u16::MAX);
let remap = |v: u16| (v as f32) / 65535.0f32;
*pixels = expand.map(|v| v.map(remap));
}
}
}
}
SampleBits::Float32x4 => {
let texels = in_texel.as_texels(tex_f32).iter();
match info.common_pixel {
CommonPixel::F32x4 => {
let pixels = pixel_buf
.as_mut_texels(<[f32; 4]>::texel())
.chunks_exact_mut(2);
debug_assert!(pixels.len() == texels.len());
for (texel, pixel_chunk) in texels.zip(pixels) {
let pixels: &mut [_; M] = pixel_chunk.try_into().unwrap();
*pixels = F::expand(*texel, 1.0);
}
}
}
}
other => {
debug_assert!(false, "Bad YUV spec {:?}", other);
}
}
}
fn join_from_info(
info: &Info,
ops: &ConvertOps,
pixel_buf: &TexelBuffer,
out_texels: &mut TexelBuffer,
_: &mut [PlaneTarget],
) {
let TexelBits { bits, parts, block } = info.out_layout.texel;
match block {
Block::Pixel => {
let bits = FromBits::for_pixel(bits, parts);
if let SampleBits::UInt8x4 = info.out_layout.texel.bits {
return Self::join_uint8x4(ops, bits, pixel_buf, out_texels);
} else if let SampleBits::UInt16x4 = info.out_layout.texel.bits {
return Self::join_uint16x4(ops, bits, pixel_buf, out_texels);
} else if let SampleBits::UInt8x3 = info.out_layout.texel.bits {
return Self::join_uint8x3(ops, bits, pixel_buf, out_texels);
} else if let SampleBits::UInt16x3 = info.out_layout.texel.bits {
return Self::join_uint16x3(ops, bits, pixel_buf, out_texels);
} else {
Self::join_bits(info, ops, [bits], pixel_buf, out_texels)
}
}
Block::Pack1x2 => {
let bits = FromBits::for_pixels::<2>(bits, parts);
Self::join_bits(info, ops, bits, pixel_buf, out_texels)
}
Block::Pack1x4 => {
let bits = FromBits::for_pixels::<4>(bits, parts);
Self::join_bits(info, ops, bits, pixel_buf, out_texels)
}
Block::Pack1x8 => {
let bits = FromBits::for_pixels::<8>(bits, parts);
Self::join_bits(info, ops, bits, pixel_buf, out_texels)
}
Block::Sub1x2 | Block::Sub1x4 | Block::Sub2x2 | Block::Sub2x4 | Block::Sub4x4 => {
Self::join_bits(
info,
ops,
[FromBits::for_pixel(bits, parts)],
pixel_buf,
out_texels,
);
Self::join_sub_blocks(out_texels, info, info.common_blocks);
}
Block::Yuv422 => {
debug_assert!(matches!(info.out_layout.texel.block, Block::Sub1x2));
debug_assert!(matches!(info.out_layout.texel.parts.num_components(), 3));
Self::join_yuv422(info, pixel_buf, out_texels)
}
other => {
debug_assert!(false, "{:?}", other);
}
}
}
fn join_bits<const N: usize>(
info: &Info,
ops: &ConvertOps,
bits: [[FromBits; 4]; N],
pixel_buf: &TexelBuffer,
out_texels: &mut TexelBuffer,
) {
const M: usize = SampleBits::MAX_COMPONENTS;
let (encoding, len) = info.out_layout.texel.bits.bit_encoding();
if encoding[..len as usize] == [BitEncoding::UInt; M][..len as usize] {
return Self::join_ints(info, bits, pixel_buf, out_texels);
} else if encoding[..len as usize] == [BitEncoding::Float; M][..len as usize] {
return Self::join_floats(info, bits[0], pixel_buf, out_texels);
} else {
debug_assert!(false, "{:?}", &encoding[..len as usize]);
}
}
fn join_ints<const N: usize>(
info: &Info,
bits: [[FromBits; 4]; N],
pixel_buf: &TexelBuffer,
out_texels: &mut TexelBuffer,
) {
struct JoinAction<'data, T, F: FnMut(&T, &FromBits, u8) -> u32, const N: usize> {
join: Texel<T>,
join_fn: F,
bits: [[FromBits; 4]; N],
out_texels: &'data mut TexelBuffer,
pixel_buf: &'data TexelBuffer,
}
impl<Expanded, F, const N: usize> GenericTexelAction<()> for JoinAction<'_, Expanded, F, N>
where
F: FnMut(&Expanded, &FromBits, u8) -> u32,
{
fn run<T>(mut self, texel: Texel<T>) -> () {
let texel_slice = self.out_texels.as_mut_texels(texel);
let pixel_slice = self.pixel_buf.as_texels(self.join.array::<N>());
for ch in [0u8, 1, 2, 3] {
for (texbits, pixels) in texel_slice.iter_mut().zip(pixel_slice) {
for (pixel_bits, joined) in self.bits.iter().zip(pixels) {
let bits = pixel_bits[ch as usize];
let value = (self.join_fn)(joined, &bits, ch);
bits.insert_as_lsb(texel, texbits, value);
}
}
}
}
}
match info.common_pixel {
CommonPixel::F32x4 => info.out_kind.action(JoinAction {
join: <[f32; 4]>::texel(),
join_fn: |num, bits, idx| {
let max_val = bits.mask();
let raw = (num[(idx & 0x3) as usize] * max_val as f32).round() as u32;
raw.min(max_val)
},
bits,
out_texels,
pixel_buf,
}),
}
}
fn join_sub_blocks(pixel_buf: &mut TexelBuffer, info: &Info, order: CommonPixelOrder) {
debug_assert!(matches!(order, CommonPixelOrder::PixelsInRowOrder));
let block = info.out_layout.texel.block;
let (bwidth, bheight) = (block.width(), block.height());
let pixels = pixel_buf.as_mut_texels(<[f32; 4]>::texel());
let texlen = pixels.len() / (bwidth as usize) / (bheight as usize);
let block = bwidth as usize * bheight as usize;
for i in (0..texlen).rev() {
let mut sum = [0.0, 0.0, 0.0, 0.0];
for &[a, b, c, d] in &pixels[block * i..][..block] {
sum[0] += a;
sum[1] += b;
sum[2] += c;
sum[3] += d;
}
pixels[i] = sum.map(|i| i / block as f32);
}
}
fn join_uint8x4(
ops: &ConvertOps,
bits: [FromBits; 4],
pixel_buf: &TexelBuffer,
out_texels: &mut TexelBuffer,
) {
let src = pixel_buf.as_texels(f32::texel());
let dst = out_texels.as_mut_texels(u8::texel());
for (tex, &pix) in dst.iter_mut().zip(src) {
*tex = ((pix * (u8::MAX as f32)) + 0.5) as u8;
}
let mut shuffle = [0x80u8; 4];
for (idx, bits) in (0u8..4).zip(&bits) {
if bits.len > 0 {
shuffle[(bits.begin / 8) as usize] = idx;
}
}
(ops.shuffle.shuffle_u8x4)(out_texels.as_mut_texels(<[u8; 4]>::texel()), shuffle);
}
fn join_uint16x4(
ops: &ConvertOps,
bits: [FromBits; 4],
pixel_buf: &TexelBuffer,
out_texels: &mut TexelBuffer,
) {
let src = pixel_buf.as_texels(f32::texel());
let dst = out_texels.as_mut_texels(u16::texel());
for (tex, &pix) in dst.iter_mut().zip(src) {
*tex = ((pix * (u16::MAX as f32)) + 0.5) as u16;
}
let mut shuffle = [0x80u8; 4];
for (idx, bits) in (0u8..4).zip(&bits) {
if bits.len > 0 {
shuffle[(bits.begin / 16) as usize] = idx;
}
}
(ops.shuffle.shuffle_u16x4)(out_texels.as_mut_texels(<[u16; 4]>::texel()), shuffle);
}
fn join_uint8x3(
_: &ConvertOps,
bits: [FromBits; 4],
pixel_buf: &TexelBuffer,
out_texels: &mut TexelBuffer,
) {
let src = pixel_buf.as_texels(<[f32; 4]>::texel());
let dst = out_texels.as_mut_texels(<[u8; 3]>::texel());
let mut shuffle = [0x80u8; 3];
for (idx, bits) in (0u8..4).zip(&bits) {
if bits.len > 0 {
shuffle[(bits.begin / 8) as usize] = idx;
}
}
for (tex, pix) in dst.iter_mut().zip(src) {
*tex = shuffle.map(|i| {
let val = *pix.get(i as usize).unwrap_or(&0.0);
(val * u8::MAX as f32 + 0.5) as u8
});
}
}
fn join_uint16x3(
_: &ConvertOps,
bits: [FromBits; 4],
pixel_buf: &TexelBuffer,
out_texels: &mut TexelBuffer,
) {
let src = pixel_buf.as_texels(<[f32; 4]>::texel());
let dst = out_texels.as_mut_texels(<[u16; 3]>::texel());
let mut shuffle = [0x80u8; 3];
for (idx, bits) in (0u8..4).zip(&bits) {
if bits.len > 0 {
shuffle[(bits.begin / 16) as usize] = idx;
}
}
for (tex, pix) in dst.iter_mut().zip(src) {
*tex = shuffle.map(|i| {
let val = *pix.get(i as usize).unwrap_or(&0.0);
(val * u16::MAX as f32 + 0.5) as u16
});
}
}
fn join_floats(
info: &Info,
bits: [FromBits; 4],
pixel_buf: &TexelBuffer,
out_texels: &mut TexelBuffer,
) {
debug_assert!(
matches!(info.common_pixel, CommonPixel::F32x4),
"Improper common choices {:?}",
info.common_pixel
);
let source = pixel_buf.as_texels(<[f32; 4]>::texel());
let pitch = info.out_kind.size() / 4;
for (&ch, ch_idx) in bits.iter().zip(0..4) {
match ch.len {
0 => continue,
32 => {}
_ => continue,
}
let position = ch.begin / 32;
let texels = out_texels.as_mut_texels(<f32>::texel());
let pitched = texels[position..].chunks_mut(pitch);
for (pix, texel) in source.iter().zip(pitched) {
texel[0] = pix[ch_idx];
}
}
}
fn join_yuv422(_: &Info, _: &TexelBuffer, _: &mut TexelBuffer) {
debug_assert!(false);
}
fn action<R>(self, action: impl GenericTexelAction<R>) -> R {
match self {
CommonPixel::F32x4 => action.run(<[f32; 4]>::texel()),
}
}
}
impl CommonColor {
fn cie_xyz_from_info(info: &Info, pixel: &TexelBuffer, xyz: &mut TexelBuffer) {
if !matches!(info.common_pixel, CommonPixel::F32x4) {
return;
}
let texel = <[f32; 4]>::texel();
let pixel = pixel.as_texels(texel);
let xyz = xyz.as_mut_texels(texel);
assert_eq!(
pixel.len(),
xyz.len(),
"Setup create invalid conversion buffer"
);
match info.in_layout.color.as_ref() {
None => xyz.copy_from_slice(pixel),
Some(color) => color.to_xyz_slice(pixel, xyz),
}
}
fn cie_xyz_into_info(info: &Info, xyz: &TexelBuffer, pixel: &mut TexelBuffer) {
if !matches!(info.common_pixel, CommonPixel::F32x4) {
return;
}
let texel = <[f32; 4]>::texel();
let xyz = xyz.as_texels(texel);
let pixel = pixel.as_mut_texels(texel);
assert_eq!(
pixel.len(),
xyz.len(),
"Setup create invalid conversion buffer"
);
match info.out_layout.color.as_ref() {
None => pixel.copy_from_slice(xyz),
Some(color) => color.from_xyz_slice(xyz, pixel),
}
}
}
impl TexelKind {
pub(crate) fn action<R>(self, action: impl GenericTexelAction<R>) -> R {
match self {
TexelKind::U8 => action.run(u8::texel()),
TexelKind::U8x2 => action.run(<[u8; 2]>::texel()),
TexelKind::U8x3 => action.run(<[u8; 3]>::texel()),
TexelKind::U8x4 => action.run(<[u8; 4]>::texel()),
TexelKind::U8x6 => action.run(<[u8; 6]>::texel()),
TexelKind::U16 => action.run(<[u16; 1]>::texel()),
TexelKind::U16x2 => action.run(<[u16; 2]>::texel()),
TexelKind::U16x3 => action.run(<[u16; 3]>::texel()),
TexelKind::U16x4 => action.run(<[u16; 4]>::texel()),
TexelKind::U16x6 => action.run(<[u16; 6]>::texel()),
TexelKind::F32 => action.run(<[f32; 1]>::texel()),
TexelKind::F32x2 => action.run(<[f32; 2]>::texel()),
TexelKind::F32x3 => action.run(<[f32; 3]>::texel()),
TexelKind::F32x4 => action.run(<[f32; 4]>::texel()),
TexelKind::F32x6 => action.run(<[f32; 6]>::texel()),
}
}
fn size(self) -> usize {
struct ToSize;
impl GenericTexelAction<usize> for ToSize {
fn run<T>(self, texel: image_texel::Texel<T>) -> usize {
image_texel::layout::TexelLayout::from(texel).size()
}
}
TexelKind::from(self).action(ToSize)
}
}
impl From<TexelBits> for TexelKind {
fn from(texel: TexelBits) -> Self {
Self::from(texel.bits)
}
}
impl From<SampleBits> for TexelKind {
fn from(bits: SampleBits) -> Self {
use SampleBits::*;
match bits {
Int8 | UInt8 | UInt1x8 | UInt2x4 | UInt332 | UInt233 | UInt4x2 => TexelKind::U8,
Int16 | UInt16 | UInt4x4 | UInt_444 | UInt444_ | UInt565 => TexelKind::U16,
Int8x2 | UInt8x2 => TexelKind::U8x2,
Int8x3 | UInt8x3 | UInt4x6 => TexelKind::U8x3,
Int8x4 | UInt8x4 => TexelKind::U8x4,
UInt8x6 => TexelKind::U8x6,
Int16x2 | UInt16x2 => TexelKind::U16x2,
Int16x3 | UInt16x3 => TexelKind::U16x3,
Int16x4 | UInt16x4 => TexelKind::U16x4,
UInt16x6 => TexelKind::U16x6,
UInt1010102 | UInt2101010 | UInt101010_ | UInt_101010 => TexelKind::U16x2,
Float16x4 => TexelKind::U16x4,
Float32 => TexelKind::F32,
Float32x2 => TexelKind::F32x2,
Float32x3 => TexelKind::F32x3,
Float32x4 => TexelKind::F32x4,
Float32x6 => TexelKind::F32x6,
}
}
}
#[test]
fn from_bits() {
let bits = FromBits::for_pixel(SampleBits::UInt332, SampleParts::Rgb);
let (texel, value) = (u8::texel(), &0b01010110);
assert_eq!(bits[0].extract_as_lsb(texel, value), 0b010);
assert_eq!(bits[1].extract_as_lsb(texel, value), 0b101);
assert_eq!(bits[2].extract_as_lsb(texel, value), 0b10);
assert_eq!(bits[3].extract_as_lsb(texel, value), 0b0);
}
#[test]
fn to_bits() {
let bits = FromBits::for_pixel(SampleBits::UInt332, SampleParts::Rgb);
let (texel, ref mut value) = (u8::texel(), 0);
bits[0].insert_as_lsb(texel, value, 0b010);
bits[1].insert_as_lsb(texel, value, 0b101);
bits[2].insert_as_lsb(texel, value, 0b10);
bits[3].insert_as_lsb(texel, value, 0b0);
assert_eq!(*value, 0b01010110);
}