1use alloc::{boxed::Box, vec::Vec};
6use core::{fmt, ops::Range};
7use image_texel::image::{AtomicImageRef, CellImageRef, ImageMut, ImageRef};
8use image_texel::{AsTexel, Texel, TexelBuffer};
9
10use crate::arch::ShuffleOps;
11use crate::bits::FromBits;
12use crate::color::Color;
13use crate::frame::{BytePlaneAtomics, BytePlaneCells, BytePlaneMut, BytePlaneRef};
14use crate::layout::{
15 BitEncoding, Block, CanvasLayout, PlaneBytes, SampleBits, SampleParts, Texel as TexelBits,
16};
17use crate::Canvas;
18
19pub struct Converter {
21 inner: Box<ConverterRt>,
22}
23
24pub struct ConverterRun<'data> {
30 rt: &'data mut ConverterRt,
32 info: ConvertInfo,
34 recolor: Option<RecolorOps>,
36 int_shuffle_params: IntShuffleParameter,
38 convert_with: TexelConvertWith,
40 buffers: ConverterBuffer<'data>,
42 color_planes: PlaneConfiguration,
44}
45
46struct PlaneConfiguration {
47 in_idx: PlaneIdx,
48 out_idx: PlaneIdx,
49}
50
51struct ColorLayout {
52 in_layout: PlaneBytes,
55 out_layout: PlaneBytes,
56 in_color: Color,
57 out_color: Color,
58}
59
60#[derive(Default)]
61struct ConverterBuffer<'data> {
62 in_plane: Vec<PlaneSource<'data>>,
63 in_cell: Vec<CellSource<'data>>,
64 in_atomic: Vec<AtomicSource<'data>>,
65 out_plane: Vec<PlaneTarget<'data>>,
66 out_cell: Vec<CellTarget<'data>>,
67 out_atomic: Vec<AtomicTarget<'data>>,
68}
69
70#[derive(Clone, Copy, Debug)]
71enum PlaneIdx {
72 Sync(u16),
73 Cell(u16),
74 Atomic(u16),
75}
76
77pub struct ConverterPlaneHandle<'run> {
79 idx: PlaneIdx,
81 direction_in: bool,
83 hdl: &'run mut PlaneConfiguration,
85}
86
87struct ConverterRt {
89 chunk: usize,
94 chunk_count: usize,
100
101 chunk_per_fetch: usize,
103 chunk_per_write: usize,
105
106 super_blocks: TexelBuffer<[u32; 2]>,
107 in_texels: TexelBuffer,
109 in_coords: TexelBuffer<[u32; 2]>,
111 in_index_list: Vec<usize>,
113 in_slices: TexelBuffer<[usize; 2]>,
117 out_texels: TexelBuffer,
119 out_coords: TexelBuffer<[u32; 2]>,
121 out_index_list: Vec<usize>,
123 out_slices: TexelBuffer<[usize; 2]>,
127
128 pixel_in_buffer: TexelBuffer,
130 neutral_color_buffer: TexelBuffer,
132 pixel_out_buffer: TexelBuffer,
134}
135
136struct ConvertInfo {
137 layout: ColorLayout,
139 common_pixel: CommonPixel,
141 #[allow(unused)]
143 common_color: CommonColor,
144 common_blocks: CommonPixelOrder,
146 in_kind: TexelKind,
151 out_kind: TexelKind,
156}
157
158#[derive(Clone, Copy, Debug)]
168pub enum TexelKind {
169 U8,
170 U8x2,
171 U8x3,
172 U8x4,
173 U8x6,
174 U16,
175 U16x2,
176 U16x3,
177 U16x4,
178 U16x6,
179 F32,
180 F32x2,
181 F32x3,
182 F32x4,
183 F32x6,
184}
185
186#[derive(Clone, Debug)]
187pub enum ConversionError {
188 InputLayoutDoesNotMatchPlan,
189 OutputLayoutDoesNotMatchPlan,
190 InputColorDoesNotMatchPlanes,
191 OutputColorDoesNotMatchPlanes,
192 UnsupportedInputLayout,
193 UnsupportedInputColor,
194 UnsupportedOutputLayout,
195 UnsupportedOutputColor,
196}
197
198pub(crate) trait GenericTexelAction<R = ()> {
199 fn run<T>(self, texel: Texel<T>) -> R;
200}
201
202#[derive(Clone, Copy, Debug)]
211enum CommonPixel {
212 F32x4,
213}
214
215#[derive(Clone, Copy, Debug)]
216enum CommonColor {
217 CieXyz,
218}
219
220#[derive(Clone, Copy, Debug)]
223enum CommonPixelOrder {
224 PixelsInRowOrder,
226}
227
228type PlaneSource<'data> = ImageRef<'data, PlaneBytes>;
229type CellSource<'data> = CellImageRef<'data, PlaneBytes>;
230type AtomicSource<'data> = AtomicImageRef<'data, PlaneBytes>;
231
232type PlaneTarget<'data> = ImageMut<'data, PlaneBytes>;
233type CellTarget<'data> = CellImageRef<'data, PlaneBytes>;
234type AtomicTarget<'data> = AtomicImageRef<'data, PlaneBytes>;
235
236struct Sources<'re, 'data> {
237 sync: &'re [PlaneSource<'data>],
238 cell: &'re [CellSource<'data>],
239 atomic: &'re [AtomicSource<'data>],
240}
241
242struct Targets<'re, 'data> {
243 sync: &'re mut [PlaneTarget<'data>],
244 cell: &'re [CellTarget<'data>],
245 atomic: &'re [AtomicTarget<'data>],
246}
247
248struct PlaneIo<'re, 'data> {
249 sources: Sources<'re, 'data>,
250 targets: Targets<'re, 'data>,
251}
252
253struct ConvertOps<'rt> {
259 fill_in_index: fn(&ConvertInfo, &[[u32; 2]], &mut [usize], ChunkSpec),
261 fill_out_index: fn(&ConvertInfo, &[[u32; 2]], &mut [usize], ChunkSpec),
263
264 expand: fn(&ConvertOps, &TexelBuffer, &mut TexelBuffer, PlaneIo),
266 recolor: Option<RecolorOps>,
268 join: fn(&ConvertOps, &TexelBuffer, &mut TexelBuffer, PlaneIo),
270
271 shuffle: ShuffleOps,
273
274 color_in_plane: PlaneIdx,
280 color_out_plane: PlaneIdx,
282
283 int_shuffle_params: IntShuffleParameter,
285
286 texel: TexelConvertWith,
292
293 info: &'rt ConvertInfo,
295}
296
297struct TexelConvertWith {
298 ops: fn(&mut ConverterRt, &ConvertOps, PlaneIo),
299 should_defer_texel_read: bool,
300 should_defer_texel_write: bool,
301}
302
303struct RecolorOps {
304 from: fn(&ConvertInfo, &TexelBuffer, &mut TexelBuffer),
305 into: fn(&ConvertInfo, &TexelBuffer, &mut TexelBuffer),
306}
307
308struct IntShuffleOps {
309 call: fn(&mut ConverterRt, &ConvertOps, PlaneIo),
310 shuffle: [u8; 4],
311 should_defer_texel_read: bool,
312 should_defer_texel_write: bool,
313}
314
315#[derive(Default)]
316struct IntShuffleParameter {
317 shuffle: [u8; 4],
318}
319
320#[derive(Debug)]
321struct SuperTexel {
322 blocks: Range<u32>,
323 in_per_super: u32,
325 out_per_super: u32,
327}
328
329pub(crate) struct ChunkSpec<'ch> {
330 pub chunks: &'ch mut [[usize; 2]],
331 pub chunk_size: usize,
332 pub should_defer_texel_ops: bool,
333}
334
335impl Converter {
336 pub fn new() -> Self {
337 Converter {
338 inner: Box::new(ConverterRt {
339 chunk: 1024,
340 chunk_count: 1,
341 chunk_per_fetch: 0,
342 chunk_per_write: 0,
343 super_blocks: TexelBuffer::default(),
344 in_texels: TexelBuffer::default(),
345 in_coords: TexelBuffer::default(),
346 in_index_list: vec![],
347 in_slices: TexelBuffer::default(),
348 out_texels: TexelBuffer::default(),
349 out_coords: TexelBuffer::default(),
350 out_index_list: vec![],
351 out_slices: TexelBuffer::default(),
352 pixel_in_buffer: TexelBuffer::default(),
353 neutral_color_buffer: TexelBuffer::default(),
354 pixel_out_buffer: TexelBuffer::default(),
355 }),
356 }
357 }
358
359 fn recolor_ops(lhs: &CanvasLayout, rhs: &CanvasLayout) -> Option<RecolorOps> {
360 match (lhs.color.as_ref()?, rhs.color.as_ref()?) {
361 (c0, c1) if c0 == c1 => None,
362 (_, _) => Some(RecolorOps {
364 from: CommonColor::cie_xyz_from_info,
365 into: CommonColor::cie_xyz_into_info,
366 }),
367 }
368 }
369
370 pub fn run_to_completion(
376 &mut self,
377 frame_in: &Canvas,
378 frame_out: &mut Canvas,
379 ) -> Result<(), ConversionError> {
380 let mut plan = self.plan(frame_in.layout().clone(), frame_out.layout().clone())?;
381 plan.use_input(frame_in);
382 plan.use_output(frame_out);
383 plan.run()
384 }
385
386 pub fn plan(
388 &mut self,
389 in_layout: CanvasLayout,
390 out_layout: CanvasLayout,
391 ) -> Result<ConverterRun<'_>, ConversionError> {
392 let info = ConvertInfo {
393 layout: ColorLayout::from_frames(&in_layout, &out_layout)?,
394 common_pixel: CommonPixel::F32x4,
397 common_color: CommonColor::CieXyz,
400 common_blocks: CommonPixelOrder::PixelsInRowOrder,
402 in_kind: TexelKind::from(in_layout.texel.bits),
403 out_kind: TexelKind::from(out_layout.texel.bits),
404 };
405
406 let recolor = Self::recolor_ops(&in_layout, &out_layout);
407
408 let int_shuffle = self
409 .inner
410 .convert_intbuf_with_nocolor_ops(&info)
411 .filter(|_| recolor.is_none());
412
413 let int_shuffle_params;
414 let convert_with: TexelConvertWith = {
416 if let Some(int_ops) = &int_shuffle {
417 int_shuffle_params = IntShuffleParameter {
418 shuffle: int_ops.shuffle,
419 };
420
421 TexelConvertWith {
422 ops: int_ops.call,
423 should_defer_texel_read: int_ops.should_defer_texel_read,
424 should_defer_texel_write: int_ops.should_defer_texel_write,
425 }
426 } else {
427 int_shuffle_params = IntShuffleParameter::default();
428
429 TexelConvertWith {
430 ops: ConverterRt::convert_texelbuf_with_ops,
431 should_defer_texel_read: false,
432 should_defer_texel_write: false,
433 }
434 }
435 };
436
437 Ok(ConverterRun {
438 rt: &mut self.inner,
439 info,
440 recolor,
441 int_shuffle_params,
442 convert_with,
443 buffers: ConverterBuffer::default(),
444 color_planes: PlaneConfiguration {
445 in_idx: PlaneIdx::Sync(0),
446 out_idx: PlaneIdx::Sync(0),
447 },
448 })
449 }
450}
451
452impl<'data> ConverterRun<'data> {
453 pub fn use_input(&mut self, frame_in: &'data Canvas) {
468 let idx = self.buffers.in_plane.len() as u16;
469 let plane = frame_in.plane(0).unwrap();
470 self.buffers.in_plane.push(plane.inner);
471 self.color_planes.in_idx = PlaneIdx::Sync(idx);
472 }
473
474 pub fn use_output(&mut self, frame_out: &'data mut Canvas) {
478 let idx = self.buffers.out_plane.len() as u16;
479 let plane = frame_out.plane_mut(0).unwrap();
480 self.buffers.out_plane.push(plane.inner);
481 self.color_planes.out_idx = PlaneIdx::Sync(idx);
482 }
483
484 pub fn add_plane_in(&mut self, plane: BytePlaneRef<'data>) -> ConverterPlaneHandle<'_> {
486 let idx = self.buffers.in_plane.len() as u16;
487 self.buffers.in_plane.push(plane.inner);
488 ConverterPlaneHandle {
489 idx: PlaneIdx::Sync(idx),
490 direction_in: true,
491 hdl: &mut self.color_planes,
492 }
493 }
494
495 pub fn add_cell_in(&mut self, plane: BytePlaneCells<'data>) -> ConverterPlaneHandle<'_> {
500 let idx = self.buffers.in_cell.len() as u16;
501 self.buffers.in_cell.push(plane.inner);
502 ConverterPlaneHandle {
503 idx: PlaneIdx::Cell(idx),
504 direction_in: true,
505 hdl: &mut self.color_planes,
506 }
507 }
508
509 pub fn add_atomic_in(&mut self, plane: BytePlaneAtomics<'data>) -> ConverterPlaneHandle<'_> {
511 let idx = self.buffers.in_atomic.len() as u16;
512 self.buffers.in_atomic.push(plane.inner);
513 ConverterPlaneHandle {
514 idx: PlaneIdx::Atomic(idx),
515 direction_in: true,
516 hdl: &mut self.color_planes,
517 }
518 }
519
520 pub fn add_plane_out(&mut self, plane: BytePlaneMut<'data>) -> ConverterPlaneHandle<'_> {
521 let idx = self.buffers.out_plane.len() as u16;
522 self.buffers.out_plane.push(plane.inner);
523 ConverterPlaneHandle {
524 idx: PlaneIdx::Sync(idx),
525 direction_in: false,
526 hdl: &mut self.color_planes,
527 }
528 }
529
530 pub fn add_cell_out(&mut self, plane: BytePlaneCells<'data>) -> ConverterPlaneHandle<'_> {
531 let idx = self.buffers.out_cell.len() as u16;
532 self.buffers.out_cell.push(plane.inner);
533 ConverterPlaneHandle {
534 idx: PlaneIdx::Cell(idx),
535 direction_in: false,
536 hdl: &mut self.color_planes,
537 }
538 }
539
540 pub fn add_atomic_out(&mut self, plane: BytePlaneAtomics<'data>) -> ConverterPlaneHandle<'_> {
541 let idx = self.buffers.out_atomic.len() as u16;
542 self.buffers.out_atomic.push(plane.inner);
543 ConverterPlaneHandle {
544 idx: PlaneIdx::Atomic(idx),
545 direction_in: false,
546 hdl: &mut self.color_planes,
547 }
548 }
549
550 pub fn run(self) -> Result<(), ConversionError> {
555 let (ii, oi) = (self.color_planes.in_idx, self.color_planes.out_idx);
556 self.run_between(ii, oi)
557 }
558
559 fn run_between(
561 mut self,
562 color_in_plane: PlaneIdx,
563 color_out_plane: PlaneIdx,
564 ) -> Result<(), ConversionError> {
565 if *self.layout_in(color_in_plane)? != self.info.layout.in_layout {
566 return Err(ConversionError::InputColorDoesNotMatchPlanes);
567 }
568
569 if *self.layout_out(color_out_plane)? != self.info.layout.out_layout {
570 return Err(ConversionError::OutputColorDoesNotMatchPlanes);
571 }
572
573 if !matches!(color_in_plane, PlaneIdx::Sync(_))
578 || !matches!(color_out_plane, PlaneIdx::Sync(_))
579 {
580 self.convert_with = TexelConvertWith {
581 ops: ConverterRt::convert_texelbuf_with_ops,
582 should_defer_texel_read: false,
583 should_defer_texel_write: false,
584 };
585 }
586
587 let ops = ConvertOps {
588 fill_in_index: ConverterRt::index_from_in_info,
589 fill_out_index: ConverterRt::index_from_out_info,
590 expand: CommonPixel::expand_from_info,
591 recolor: self.recolor,
592 join: CommonPixel::join_from_info,
593 shuffle: ShuffleOps::default().with_arch(),
594 color_in_plane,
595 color_out_plane,
596 int_shuffle_params: self.int_shuffle_params,
597 texel: self.convert_with,
598 info: &self.info,
599 };
600
601 let plane_io = PlaneIo {
602 sources: Sources {
603 sync: &self.buffers.in_plane,
604 cell: &self.buffers.in_cell,
605 atomic: &self.buffers.in_atomic,
606 },
607 targets: Targets {
608 sync: &mut self.buffers.out_plane,
609 cell: &self.buffers.out_cell,
610 atomic: &self.buffers.out_atomic,
611 },
612 };
613
614 Ok(self.rt.with_filled_texels(&self.info, &ops, plane_io))
616 }
617
618 fn layout_in(&self, color_in_plane: PlaneIdx) -> Result<&PlaneBytes, ConversionError> {
619 Ok(match color_in_plane {
620 PlaneIdx::Sync(idx) => self
621 .buffers
622 .in_plane
623 .get(usize::from(idx))
624 .ok_or(ConversionError::InputLayoutDoesNotMatchPlan)?
625 .layout(),
626 PlaneIdx::Cell(idx) => self
627 .buffers
628 .in_cell
629 .get(usize::from(idx))
630 .ok_or(ConversionError::InputLayoutDoesNotMatchPlan)?
631 .layout(),
632 PlaneIdx::Atomic(idx) => self
633 .buffers
634 .in_atomic
635 .get(usize::from(idx))
636 .ok_or(ConversionError::InputLayoutDoesNotMatchPlan)?
637 .layout(),
638 })
639 }
640
641 fn layout_out(&self, color_out_plane: PlaneIdx) -> Result<&PlaneBytes, ConversionError> {
642 Ok(match color_out_plane {
643 PlaneIdx::Sync(idx) => self
644 .buffers
645 .out_plane
646 .get(usize::from(idx))
647 .ok_or(ConversionError::OutputLayoutDoesNotMatchPlan)?
648 .layout(),
649 PlaneIdx::Cell(idx) => self
650 .buffers
651 .out_cell
652 .get(usize::from(idx))
653 .ok_or(ConversionError::OutputLayoutDoesNotMatchPlan)?
654 .layout(),
655 PlaneIdx::Atomic(idx) => self
656 .buffers
657 .out_atomic
658 .get(usize::from(idx))
659 .ok_or(ConversionError::OutputLayoutDoesNotMatchPlan)?
660 .layout(),
661 })
662 }
663}
664
665impl ConverterPlaneHandle<'_> {
666 pub fn set_as_color(self) {
673 if self.direction_in {
674 self.hdl.in_idx = self.idx;
675 } else {
676 self.hdl.out_idx = self.idx;
677 }
678 }
679}
680
681impl ConverterRt {
682 fn convert_texelbuf_with_ops(&mut self, ops: &ConvertOps, mut plane_io: PlaneIo) {
688 (ops.expand)(
689 ops,
690 &self.in_texels,
691 &mut self.pixel_in_buffer,
692 plane_io.borrow(),
693 );
694
695 let pixel_out = if let Some(ref recolor) = ops.recolor {
696 (recolor.from)(
697 &ops.info,
698 &self.pixel_in_buffer,
699 &mut self.neutral_color_buffer,
700 );
701 (recolor.into)(
702 &ops.info,
703 &self.neutral_color_buffer,
704 &mut self.pixel_out_buffer,
705 );
706 &self.pixel_out_buffer
707 } else {
708 &self.pixel_in_buffer
709 };
710
711 (ops.join)(ops, pixel_out, &mut self.out_texels, plane_io.borrow());
713 }
714
715 fn convert_intbuf_with_nocolor_ops(&mut self, info: &ConvertInfo) -> Option<IntShuffleOps> {
725 fn determine_shuffle(inp: SampleParts, outp: SampleParts) -> Option<[u8; 4]> {
736 let mut ch_from_common = [0x80u8; 4];
737 let mut ch_from_input = [0x80u8; 4];
738
739 for ((ch, common_pos), idx) in inp.channels().zip(0..4) {
740 if ch.is_some() {
741 ch_from_input[common_pos as usize] = idx;
742 }
743 }
744
745 for ((ch, common_pos), idx) in outp.channels().zip(0..4) {
746 if ch.is_some() {
747 ch_from_common[idx] = ch_from_input[common_pos as usize];
748 }
749 }
750
751 Some(ch_from_common)
752 }
753
754 let in_texel = &info.layout.in_layout.texel;
755 let out_texel = &info.layout.out_layout.texel;
756
757 if in_texel.block != Block::Pixel || out_texel.block != Block::Pixel {
758 return None;
759 }
760
761 if info.layout.in_color != info.layout.out_color {
763 return None;
764 }
765
766 let shuffle = determine_shuffle(in_texel.parts, out_texel.parts)?;
767
768 trait Shuffle<T, const N: usize, const M: usize> {
769 fn run(_: &ConvertOps, _: &[[T; N]], _: &mut [[T; M]], _: [u8; 4]);
770 }
771
772 fn shuffle_with_texel<T, S: Shuffle<T, N, M>, const N: usize, const M: usize>(
773 that: &mut ConverterRt,
774 ops: &ConvertOps,
775 io: PlaneIo,
776 ) where
777 T: AsTexel,
778 {
779 debug_assert_eq!(
780 that.chunk, that.chunk_per_fetch,
781 "Inconsistent usage of channel shuffle, only applicable to matching texels"
782 );
783
784 debug_assert_eq!(
785 that.chunk, that.chunk_per_write,
786 "Inconsistent usage of channel shuffle, only applicable to matching texels"
787 );
788
789 let shuffle = ops.int_shuffle_params.shuffle;
790
791 let in_texel = T::texel().array::<N>();
792 let out_texel = T::texel().array::<M>();
793
794 let i_idx = ops.color_in_plane.into_index();
795 let o_idx = ops.color_out_plane.into_index();
796
797 let source_texels = io.sources.sync[i_idx].as_texels(in_texel);
798 let target_texels = io.targets.sync[o_idx].as_mut_texels(out_texel);
799
800 let in_texels = that.in_texels.as_texels(in_texel);
801 let out_texels = that.out_texels.as_mut_texels(out_texel);
802
803 let in_slices = that.in_slices.iter_mut();
804 let out_slices = that.out_slices.iter_mut();
805 let chunks = (0..in_texels.len()).step_by(that.chunk);
806
807 for ((islice, oslice), chunk_start) in in_slices.zip(out_slices).zip(chunks) {
808 let length = in_texels[chunk_start..].len().min(that.chunk);
809
810 let input_slice = if islice[1] > 0 {
811 debug_assert!(length == islice[1]);
812 let length = core::mem::replace(&mut islice[1], 0);
813 &source_texels[islice[0]..][..length]
814 } else {
815 &in_texels[chunk_start..][..length]
816 };
817
818 let output_slice = if oslice[1] > 0 {
819 debug_assert!(length == oslice[1]);
820 let length = core::mem::replace(&mut oslice[1], 0);
821 &mut target_texels[oslice[0]..][..length]
822 } else {
823 &mut out_texels[chunk_start..][..length]
824 };
825
826 S::run(ops, input_slice, output_slice, shuffle)
827 }
828 }
829
830 struct ShuffleInt8;
831 struct ShuffleInt16;
832
833 impl Shuffle<u8, 4, 4> for ShuffleInt8 {
834 fn run(ops: &ConvertOps, inp: &[[u8; 4]], outp: &mut [[u8; 4]], shuffle: [u8; 4]) {
835 outp.copy_from_slice(inp);
836 (ops.shuffle.shuffle_u8x4)(outp, shuffle);
837 }
838 }
839
840 impl Shuffle<u8, 3, 4> for ShuffleInt8 {
841 fn run(ops: &ConvertOps, inp: &[[u8; 3]], outp: &mut [[u8; 4]], shuffle: [u8; 4]) {
842 (ops.shuffle.shuffle_u8x3_to_u8x4)(inp, outp, shuffle);
843 }
844 }
845
846 impl Shuffle<u8, 4, 3> for ShuffleInt8 {
847 fn run(ops: &ConvertOps, inp: &[[u8; 4]], outp: &mut [[u8; 3]], shuffle: [u8; 4]) {
848 let shuffle = [shuffle[0], shuffle[1], shuffle[2]];
849 (ops.shuffle.shuffle_u8x4_to_u8x3)(inp, outp, shuffle);
850 }
851 }
852
853 impl Shuffle<u16, 4, 4> for ShuffleInt16 {
854 fn run(ops: &ConvertOps, inp: &[[u16; 4]], outp: &mut [[u16; 4]], shuffle: [u8; 4]) {
855 outp.copy_from_slice(inp);
856 (ops.shuffle.shuffle_u16x4)(outp, shuffle);
857 }
858 }
859
860 impl Shuffle<u16, 3, 4> for ShuffleInt16 {
861 fn run(ops: &ConvertOps, inp: &[[u16; 3]], outp: &mut [[u16; 4]], shuffle: [u8; 4]) {
862 (ops.shuffle.shuffle_u16x3_to_u16x4)(inp, outp, shuffle);
863 }
864 }
865
866 impl Shuffle<u16, 4, 3> for ShuffleInt16 {
867 fn run(ops: &ConvertOps, inp: &[[u16; 4]], outp: &mut [[u16; 3]], shuffle: [u8; 4]) {
868 let shuffle = [shuffle[0], shuffle[1], shuffle[2]];
869 (ops.shuffle.shuffle_u16x4_to_u16x3)(inp, outp, shuffle);
870 }
871 }
872
873 Some(match (in_texel.bits, out_texel.bits) {
874 (SampleBits::UInt8x4, SampleBits::UInt8x4)
875 | (SampleBits::Int8x4, SampleBits::Int8x4) => IntShuffleOps {
876 call: shuffle_with_texel::<u8, ShuffleInt8, 4, 4>,
877 shuffle,
878 should_defer_texel_read: true,
879 should_defer_texel_write: true,
880 },
881 (SampleBits::UInt8x3, SampleBits::UInt8x4)
882 | (SampleBits::Int8x3, SampleBits::Int8x4) => IntShuffleOps {
883 call: shuffle_with_texel::<u8, ShuffleInt8, 3, 4>,
884 shuffle,
885 should_defer_texel_read: true,
886 should_defer_texel_write: true,
887 },
888 (SampleBits::UInt8x4, SampleBits::UInt8x3)
889 | (SampleBits::Int8x4, SampleBits::Int8x3) => IntShuffleOps {
890 call: shuffle_with_texel::<u8, ShuffleInt8, 4, 3>,
891 shuffle,
892 should_defer_texel_read: true,
893 should_defer_texel_write: true,
894 },
895
896 (SampleBits::UInt16x4, SampleBits::UInt16x4)
898 | (SampleBits::Int16x4, SampleBits::Int16x4) => IntShuffleOps {
899 call: shuffle_with_texel::<u16, ShuffleInt16, 4, 4>,
900 shuffle,
901 should_defer_texel_read: true,
902 should_defer_texel_write: true,
903 },
904 (SampleBits::UInt16x3, SampleBits::UInt16x4)
905 | (SampleBits::Int16x3, SampleBits::Int16x4) => IntShuffleOps {
906 call: shuffle_with_texel::<u16, ShuffleInt16, 3, 4>,
907 shuffle,
908 should_defer_texel_read: true,
909 should_defer_texel_write: true,
910 },
911 (SampleBits::UInt16x4, SampleBits::UInt16x3)
912 | (SampleBits::Int16x4, SampleBits::Int16x3) => IntShuffleOps {
913 call: shuffle_with_texel::<u16, ShuffleInt16, 4, 3>,
914 shuffle,
915 should_defer_texel_read: true,
916 should_defer_texel_write: true,
917 },
918 _ => return None,
919 })
920 }
921
922 fn with_filled_texels(&mut self, info: &ConvertInfo, ops: &ConvertOps, mut frame_io: PlaneIo) {
924 assert!(self.chunk > 0);
926 assert!(self.chunk_count > 0);
927
928 let (sb_x, sb_y) = self.super_texel(info);
934 let mut blocks = Self::blocks(sb_x.blocks.clone(), sb_y.blocks.clone());
935
936 assert!(sb_x.in_per_super > 0);
937 assert!(sb_x.in_per_super > 0);
938 assert!(sb_x.out_per_super > 0);
939 assert!(sb_y.out_per_super > 0);
940
941 self.chunk_per_fetch = self.chunk * (sb_x.in_per_super * sb_y.in_per_super) as usize;
942 self.chunk_per_write = self.chunk * (sb_x.out_per_super * sb_y.out_per_super) as usize;
943
944 assert!(self.chunk_per_fetch > 0);
945 assert!(self.chunk_per_write > 0);
946
947 loop {
948 let at_once = self.chunk * self.chunk_count;
949 self.super_blocks.resize(at_once);
950 let actual = blocks(self.super_blocks.as_mut_slice());
951 self.super_blocks.resize(actual);
952
953 if self.super_blocks.is_empty() {
954 break;
955 }
956
957 self.generate_coords(info, ops, &sb_x, &sb_y);
958 self.reserve_buffers(info, ops);
959 self.read_texels(info, ops, frame_io.borrow());
961 (ops.texel.ops)(self, ops, frame_io.borrow());
962 self.write_texels(info, ops, frame_io.borrow());
964 }
965 }
966
967 fn super_texel(&self, info: &ConvertInfo) -> (SuperTexel, SuperTexel) {
968 let b0 = info.layout.in_layout.texel.block;
969 let b1 = info.layout.out_layout.texel.block;
970
971 let super_width = core::cmp::max(b0.width(), b1.width());
972 let super_height = core::cmp::max(b0.height(), b1.height());
973
974 assert!(super_width % b0.width() == 0);
976 assert!(super_width % b1.width() == 0);
977 assert!(super_height % b0.height() == 0);
978 assert!(super_height % b1.height() == 0);
979
980 let sampled_with = |w, bs| w / bs + if w % bs == 0 { 0 } else { 1 };
981
982 let sb_width = sampled_with(info.layout.in_layout.width, super_width);
983 let sb_height = sampled_with(info.layout.in_layout.height, super_height);
984
985 (
986 SuperTexel {
987 blocks: 0..sb_height,
988 in_per_super: super_height / b0.height(),
989 out_per_super: super_height / b1.height(),
990 },
991 SuperTexel {
992 blocks: 0..sb_width,
993 in_per_super: super_width / b0.width(),
994 out_per_super: super_width / b1.width(),
995 },
996 )
997 }
998
999 fn generate_coords(
1000 &mut self,
1001 info: &ConvertInfo,
1002 ops: &ConvertOps,
1003 sb_x: &SuperTexel,
1004 sb_y: &SuperTexel,
1005 ) {
1006 fn is_trivial_super(sup: &SuperTexel) -> bool {
1007 sup.in_per_super == 1 && sup.out_per_super == 1
1008 }
1009
1010 self.in_coords.resize(0);
1011 self.out_coords.resize(0);
1012
1013 if is_trivial_super(sb_x) && is_trivial_super(sb_y) {
1014 self.in_coords.resize(self.super_blocks.len());
1020 self.out_coords.resize(self.super_blocks.len());
1021 self.in_coords
1022 .as_mut_slice()
1023 .copy_from_slice(&self.super_blocks);
1024 self.out_coords
1025 .as_mut_slice()
1026 .copy_from_slice(&self.super_blocks);
1027 } else {
1028 let in_chunk_len = (sb_x.in_per_super * sb_y.in_per_super) as usize;
1029 self.in_coords
1030 .resize(self.super_blocks.len() * in_chunk_len);
1031 let out_chunk_len = (sb_x.out_per_super * sb_y.out_per_super) as usize;
1032 self.out_coords
1033 .resize(self.super_blocks.len() * out_chunk_len);
1034
1035 let mut in_chunks = self.in_coords.as_mut_slice().chunks_exact_mut(in_chunk_len);
1039 let mut out_chunks = self
1040 .out_coords
1041 .as_mut_slice()
1042 .chunks_exact_mut(out_chunk_len);
1043
1044 for &[bx, by] in self.super_blocks.as_slice().iter() {
1045 let (sx, sy) = (bx * sb_x.in_per_super, by * sb_y.in_per_super);
1046 if let Some(chunk) = in_chunks.next() {
1047 Self::blocks(0..sb_x.in_per_super, 0..sb_y.in_per_super)(chunk);
1048 for p in chunk.iter_mut() {
1049 let [ix, iy] = *p;
1050 *p = [sx + ix, sy + iy];
1051 }
1052 }
1053
1054 let (sx, sy) = (bx * sb_x.out_per_super, by * sb_y.out_per_super);
1055 if let Some(chunk) = out_chunks.next() {
1056 Self::blocks(0..sb_x.out_per_super, 0..sb_y.out_per_super)(chunk);
1057 for p in chunk.iter_mut() {
1058 let [ox, oy] = *p;
1059 *p = [sx + ox, sy + oy];
1060 }
1061 }
1062 }
1063 }
1064
1065 self.in_index_list.resize_with(self.in_coords.len(), || 0);
1066 self.out_index_list.resize_with(self.out_coords.len(), || 0);
1067
1068 self.in_slices.resize(self.chunk_count);
1069 self.out_slices.resize(self.chunk_count);
1070
1071 let in_chunk = ChunkSpec {
1072 chunks: self.in_slices.as_mut_slice(),
1073 chunk_size: self.chunk_per_fetch,
1074 should_defer_texel_ops: ops.texel.should_defer_texel_read,
1075 };
1076
1077 let out_chunk = ChunkSpec {
1078 chunks: self.out_slices.as_mut_slice(),
1079 chunk_size: self.chunk_per_write,
1080 should_defer_texel_ops: ops.texel.should_defer_texel_write,
1081 };
1082
1083 (ops.fill_in_index)(
1084 &info,
1085 self.in_coords.as_slice(),
1086 &mut self.in_index_list,
1087 in_chunk,
1088 );
1089
1090 (ops.fill_out_index)(
1091 &info,
1092 self.out_coords.as_slice(),
1093 &mut self.out_index_list,
1094 out_chunk,
1095 );
1096 }
1097
1098 fn reserve_buffers(&mut self, info: &ConvertInfo, ops: &ConvertOps) {
1099 struct ResizeAction<'data>(&'data mut TexelBuffer, usize);
1100
1101 impl GenericTexelAction for ResizeAction<'_> {
1102 fn run<T>(self, texel: Texel<T>) {
1103 self.0.resize_for_texel(self.1, texel)
1104 }
1105 }
1106
1107 let num_in_texels = self.in_coords.len();
1108 let in_block = info.layout.in_layout.texel.block;
1109 let in_pixels = (in_block.width() * in_block.height()) as usize * num_in_texels;
1110 info.in_kind
1111 .action(ResizeAction(&mut self.in_texels, num_in_texels));
1112
1113 let num_out_texels = self.out_coords.len();
1114 let out_block = info.layout.out_layout.texel.block;
1115 let out_pixels = (out_block.width() * out_block.height()) as usize * num_out_texels;
1116 info.out_kind
1117 .action(ResizeAction(&mut self.out_texels, num_out_texels));
1118
1119 debug_assert!(
1120 in_pixels == out_pixels,
1121 "Mismatching in super block layout: {} {}",
1122 in_pixels,
1123 out_pixels
1124 );
1125
1126 let pixels = in_pixels.max(out_pixels);
1127 info.common_pixel
1128 .action(ResizeAction(&mut self.pixel_in_buffer, pixels));
1129
1130 if let Some(_) = ops.recolor {
1131 info.common_pixel
1132 .action(ResizeAction(&mut self.neutral_color_buffer, pixels));
1133 info.common_pixel
1134 .action(ResizeAction(&mut self.pixel_out_buffer, pixels));
1135 }
1136 }
1137
1138 fn read_texels(&mut self, info: &ConvertInfo, ops: &ConvertOps, from: PlaneIo) {
1139 fn fetch_from_texel_array<T>(
1140 from: &PlaneSource,
1141 idx: &[usize],
1142 into: &mut TexelBuffer,
1143 range: Range<usize>,
1144 texel: Texel<T>,
1145 ) {
1146 into.resize_for_texel(idx.len(), texel);
1147 let idx = idx[range.clone()].iter();
1148 let texels = &mut into.as_mut_texels(texel)[range];
1149
1150 let texel_slice = from.as_texels(texel);
1151 for (&index, into) in idx.zip(texels) {
1152 if let Some(from) = texel_slice.get(index) {
1153 *into = texel.copy_val(from);
1154 }
1155 }
1156 }
1157
1158 fn fetch_from_texel_cell<T>(
1159 from: &CellSource,
1160 idx: &[usize],
1161 into: &mut TexelBuffer,
1162 range: Range<usize>,
1163 texel: Texel<T>,
1164 ) {
1165 into.resize_for_texel(idx.len(), texel);
1166 let idx = idx[range.clone()].iter();
1167 let texels = &mut into.as_mut_texels(texel)[range];
1168 let texel_slice = from.as_texels(texel).as_slice_of_cells();
1169
1170 for (&index, into) in idx.zip(texels) {
1171 if let Some(from) = texel_slice.get(index) {
1172 *into = texel.copy_cell(from);
1173 }
1174 }
1175 }
1176
1177 fn fetch_from_texel_atomics<T>(
1178 from: &AtomicSource,
1179 idx: &[usize],
1180 into: &mut TexelBuffer,
1181 range: Range<usize>,
1182 texel: Texel<T>,
1183 ) {
1184 into.resize_for_texel(idx.len(), texel);
1185 let idx = idx[range.clone()].iter();
1186 let texels = &mut into.as_mut_texels(texel)[range];
1187 let texel_slice = from.as_texels(texel);
1188
1189 for (&index, into) in idx.zip(texels) {
1190 if let Some(from) = texel_slice.get(index..index + 1) {
1191 from.write_to_slice(core::slice::from_mut(into));
1192 }
1193 }
1194 }
1195
1196 struct ReadUnit<'plane, 'data> {
1197 from: &'plane PlaneSource<'data>,
1198 idx: &'plane [usize],
1199 into: &'plane mut TexelBuffer,
1200 range: Range<usize>,
1201 }
1202
1203 impl GenericTexelAction for ReadUnit<'_, '_> {
1204 fn run<T>(self, texel: Texel<T>) {
1205 fetch_from_texel_array(self.from, self.idx, self.into, self.range, texel)
1206 }
1207 }
1208
1209 struct ReadCell<'plane, 'data> {
1210 from: &'plane CellSource<'data>,
1211 idx: &'plane [usize],
1212 into: &'plane mut TexelBuffer,
1213 range: Range<usize>,
1214 }
1215
1216 impl GenericTexelAction for ReadCell<'_, '_> {
1217 fn run<T>(self, texel: Texel<T>) {
1218 fetch_from_texel_cell(self.from, self.idx, self.into, self.range, texel)
1219 }
1220 }
1221
1222 struct ReadAtomic<'plane, 'data> {
1223 from: &'plane AtomicSource<'data>,
1224 idx: &'plane [usize],
1225 into: &'plane mut TexelBuffer,
1226 range: Range<usize>,
1227 }
1228
1229 impl GenericTexelAction for ReadAtomic<'_, '_> {
1230 fn run<T>(self, texel: Texel<T>) {
1231 fetch_from_texel_atomics(self.from, self.idx, self.into, self.range, texel)
1232 }
1233 }
1234
1235 if ops.texel.should_defer_texel_read {
1236 debug_assert!(matches!(ops.color_in_plane, PlaneIdx::Sync(_)));
1237 let chunks = self.in_slices.as_mut_slice();
1246 let indexes = self.in_index_list.chunks(self.chunk_per_fetch);
1247 let range = (0..self.in_index_list.len()).step_by(self.chunk_per_fetch);
1248
1249 for (chunk, (indexes, start)) in chunks.iter_mut().zip(indexes.zip(range)) {
1250 let [_, available] = chunk;
1251
1252 if *available == indexes.len() {
1253 continue;
1254 }
1255
1256 *available = 0;
1260 info.in_kind.action(ReadUnit {
1261 from: &from.sources.sync[ops.color_in_plane.into_index()],
1262 idx: &self.in_index_list,
1263 into: &mut self.in_texels,
1264 range: start..start + indexes.len(),
1265 });
1266 }
1267 } else {
1268 match ops.color_in_plane {
1271 PlaneIdx::Sync(_) => {
1272 info.in_kind.action(ReadUnit {
1273 from: &from.sources.sync[ops.color_in_plane.into_index()],
1274 idx: &self.in_index_list,
1275 into: &mut self.in_texels,
1276 range: 0..self.in_index_list.len(),
1277 });
1278 }
1279 PlaneIdx::Cell(_) => {
1280 info.in_kind.action(ReadCell {
1281 from: &from.sources.cell[ops.color_in_plane.into_index()],
1282 idx: &self.in_index_list,
1283 into: &mut self.in_texels,
1284 range: 0..self.in_index_list.len(),
1285 });
1286 }
1287 PlaneIdx::Atomic(_) => {
1288 info.in_kind.action(ReadAtomic {
1289 from: &from.sources.atomic[ops.color_in_plane.into_index()],
1290 idx: &self.in_index_list,
1291 into: &mut self.in_texels,
1292 range: 0..self.in_index_list.len(),
1293 });
1294 }
1295 }
1296 }
1297 }
1298
1299 fn write_texels(&mut self, info: &ConvertInfo, ops: &ConvertOps, into: PlaneIo) {
1301 fn write_for_texel_array<T>(
1302 into: &mut PlaneTarget,
1303 idx: &[usize],
1304 from: &TexelBuffer,
1305 range: Range<usize>,
1306 texel: Texel<T>,
1307 ) {
1308 let idx = idx[range.clone()].iter();
1311 let texels = &from.as_texels(texel)[range];
1312 let texel_slice = into.as_mut_texels(texel);
1313
1314 debug_assert_eq!(idx.len(), texels.len());
1316
1317 for (&index, from) in idx.zip(texels) {
1318 if let Some(into) = texel_slice.get_mut(index) {
1319 *into = texel.copy_val(from);
1320 }
1321 }
1322 }
1323
1324 fn write_for_texel_cells<T>(
1325 into: &CellTarget,
1326 idx: &[usize],
1327 from: &TexelBuffer,
1328 range: Range<usize>,
1329 texel: Texel<T>,
1330 ) {
1331 let idx = idx[range.clone()].iter();
1334 let texels = &from.as_texels(texel)[range];
1335 let texel_slice = into.as_texels(texel).as_slice_of_cells();
1336
1337 debug_assert_eq!(idx.len(), texels.len());
1339 for (&index, from) in idx.zip(texels) {
1342 if let Some(into) = texel_slice.get(index) {
1343 into.set(texel.copy_val(from));
1344 }
1345 }
1346 }
1347
1348 fn write_for_texel_atomics<T>(
1349 into: &AtomicTarget,
1350 idx: &[usize],
1351 from: &TexelBuffer,
1352 range: Range<usize>,
1353 texel: Texel<T>,
1354 ) {
1355 let idx = idx[range.clone()].iter();
1358 let texels = &from.as_texels(texel)[range];
1359 let texel_slice = into.as_texels(texel);
1360
1361 debug_assert_eq!(idx.len(), texels.len());
1363 for (&index, from) in idx.zip(texels) {
1366 if let Some(into) = texel_slice.get(index..index + 1) {
1367 into.read_from_slice(core::slice::from_ref(&from));
1368 }
1369 }
1370 }
1371
1372 struct WriteUnit<'plane, 'data> {
1373 into: &'plane mut PlaneTarget<'data>,
1374 idx: &'plane [usize],
1375 from: &'plane TexelBuffer,
1376 range: Range<usize>,
1377 }
1378
1379 impl GenericTexelAction for WriteUnit<'_, '_> {
1380 fn run<T>(self, texel: Texel<T>) {
1381 write_for_texel_array(self.into, self.idx, self.from, self.range, texel)
1382 }
1383 }
1384
1385 struct WriteCell<'plane, 'data> {
1386 into: &'plane CellTarget<'data>,
1387 idx: &'plane [usize],
1388 from: &'plane TexelBuffer,
1389 range: Range<usize>,
1390 }
1391
1392 impl GenericTexelAction for WriteCell<'_, '_> {
1393 fn run<T>(self, texel: Texel<T>) {
1394 write_for_texel_cells(self.into, self.idx, self.from, self.range, texel)
1395 }
1396 }
1397
1398 struct WriteAtomic<'plane, 'data> {
1399 into: &'plane AtomicTarget<'data>,
1400 idx: &'plane [usize],
1401 from: &'plane TexelBuffer,
1402 range: Range<usize>,
1403 }
1404
1405 impl GenericTexelAction for WriteAtomic<'_, '_> {
1406 fn run<T>(self, texel: Texel<T>) {
1407 write_for_texel_atomics(self.into, self.idx, self.from, self.range, texel)
1408 }
1409 }
1410
1411 if ops.texel.should_defer_texel_write {
1412 debug_assert!(matches!(ops.color_out_plane, PlaneIdx::Sync(_)));
1413
1414 let chunks = self.out_slices.as_slice();
1421 let indexes = self.out_index_list.chunks(self.chunk_per_write);
1422 let range = (0..self.out_index_list.len()).step_by(self.chunk_per_write);
1423
1424 for (&chunk, (indexes, start)) in chunks.iter().zip(indexes.zip(range)) {
1425 let [_, unwritten] = chunk;
1426 debug_assert!(unwritten <= indexes.len());
1427
1428 if unwritten > indexes.len() {
1429 continue;
1430 }
1431
1432 if unwritten == 0 {
1433 continue;
1434 }
1435
1436 let offset = indexes.len() - unwritten;
1437 info.out_kind.action(WriteUnit {
1438 into: &mut into.targets.sync[ops.color_out_plane.into_index()],
1439 idx: &self.out_index_list,
1440 from: &self.out_texels,
1441 range: start + offset..start + indexes.len(),
1442 });
1443 }
1444 } else {
1445 match ops.color_out_plane {
1446 PlaneIdx::Sync(_) => {
1447 info.out_kind.action(WriteUnit {
1448 into: &mut into.targets.sync[ops.color_out_plane.into_index()],
1449 idx: &self.out_index_list,
1450 from: &self.out_texels,
1451 range: 0..self.out_index_list.len(),
1452 });
1453 }
1454 PlaneIdx::Cell(_) => {
1455 info.out_kind.action(WriteCell {
1456 into: &into.targets.cell[ops.color_out_plane.into_index()],
1457 idx: &self.out_index_list,
1458 from: &self.out_texels,
1459 range: 0..self.out_index_list.len(),
1460 });
1461 }
1462 PlaneIdx::Atomic(_) => {
1463 info.out_kind.action(WriteAtomic {
1464 into: &into.targets.atomic[ops.color_out_plane.into_index()],
1465 idx: &self.out_index_list,
1466 from: &self.out_texels,
1467 range: 0..self.out_index_list.len(),
1468 });
1469 }
1470 }
1471 }
1472 }
1473
1474 fn blocks(mut x: Range<u32>, mut y: Range<u32>) -> impl FnMut(&mut [[u32; 2]]) -> usize {
1477 #[inline(never)]
1482 move |buffer| {
1483 let maximum = buffer.len();
1484 if x.start == x.end {
1485 return 0;
1486 }
1487
1488 if y.end == 0 {
1489 return 0;
1490 }
1491
1492 let lines_left = x.end - x.start;
1493 let line_len = y.end;
1494
1495 let pix_left = u64::from(lines_left) * u64::from(line_len) - u64::from(y.start);
1496 let actual = pix_left.min(maximum as u64);
1497
1498 for p in buffer[..actual as usize].iter_mut() {
1499 let cx = x.start;
1500 let cy = y.start;
1501 *p = [cx, cy];
1502 y.start += 1;
1503
1504 if y.start >= y.end {
1505 y.start = 0;
1506 x.start += 1;
1507 }
1508 }
1509
1510 return actual as usize;
1511 }
1512 }
1513
1514 fn index_from_in_info(
1515 info: &ConvertInfo,
1516 texel: &[[u32; 2]],
1517 idx: &mut [usize],
1518 chunks: ChunkSpec,
1519 ) {
1520 Self::index_from_layer(&info.layout.in_layout, texel, idx, chunks)
1521 }
1522
1523 fn index_from_out_info(
1524 info: &ConvertInfo,
1525 texel: &[[u32; 2]],
1526 idx: &mut [usize],
1527 chunks: ChunkSpec,
1528 ) {
1529 Self::index_from_layer(&info.layout.out_layout, texel, idx, chunks)
1530 }
1531
1532 fn index_from_layer(
1533 info: &PlaneBytes,
1534 texel: &[[u32; 2]],
1535 idx: &mut [usize],
1536 chunks: ChunkSpec,
1537 ) {
1538 info.fill_texel_indices_impl(idx, texel, chunks)
1540 }
1541}
1542
1543impl<'re, 'data> PlaneIo<'re, 'data> {
1544 pub fn borrow(&mut self) -> PlaneIo<'_, 'data> {
1545 PlaneIo {
1546 sources: Sources {
1547 sync: self.sources.sync,
1548 cell: self.sources.cell,
1549 atomic: self.sources.atomic,
1550 },
1551 targets: Targets {
1552 sync: &mut *self.targets.sync,
1553 cell: self.targets.cell,
1554 atomic: self.targets.atomic,
1555 },
1556 }
1557 }
1558}
1559
1560trait ExpandYuvLike<const IN: usize, const OUT: usize> {
1561 fn expand<T: Copy>(_: [T; IN], fill: T) -> [[T; 4]; OUT];
1562}
1563
1564impl CommonPixel {
1565 fn expand_from_info(
1571 ops: &ConvertOps,
1573 in_texel: &TexelBuffer,
1574 pixel_buf: &mut TexelBuffer,
1575 _: PlaneIo,
1576 ) {
1577 let info = &ops.info;
1578
1579 let TexelBits { bits, parts, block } = info.layout.in_layout.texel;
1583
1584 match block {
1585 Block::Pixel => Self::expand_bits(
1586 info,
1587 [FromBits::for_pixel(bits, parts)],
1588 in_texel,
1589 pixel_buf,
1590 ),
1591 Block::Pack1x2 => {
1592 let bits = FromBits::for_pixels::<2>(bits, parts);
1593 Self::expand_bits(info, bits, in_texel, pixel_buf)
1594 }
1595 Block::Pack1x4 => {
1596 let bits = FromBits::for_pixels::<4>(bits, parts);
1597 Self::expand_bits(info, bits, in_texel, pixel_buf)
1598 }
1599 Block::Pack1x8 => {
1600 let bits = FromBits::for_pixels::<8>(bits, parts);
1601 Self::expand_bits(info, bits, in_texel, pixel_buf)
1602 }
1603 Block::Sub1x2 | Block::Sub1x4 | Block::Sub2x2 | Block::Sub2x4 | Block::Sub4x4 => {
1604 Self::expand_bits(
1606 info,
1607 [FromBits::for_pixel(bits, parts)],
1608 in_texel,
1609 pixel_buf,
1610 );
1611 Self::expand_sub_blocks(pixel_buf, info, info.common_blocks);
1612 }
1613 Block::Yuv422 => {
1614 debug_assert!(matches!(info.layout.in_layout.texel.block, Block::Sub1x2));
1615 debug_assert!(matches!(
1616 info.layout.in_layout.texel.parts.num_components(),
1617 3
1618 ));
1619 Self::expand_yuv422(info, in_texel, pixel_buf);
1620 }
1621 Block::Yuv411 => {
1622 debug_assert!(matches!(info.layout.in_layout.texel.block, Block::Sub1x4));
1623 debug_assert!(matches!(
1624 info.layout.in_layout.texel.parts.num_components(),
1625 3
1626 ));
1627 Self::expand_yuv411(info, in_texel, pixel_buf);
1628 }
1629 other => {
1631 debug_assert!(false, "{:?}", other);
1632 }
1633 }
1634 }
1635
1636 fn expand_bits<const N: usize>(
1637 info: &ConvertInfo,
1638 bits: [[FromBits; 4]; N],
1639 in_texel: &TexelBuffer,
1640 pixel_buf: &mut TexelBuffer,
1641 ) {
1642 const M: usize = SampleBits::MAX_COMPONENTS;
1643 let (encoding, len) = info.layout.in_layout.texel.bits.bit_encoding();
1644
1645 if encoding[..len as usize] == [BitEncoding::UInt; M][..len as usize] {
1646 return Self::expand_ints::<N>(info, bits, in_texel, pixel_buf);
1647 } else if encoding[..len as usize] == [BitEncoding::Float; M][..len as usize] {
1648 return Self::expand_floats(info, bits[0], in_texel, pixel_buf);
1649 } else {
1650 debug_assert!(false, "{:?}", &encoding[..len as usize]);
1652 }
1653 }
1654
1655 fn expand_sub_blocks(pixel_buf: &mut TexelBuffer, info: &ConvertInfo, order: CommonPixelOrder) {
1657 debug_assert!(matches!(order, CommonPixelOrder::PixelsInRowOrder));
1658 let block = info.layout.in_layout.texel.block;
1659 let (bwidth, bheight) = (block.width(), block.height());
1660
1661 let pixels = pixel_buf.as_mut_texels(<[f32; 4]>::texel());
1662 let texlen = pixels.len() / (bwidth as usize) / (bheight as usize);
1663 let block = bwidth as usize * bheight as usize;
1664
1665 for i in (0..texlen).rev() {
1666 let source = pixels[i];
1667 for target in &mut pixels[block * i..][..block] {
1668 *target = source;
1669 }
1670 }
1671
1672 }
1674
1675 fn expand_ints<const N: usize>(
1680 info: &ConvertInfo,
1681 bits: [[FromBits; 4]; N],
1682 in_texel: &TexelBuffer,
1683 pixel_buf: &mut TexelBuffer,
1684 ) {
1685 struct ExpandAction<'data, T, const N: usize> {
1686 expand: Texel<T>,
1687 expand_fn: fn([u32; 4], &[FromBits; 4]) -> T,
1688 bits: [[FromBits; 4]; N],
1689 in_texel: &'data TexelBuffer,
1690 pixel_buf: &'data mut TexelBuffer,
1691 }
1692
1693 impl<Expanded, const N: usize> GenericTexelAction<()> for ExpandAction<'_, Expanded, N> {
1694 fn run<T>(self, texel: Texel<T>) -> () {
1695 let texel_slice = self.in_texel.as_texels(texel);
1696 let pixel_slice = self.pixel_buf.as_mut_texels(self.expand.array::<N>());
1697
1698 for (texbits, expand) in texel_slice.iter().zip(pixel_slice) {
1701 let pixels = self.bits.map(|bits| {
1702 (self.expand_fn)(bits.map(|b| b.extract_as_lsb(texel, texbits)), &bits)
1703 });
1704
1705 *expand = pixels;
1706 }
1707 }
1708 }
1709
1710 match info.common_pixel {
1711 CommonPixel::F32x4 => info.in_kind.action(ExpandAction {
1715 expand: <[f32; 4]>::texel(),
1716 expand_fn: |num, bits| {
1717 [0, 1, 2, 3].map(|idx| {
1718 let max_val = bits[idx].mask() as u64;
1719 num[idx] as f32 / max_val as f32
1720 })
1721 },
1722 bits,
1723 in_texel,
1724 pixel_buf,
1725 }),
1726 }
1727
1728 let expanded = <[f32; 4]>::texel().array::<N>();
1733 for (pixel_idx, bits) in bits.into_iter().enumerate() {
1734 for (idx, component) in (0..4).zip(bits) {
1735 if component.len > 0 {
1736 continue;
1737 }
1738
1739 let default = if idx == 3 { 1.0 } else { 0.0 };
1740 for pix in pixel_buf.as_mut_texels(expanded) {
1741 pix[pixel_idx][idx] = default;
1742 }
1743 }
1744 }
1745 }
1746
1747 fn expand_floats(
1748 info: &ConvertInfo,
1749 bits: [FromBits; 4],
1750 in_texel: &TexelBuffer,
1751 pixel_buf: &mut TexelBuffer,
1752 ) {
1753 debug_assert!(
1754 matches!(info.common_pixel, CommonPixel::F32x4),
1755 "Improper common choices {:?}",
1756 info.common_pixel
1757 );
1758 let destination = pixel_buf.as_mut_texels(<[f32; 4]>::texel());
1759
1760 let pitch = info.in_kind.size() / 4;
1762
1763 for (&ch, ch_idx) in bits.iter().zip(0..4) {
1764 match ch.len {
1765 0 => continue,
1766 32 => {}
1767 _ => continue,
1769 }
1770
1771 let position = ch.begin / 32;
1772 let texels = in_texel.as_texels(<f32>::texel());
1773 let pitched = texels[position..].chunks(pitch);
1774
1775 for (pix, texel) in destination.iter_mut().zip(pitched) {
1776 pix[ch_idx] = texel[0];
1777 }
1778 }
1779 }
1780
1781 fn expand_yuv422(info: &ConvertInfo, in_texel: &TexelBuffer, pixel_buf: &mut TexelBuffer) {
1782 struct ExpandYuv422;
1783
1784 impl ExpandYuvLike<4, 2> for ExpandYuv422 {
1785 fn expand<T: Copy>(yuyv: [T; 4], fill: T) -> [[T; 4]; 2] {
1786 let [u, y1, v, y2] = yuyv;
1787
1788 [[y1, u, v, fill], [y2, u, v, fill]]
1789 }
1790 }
1791
1792 Self::expand_yuv_like::<ExpandYuv422, 4, 2>(
1793 info,
1794 in_texel,
1795 pixel_buf,
1796 <[u8; 4]>::texel(),
1797 <[u16; 4]>::texel(),
1798 <[f32; 4]>::texel(),
1799 )
1800 }
1801
1802 fn expand_yuy2(info: &ConvertInfo, in_texel: &TexelBuffer, pixel_buf: &mut TexelBuffer) {
1803 struct ExpandYuy2;
1804
1805 impl ExpandYuvLike<4, 2> for ExpandYuy2 {
1806 fn expand<T: Copy>(yuyv: [T; 4], fill: T) -> [[T; 4]; 2] {
1807 let [y1, u, y2, v] = yuyv;
1808
1809 [[y1, u, v, fill], [y2, u, v, fill]]
1810 }
1811 }
1812
1813 Self::expand_yuv_like::<ExpandYuy2, 4, 2>(
1814 info,
1815 in_texel,
1816 pixel_buf,
1817 <[u8; 4]>::texel(),
1818 <[u16; 4]>::texel(),
1819 <[f32; 4]>::texel(),
1820 )
1821 }
1822
1823 fn expand_yuv411(info: &ConvertInfo, in_texel: &TexelBuffer, pixel_buf: &mut TexelBuffer) {
1824 struct ExpandYuv411;
1825
1826 impl ExpandYuvLike<6, 4> for ExpandYuv411 {
1827 fn expand<T: Copy>(yuyv: [T; 6], fill: T) -> [[T; 4]; 4] {
1828 let [u, y1, y2, v, y3, y4] = yuyv;
1829
1830 [
1831 [y1, u, v, fill],
1832 [y2, u, v, fill],
1833 [y3, u, v, fill],
1834 [y4, u, v, fill],
1835 ]
1836 }
1837 }
1838
1839 Self::expand_yuv_like::<ExpandYuv411, 6, 4>(
1840 info,
1841 in_texel,
1842 pixel_buf,
1843 <[u8; 6]>::texel(),
1844 <[u16; 6]>::texel(),
1845 <[f32; 6]>::texel(),
1846 )
1847 }
1848
1849 fn expand_yuv_like<F, const N: usize, const M: usize>(
1850 info: &ConvertInfo,
1851 in_texel: &TexelBuffer,
1852 pixel_buf: &mut TexelBuffer,
1853 tex_u8: Texel<[u8; N]>,
1854 tex_u16: Texel<[u16; N]>,
1855 tex_f32: Texel<[f32; N]>,
1856 ) where
1857 F: ExpandYuvLike<N, M>,
1858 {
1859 match info.layout.in_layout.texel.bits {
1862 SampleBits::UInt8x4 => {
1863 let texels = in_texel.as_texels(tex_u8).iter();
1864 match info.common_pixel {
1865 CommonPixel::F32x4 => {
1866 let pixels = pixel_buf
1867 .as_mut_texels(<[f32; 4]>::texel())
1868 .chunks_exact_mut(M);
1869 debug_assert!(pixels.len() == texels.len());
1870
1871 for (texel, pixel_chunk) in texels.zip(pixels) {
1872 let pixels: &mut [_; M] = pixel_chunk.try_into().unwrap();
1873 let expand = F::expand(*texel, u8::MAX);
1874 let remap = |v: u8| (v as f32) / 255.0f32;
1875 *pixels = expand.map(|v| v.map(remap));
1876 }
1877 }
1878 }
1879 }
1880 SampleBits::UInt16x4 => {
1881 let texels = in_texel.as_texels(tex_u16).iter();
1882 match info.common_pixel {
1883 CommonPixel::F32x4 => {
1884 let pixels = pixel_buf
1885 .as_mut_texels(<[f32; 4]>::texel())
1886 .chunks_exact_mut(M);
1887 debug_assert!(pixels.len() == texels.len());
1888
1889 for (texel, pixel_chunk) in texels.zip(pixels) {
1890 let pixels: &mut [_; M] = pixel_chunk.try_into().unwrap();
1891 let expand = F::expand(*texel, u16::MAX);
1892 let remap = |v: u16| (v as f32) / 65535.0f32;
1893 *pixels = expand.map(|v| v.map(remap));
1894 }
1895 }
1896 }
1897 }
1898 SampleBits::Float32x4 => {
1899 let texels = in_texel.as_texels(tex_f32).iter();
1900 match info.common_pixel {
1901 CommonPixel::F32x4 => {
1902 let pixels = pixel_buf
1903 .as_mut_texels(<[f32; 4]>::texel())
1904 .chunks_exact_mut(2);
1905 debug_assert!(pixels.len() == texels.len());
1906
1907 for (texel, pixel_chunk) in texels.zip(pixels) {
1908 let pixels: &mut [_; M] = pixel_chunk.try_into().unwrap();
1909 *pixels = F::expand(*texel, 1.0);
1910 }
1911 }
1912 }
1913 }
1914 other => {
1915 debug_assert!(false, "Bad YUV spec {:?}", other);
1916 }
1917 }
1918 }
1919
1920 fn join_from_info(
1921 ops: &ConvertOps,
1922 pixel_buf: &TexelBuffer,
1923 out_texels: &mut TexelBuffer,
1924 _: PlaneIo,
1926 ) {
1927 let info = &ops.info;
1928
1929 let TexelBits { bits, parts, block } = info.layout.out_layout.texel;
1933
1934 match block {
1935 Block::Pixel => {
1936 let bits = FromBits::for_pixel(bits, parts);
1937 if let SampleBits::UInt8x4 = info.layout.out_layout.texel.bits {
1939 return Self::join_uint8x4(ops, bits, pixel_buf, out_texels);
1940 } else if let SampleBits::UInt16x4 = info.layout.out_layout.texel.bits {
1941 return Self::join_uint16x4(ops, bits, pixel_buf, out_texels);
1942 } else if let SampleBits::UInt8x3 = info.layout.out_layout.texel.bits {
1943 return Self::join_uint8x3(ops, bits, pixel_buf, out_texels);
1944 } else if let SampleBits::UInt16x3 = info.layout.out_layout.texel.bits {
1945 return Self::join_uint16x3(ops, bits, pixel_buf, out_texels);
1946 } else {
1947 Self::join_bits(info, ops, [bits], pixel_buf, out_texels)
1948 }
1949 }
1950 Block::Pack1x2 => {
1951 let bits = FromBits::for_pixels::<2>(bits, parts);
1952 Self::join_bits(info, ops, bits, pixel_buf, out_texels)
1953 }
1954 Block::Pack1x4 => {
1955 let bits = FromBits::for_pixels::<4>(bits, parts);
1956 Self::join_bits(info, ops, bits, pixel_buf, out_texels)
1957 }
1958 Block::Pack1x8 => {
1959 let bits = FromBits::for_pixels::<8>(bits, parts);
1960 Self::join_bits(info, ops, bits, pixel_buf, out_texels)
1961 }
1962 Block::Sub1x2 | Block::Sub1x4 | Block::Sub2x2 | Block::Sub2x4 | Block::Sub4x4 => {
1963 Self::join_bits(
1965 info,
1966 ops,
1967 [FromBits::for_pixel(bits, parts)],
1968 pixel_buf,
1969 out_texels,
1970 );
1971 Self::join_sub_blocks(out_texels, info, info.common_blocks);
1972 }
1973 Block::Yuv422 => {
1974 debug_assert!(matches!(info.layout.out_layout.texel.block, Block::Sub1x2));
1976 debug_assert!(matches!(
1977 info.layout.out_layout.texel.parts.num_components(),
1978 3
1979 ));
1980 Self::join_yuv422(info, pixel_buf, out_texels)
1981 }
1982 other => {
1983 debug_assert!(false, "{:?}", other);
1984 }
1985 }
1986 }
1987
1988 fn join_bits<const N: usize>(
1991 info: &ConvertInfo,
1992 _: &ConvertOps,
1993 bits: [[FromBits; 4]; N],
1994 pixel_buf: &TexelBuffer,
1995 out_texels: &mut TexelBuffer,
1996 ) {
1997 const M: usize = SampleBits::MAX_COMPONENTS;
1998 let (encoding, len) = info.layout.out_layout.texel.bits.bit_encoding();
1999
2000 if encoding[..len as usize] == [BitEncoding::UInt; M][..len as usize] {
2001 return Self::join_ints(info, bits, pixel_buf, out_texels);
2002 } else if encoding[..len as usize] == [BitEncoding::Float; M][..len as usize] {
2003 return Self::join_floats(info, bits[0], pixel_buf, out_texels);
2004 } else {
2005 debug_assert!(false, "{:?}", &encoding[..len as usize]);
2007 }
2008 }
2009
2010 fn join_ints<const N: usize>(
2012 info: &ConvertInfo,
2013 bits: [[FromBits; 4]; N],
2014 pixel_buf: &TexelBuffer,
2015 out_texels: &mut TexelBuffer,
2016 ) {
2017 struct JoinAction<'data, T, F: FnMut(&T, &FromBits, u8) -> u32, const N: usize> {
2018 join: Texel<T>,
2019 join_fn: F,
2020 bits: [[FromBits; 4]; N],
2021 out_texels: &'data mut TexelBuffer,
2022 pixel_buf: &'data TexelBuffer,
2023 }
2024
2025 impl<Expanded, F, const N: usize> GenericTexelAction<()> for JoinAction<'_, Expanded, F, N>
2026 where
2027 F: FnMut(&Expanded, &FromBits, u8) -> u32,
2028 {
2029 fn run<T>(mut self, texel: Texel<T>) -> () {
2030 let texel_slice = self.out_texels.as_mut_texels(texel);
2031 let pixel_slice = self.pixel_buf.as_texels(self.join.array::<N>());
2032
2033 debug_assert_eq!(texel_slice.len(), pixel_slice.len());
2034
2035 for ch in [0u8, 1, 2, 3] {
2036 for (texbits, pixels) in texel_slice.iter_mut().zip(pixel_slice) {
2037 for (pixel_bits, joined) in self.bits.iter().zip(pixels) {
2038 let bits = pixel_bits[ch as usize];
2039 let value = (self.join_fn)(joined, &bits, ch);
2041 bits.insert_as_lsb(texel, texbits, value);
2042 }
2043 }
2044 }
2045 }
2046 }
2047
2048 match info.common_pixel {
2049 CommonPixel::F32x4 => info.out_kind.action(JoinAction {
2053 join: <[f32; 4]>::texel(),
2054 join_fn: |num, bits, idx| {
2056 let max_val = bits.mask();
2057 let round = |x| (x + 0.5) as u32;
2059 let raw = round(num[(idx & 0x3) as usize] * max_val as f32);
2060 raw.min(max_val)
2061 },
2062 bits,
2063 out_texels,
2064 pixel_buf,
2065 }),
2066 }
2067 }
2068
2069 fn join_sub_blocks(pixel_buf: &mut TexelBuffer, info: &ConvertInfo, order: CommonPixelOrder) {
2071 debug_assert!(matches!(order, CommonPixelOrder::PixelsInRowOrder));
2072 let block = info.layout.out_layout.texel.block;
2073 let (bwidth, bheight) = (block.width(), block.height());
2074
2075 let pixels = pixel_buf.as_mut_texels(<[f32; 4]>::texel());
2076 let texlen = pixels.len() / (bwidth as usize) / (bheight as usize);
2077 let block = bwidth as usize * bheight as usize;
2078
2079 for i in (0..texlen).rev() {
2082 let mut sum = [0.0, 0.0, 0.0, 0.0];
2083 for &[a, b, c, d] in &pixels[block * i..][..block] {
2085 sum[0] += a;
2086 sum[1] += b;
2087 sum[2] += c;
2088 sum[3] += d;
2089 }
2090 pixels[i] = sum.map(|i| i / block as f32);
2091 }
2092 }
2093
2094 fn join_uint8x4(
2096 ops: &ConvertOps,
2097 bits: [FromBits; 4],
2098 pixel_buf: &TexelBuffer,
2099 out_texels: &mut TexelBuffer,
2100 ) {
2101 let src = pixel_buf.as_texels(f32::texel());
2102 let dst = out_texels.as_mut_texels(u8::texel());
2103
2104 for (tex, &pix) in dst.iter_mut().zip(src) {
2107 *tex = ((pix * (u8::MAX as f32)) + 0.5) as u8;
2108 }
2109
2110 let mut shuffle = [0x80u8; 4];
2114 for (idx, bits) in (0u8..4).zip(&bits) {
2115 if bits.len > 0 {
2116 shuffle[(bits.begin / 8) as usize] = idx;
2117 }
2118 }
2119
2120 (ops.shuffle.shuffle_u8x4)(out_texels.as_mut_texels(<[u8; 4]>::texel()), shuffle);
2121 }
2122
2123 fn join_uint16x4(
2124 ops: &ConvertOps,
2125 bits: [FromBits; 4],
2126 pixel_buf: &TexelBuffer,
2127 out_texels: &mut TexelBuffer,
2128 ) {
2129 let src = pixel_buf.as_texels(f32::texel());
2130 let dst = out_texels.as_mut_texels(u16::texel());
2131
2132 for (tex, &pix) in dst.iter_mut().zip(src) {
2135 *tex = ((pix * (u16::MAX as f32)) + 0.5) as u16;
2136 }
2137
2138 let mut shuffle = [0x80u8; 4];
2142 for (idx, bits) in (0u8..4).zip(&bits) {
2143 if bits.len > 0 {
2144 shuffle[(bits.begin / 16) as usize] = idx;
2145 }
2146 }
2147
2148 (ops.shuffle.shuffle_u16x4)(out_texels.as_mut_texels(<[u16; 4]>::texel()), shuffle);
2149 }
2150
2151 fn join_uint8x3(
2152 _: &ConvertOps,
2153 bits: [FromBits; 4],
2154 pixel_buf: &TexelBuffer,
2155 out_texels: &mut TexelBuffer,
2156 ) {
2157 let src = pixel_buf.as_texels(<[f32; 4]>::texel());
2158 let dst = out_texels.as_mut_texels(<[u8; 3]>::texel());
2159
2160 let mut shuffle = [0x80u8; 3];
2164 for (idx, bits) in (0u8..4).zip(&bits) {
2165 if bits.len > 0 {
2166 shuffle[(bits.begin / 8) as usize] = idx;
2167 }
2168 }
2169
2170 for (tex, pix) in dst.iter_mut().zip(src) {
2171 *tex = shuffle.map(|i| {
2172 let val = *pix.get(i as usize).unwrap_or(&0.0);
2173 (val * u8::MAX as f32 + 0.5) as u8
2174 });
2175 }
2176 }
2177
2178 fn join_uint16x3(
2179 _: &ConvertOps,
2180 bits: [FromBits; 4],
2181 pixel_buf: &TexelBuffer,
2182 out_texels: &mut TexelBuffer,
2183 ) {
2184 let src = pixel_buf.as_texels(<[f32; 4]>::texel());
2185 let dst = out_texels.as_mut_texels(<[u16; 3]>::texel());
2186
2187 let mut shuffle = [0x80u8; 3];
2191 for (idx, bits) in (0u8..4).zip(&bits) {
2192 if bits.len > 0 {
2193 shuffle[(bits.begin / 16) as usize] = idx;
2194 }
2195 }
2196
2197 for (tex, pix) in dst.iter_mut().zip(src) {
2198 *tex = shuffle.map(|i| {
2199 let val = *pix.get(i as usize).unwrap_or(&0.0);
2200 (val * u16::MAX as f32 + 0.5) as u16
2201 });
2202 }
2203 }
2204
2205 fn join_floats(
2206 info: &ConvertInfo,
2207 bits: [FromBits; 4],
2208 pixel_buf: &TexelBuffer,
2209 out_texels: &mut TexelBuffer,
2210 ) {
2211 debug_assert!(
2212 matches!(info.common_pixel, CommonPixel::F32x4),
2213 "Improper common choices {:?}",
2214 info.common_pixel
2215 );
2216 let source = pixel_buf.as_texels(<[f32; 4]>::texel());
2217 let pitch = info.out_kind.size() / 4;
2219
2220 for (&ch, ch_idx) in bits.iter().zip(0..4) {
2221 match ch.len {
2222 0 => continue,
2223 32 => {}
2224 _ => continue,
2226 }
2227
2228 let position = ch.begin / 32;
2229 let texels = out_texels.as_mut_texels(<f32>::texel());
2230 let pitched = texels[position..].chunks_mut(pitch);
2231
2232 for (pix, texel) in source.iter().zip(pitched) {
2233 texel[0] = pix[ch_idx];
2234 }
2235 }
2236 }
2237
2238 fn join_yuv422(_: &ConvertInfo, _: &TexelBuffer, _: &mut TexelBuffer) {
2239 debug_assert!(false);
2241 }
2242
2243 fn action<R>(self, action: impl GenericTexelAction<R>) -> R {
2244 match self {
2245 CommonPixel::F32x4 => action.run(<[f32; 4]>::texel()),
2246 }
2247 }
2248}
2249
2250impl CommonColor {
2251 fn cie_xyz_from_info(info: &ConvertInfo, pixel: &TexelBuffer, xyz: &mut TexelBuffer) {
2252 if !matches!(info.common_pixel, CommonPixel::F32x4) {
2255 return;
2257 }
2258
2259 let texel = <[f32; 4]>::texel();
2260 let pixel = pixel.as_texels(texel);
2261 let xyz = xyz.as_mut_texels(texel);
2262 assert_eq!(
2263 pixel.len(),
2264 xyz.len(),
2265 "Setup create invalid conversion buffer"
2266 );
2267
2268 info.layout.in_color.to_xyz_slice(pixel, xyz)
2269 }
2270
2271 fn cie_xyz_into_info(info: &ConvertInfo, xyz: &TexelBuffer, pixel: &mut TexelBuffer) {
2272 if !matches!(info.common_pixel, CommonPixel::F32x4) {
2275 return;
2277 }
2278
2279 let texel = <[f32; 4]>::texel();
2280 let xyz = xyz.as_texels(texel);
2281 let pixel = pixel.as_mut_texels(texel);
2282 assert_eq!(
2283 pixel.len(),
2284 xyz.len(),
2285 "Setup create invalid conversion buffer"
2286 );
2287
2288 info.layout.out_color.from_xyz_slice(xyz, pixel)
2289 }
2290}
2291
2292impl ColorLayout {
2293 fn from_frames(
2294 canvas_in: &CanvasLayout,
2295 canvas_out: &CanvasLayout,
2296 ) -> Result<Self, ConversionError> {
2297 let mut in_color = canvas_in.color.clone();
2298 let mut out_color = canvas_out.color.clone();
2299
2300 if in_color.is_none() && out_color.is_none() {
2301 in_color = Some(Color::Scalars {
2304 transfer: crate::color::Transfer::Linear,
2305 });
2306
2307 out_color = Some(Color::Scalars {
2308 transfer: crate::color::Transfer::Linear,
2309 });
2310 }
2311
2312 Ok(ColorLayout {
2313 in_layout: canvas_in
2314 .as_plane()
2315 .ok_or(ConversionError::UnsupportedInputLayout)?,
2316 out_layout: canvas_out
2317 .as_plane()
2318 .ok_or(ConversionError::UnsupportedInputLayout)?,
2319 in_color: in_color.ok_or(ConversionError::UnsupportedInputColor)?,
2320 out_color: out_color.ok_or(ConversionError::UnsupportedOutputColor)?,
2321 })
2322 }
2323}
2324
2325impl TexelKind {
2326 pub(crate) fn action<R>(self, action: impl GenericTexelAction<R>) -> R {
2327 match self {
2328 TexelKind::U8 => action.run(u8::texel()),
2329 TexelKind::U8x2 => action.run(<[u8; 2]>::texel()),
2330 TexelKind::U8x3 => action.run(<[u8; 3]>::texel()),
2331 TexelKind::U8x4 => action.run(<[u8; 4]>::texel()),
2332 TexelKind::U8x6 => action.run(<[u8; 6]>::texel()),
2333 TexelKind::U16 => action.run(<[u16; 1]>::texel()),
2334 TexelKind::U16x2 => action.run(<[u16; 2]>::texel()),
2335 TexelKind::U16x3 => action.run(<[u16; 3]>::texel()),
2336 TexelKind::U16x4 => action.run(<[u16; 4]>::texel()),
2337 TexelKind::U16x6 => action.run(<[u16; 6]>::texel()),
2338 TexelKind::F32 => action.run(<[f32; 1]>::texel()),
2339 TexelKind::F32x2 => action.run(<[f32; 2]>::texel()),
2340 TexelKind::F32x3 => action.run(<[f32; 3]>::texel()),
2341 TexelKind::F32x4 => action.run(<[f32; 4]>::texel()),
2342 TexelKind::F32x6 => action.run(<[f32; 6]>::texel()),
2343 }
2344 }
2345
2346 fn size(self) -> usize {
2347 struct ToSize;
2348
2349 impl GenericTexelAction<usize> for ToSize {
2350 fn run<T>(self, texel: image_texel::Texel<T>) -> usize {
2351 image_texel::layout::TexelLayout::from(texel).size()
2352 }
2353 }
2354
2355 TexelKind::from(self).action(ToSize)
2356 }
2357}
2358
2359impl PlaneIdx {
2360 fn into_index(self) -> usize {
2361 match self {
2362 PlaneIdx::Sync(i) => i.into(),
2363 PlaneIdx::Cell(i) => i.into(),
2364 PlaneIdx::Atomic(i) => i.into(),
2365 }
2366 }
2367}
2368
2369impl From<TexelBits> for TexelKind {
2370 fn from(texel: TexelBits) -> Self {
2371 Self::from(texel.bits)
2372 }
2373}
2374
2375impl From<SampleBits> for TexelKind {
2376 fn from(bits: SampleBits) -> Self {
2377 use SampleBits::*;
2378 match bits {
2380 Int8 | UInt8 | UInt1x8 | UInt2x4 | UInt332 | UInt233 | UInt4x2 => TexelKind::U8,
2381 Int16 | UInt16 | UInt4x4 | UInt_444 | UInt444_ | UInt565 => TexelKind::U16,
2382 Int8x2 | UInt8x2 => TexelKind::U8x2,
2383 Int8x3 | UInt8x3 | UInt4x6 => TexelKind::U8x3,
2384 Int8x4 | UInt8x4 => TexelKind::U8x4,
2385 UInt8x6 => TexelKind::U8x6,
2386 Int16x2 | UInt16x2 => TexelKind::U16x2,
2387 Int16x3 | UInt16x3 => TexelKind::U16x3,
2388 Int16x4 | UInt16x4 => TexelKind::U16x4,
2389 UInt16x6 => TexelKind::U16x6,
2390 UInt1010102 | UInt2101010 | UInt101010_ | UInt_101010 => TexelKind::U16x2,
2391 Float16x4 => TexelKind::U16x4,
2392 Float32 => TexelKind::F32,
2393 Float32x2 => TexelKind::F32x2,
2394 Float32x3 => TexelKind::F32x3,
2395 Float32x4 => TexelKind::F32x4,
2396 Float32x6 => TexelKind::F32x6,
2397 }
2398 }
2399}
2400
2401impl fmt::Display for ConversionError {
2402 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2403 match self {
2404 ConversionError::InputLayoutDoesNotMatchPlan => {
2405 write!(
2406 f,
2407 "the planned input layout does not match the actual input"
2408 )
2409 }
2410 ConversionError::OutputLayoutDoesNotMatchPlan => {
2411 write!(
2412 f,
2413 "the planned output layout does not match the actual output"
2414 )
2415 }
2416 ConversionError::InputColorDoesNotMatchPlanes => {
2417 write!(f, "the planned input color does not match the actual color")
2418 }
2419 ConversionError::OutputColorDoesNotMatchPlanes => {
2420 write!(
2421 f,
2422 "the planned output color does not match the actual color"
2423 )
2424 }
2425 ConversionError::UnsupportedInputLayout => {
2426 write!(f, "conversion from the input layout is not supported")
2427 }
2428 ConversionError::UnsupportedInputColor => {
2429 write!(f, "conversion from the input color is not supported")
2430 }
2431 ConversionError::UnsupportedOutputLayout => {
2432 write!(f, "conversion into the output layout is not supported")
2433 }
2434 ConversionError::UnsupportedOutputColor => {
2435 write!(f, "conversion into the output color is not supported")
2436 }
2437 }
2438 }
2439}
2440
2441impl core::error::Error for ConversionError {}
2442
2443#[test]
2444fn from_bits() {
2445 let bits = FromBits::for_pixel(SampleBits::UInt332, SampleParts::Rgb);
2446 let (texel, value) = (u8::texel(), &0b01010110);
2447 assert_eq!(bits[0].extract_as_lsb(texel, value), 0b010);
2448 assert_eq!(bits[1].extract_as_lsb(texel, value), 0b101);
2449 assert_eq!(bits[2].extract_as_lsb(texel, value), 0b10);
2450 assert_eq!(bits[3].extract_as_lsb(texel, value), 0b0);
2451}
2452
2453#[test]
2454fn to_bits() {
2455 let bits = FromBits::for_pixel(SampleBits::UInt332, SampleParts::Rgb);
2456 let (texel, ref mut value) = (u8::texel(), 0);
2457 bits[0].insert_as_lsb(texel, value, 0b010);
2458 bits[1].insert_as_lsb(texel, value, 0b101);
2459 bits[2].insert_as_lsb(texel, value, 0b10);
2460 bits[3].insert_as_lsb(texel, value, 0b0);
2461 assert_eq!(*value, 0b01010110);
2462}