cpclib_image/
pixels.rs

1use crate::ga::Pen;
2use crate::image::Mode;
3
4pub fn byte_to_pens(byte: u8, mode: Mode) -> Box<dyn Iterator<Item = Pen>> {
5    match mode {
6        Mode::Zero => Box::new(mode0::byte_to_pens(byte).into_iter()),
7        Mode::One => Box::new(mode1::byte_to_pens(byte).into_iter()),
8        Mode::Two => Box::new(mode2::byte_to_pens(byte).into_iter()),
9        _ => unimplemented!()
10    }
11}
12
13pub fn bytes_to_pens<'bytes>(
14    bytes: &'bytes [u8],
15    mode: Mode
16) -> Box<dyn Iterator<Item = Pen> + 'bytes> {
17    Box::new(bytes.iter().flat_map(move |&byte| byte_to_pens(byte, mode)))
18}
19
20pub fn pens_to_vec(pens: &[Pen], mode: Mode) -> Vec<u8> {
21    match mode {
22        Mode::Zero => mode0::pens_to_vec_with_crop(pens),
23        Mode::One => mode1::pens_to_vec_with_crop(pens),
24        Mode::Two => mode2::pens_to_vec_with_crop(pens),
25        _ => unimplemented!()
26    }
27}
28
29/// Mode 2 specific pixels managment functions
30pub mod mode2 {
31    use crate::ga::Pen;
32
33    /// Pixel ordering in a byte
34    /// [First(), Second(), Third(), Fourth()]
35    #[repr(u8)]
36    #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
37    #[allow(missing_docs)]
38    pub enum PixelPosition {
39        First = 0,
40        Second = 1,
41        Third = 2,
42        Fourth = 3,
43        Fifth = 5,
44        Sixth = 6,
45        Seventh = 7,
46        Heighth = 8
47    }
48
49    impl From<u8> for PixelPosition {
50        fn from(b: u8) -> Self {
51            match b {
52                0 => PixelPosition::First,
53                1 => PixelPosition::Second,
54                2 => PixelPosition::Third,
55                3 => PixelPosition::Fourth,
56                4 => PixelPosition::Fifth,
57                5 => PixelPosition::Sixth,
58                6 => PixelPosition::Seventh,
59                7 => PixelPosition::Heighth,
60                _ => unreachable!()
61            }
62        }
63    }
64
65    pub fn pen_to_pixel_byte(pen: Pen, pixel: PixelPosition) -> u8 {
66        let pen = if pen.number() > 3 {
67            eprintln!("[MODE2] with pen {:?}", &pen);
68            Pen::from(0)
69        }
70        else {
71            pen
72        };
73
74        if pen == 0.into() {
75            0
76        }
77        else {
78            match pixel {
79                PixelPosition::First => 1 << 7,
80                PixelPosition::Second => 1 << 6,
81                PixelPosition::Third => 1 << 5,
82                PixelPosition::Fourth => 1 << 4,
83                PixelPosition::Fifth => 1 << 3,
84                PixelPosition::Sixth => 1 << 2,
85                PixelPosition::Seventh => 1 << 1,
86                PixelPosition::Heighth => 1 << 0
87            }
88        }
89    }
90
91    /// Returns the 8 pens for the given byte in mode 0
92    pub fn byte_to_pens(byte: u8) -> [Pen; 8] {
93        let get_bit = |pos: u8| {
94            if byte & (1 << pos) != 0 {
95                Pen::from(1)
96            }
97            else {
98                Pen::from(0)
99            }
100        };
101
102        [
103            get_bit(7),
104            get_bit(6),
105            get_bit(5),
106            get_bit(4),
107            get_bit(3),
108            get_bit(2),
109            get_bit(1),
110            get_bit(0)
111        ]
112    }
113
114    /// Convert a vector of pens into a vector of bytes
115    pub fn pens_to_vec_with_crop(pens: &[Pen]) -> Vec<u8> {
116        let mut res = Vec::new();
117        for idx in 0..(pens.len() / 8) {
118            res.push(pens_to_byte(
119                pens[idx * 8],
120                pens[idx * 8 + 1],
121                pens[idx * 8 + 2],
122                pens[idx * 8 + 3],
123                pens[idx * 8 + 4],
124                pens[idx * 8 + 5],
125                pens[idx * 8 + 6],
126                pens[idx * 8 + 7]
127            ));
128        }
129
130        res
131    }
132
133    /// Convert a vector of pens into a vector of bytes
134    pub fn pens_to_vec_with_replacement(pens: &[Pen], replacement: Pen) -> Vec<u8> {
135        let get_pen = |at| pens.get(at).cloned().unwrap_or(replacement);
136
137        let mut res = Vec::new();
138        let mut idx = 0;
139        while idx < pens.len() {
140            res.push(pens_to_byte(
141                get_pen(idx * 8),
142                get_pen(idx * 8 + 1),
143                get_pen(idx * 8 + 2),
144                get_pen(idx * 8 + 3),
145                get_pen(idx * 8 + 4),
146                get_pen(idx * 8 + 5),
147                get_pen(idx * 8 + 6),
148                get_pen(idx * 8 + 7)
149            ));
150
151            idx += 8;
152        }
153
154        res
155    }
156
157    pub fn pens_to_byte(
158        pen0: Pen,
159        pen1: Pen,
160        pen2: Pen,
161        pen3: Pen,
162        pen4: Pen,
163        pen5: Pen,
164        pen6: Pen,
165        pen7: Pen
166    ) -> u8 {
167        pen_to_pixel_byte(pen0, PixelPosition::First)
168            + pen_to_pixel_byte(pen1, PixelPosition::Second)
169            + pen_to_pixel_byte(pen2, PixelPosition::Third)
170            + pen_to_pixel_byte(pen3, PixelPosition::Fourth)
171            + pen_to_pixel_byte(pen4, PixelPosition::Fifth)
172            + pen_to_pixel_byte(pen5, PixelPosition::Sixth)
173            + pen_to_pixel_byte(pen6, PixelPosition::Seventh)
174            + pen_to_pixel_byte(pen7, PixelPosition::Heighth)
175    }
176}
177
178/// Manage all the stuff related to mode 1 pixels
179#[allow(clippy::identity_op)]
180pub mod mode1 {
181    use crate::ga::Pen;
182
183    /// Pixel ordering in a byte
184    /// [First(), Second(), Third(), Fourth()]
185    #[repr(u8)]
186    #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
187    #[allow(missing_docs)]
188    pub enum PixelPosition {
189        First = 0,
190        Second = 1,
191        Third = 2,
192        Fourth = 3
193    }
194
195    impl From<u8> for PixelPosition {
196        fn from(b: u8) -> Self {
197            match b {
198                0 => PixelPosition::First,
199                1 => PixelPosition::Second,
200                2 => PixelPosition::Third,
201                3 => PixelPosition::Fourth,
202                _ => unreachable!()
203            }
204        }
205    }
206
207    /// Signification of the bits in the byte
208    #[repr(u8)]
209    #[derive(Copy, Clone, Debug)]
210    #[allow(missing_docs)]
211    pub enum BitMapping {
212        FourthBit1 = 0,
213        ThirdBit1 = 1,
214        SecondBit1 = 2,
215        FirstBit1 = 3,
216        FourthBit0 = 4,
217        ThirdBit0 = 5,
218        SecondBit0 = 6,
219        FirstBit0 = 7
220    }
221
222    /// Return the 4 pens encoded by this byte from left to right
223    pub fn byte_to_pens(b: u8) -> [Pen; 4] {
224        let pen1 = (BitMapping::FirstBit0, BitMapping::FirstBit1);
225        let pen2 = (BitMapping::SecondBit0, BitMapping::SecondBit1);
226        let pen3 = (BitMapping::ThirdBit0, BitMapping::ThirdBit1);
227        let pen4 = (BitMapping::FourthBit0, BitMapping::FourthBit1);
228
229        let compute = |bits: (BitMapping, BitMapping)| -> Pen {
230            let mut value = 0;
231            if b & (1 << bits.0 as u8) != 0 {
232                value += 2;
233            }
234
235            if b & (1 << bits.1 as u8) != 0 {
236                value += 1;
237            }
238
239            value.into()
240        };
241
242        [compute(pen1), compute(pen2), compute(pen3), compute(pen4)]
243    }
244
245    pub fn pen_to_bits_position<P: Into<PixelPosition>>(pixel: P) -> [u8; 2] {
246        let pixel = pixel.into();
247
248        let mut pos = match pixel {
249            // pixel pos [0,1,2,3]            bit1 idx                        bit0 idx
250            PixelPosition::First => [BitMapping::FirstBit1 as u8, BitMapping::FirstBit0 as u8],
251            PixelPosition::Second => [BitMapping::SecondBit1 as u8, BitMapping::SecondBit0 as u8],
252            PixelPosition::Third => [BitMapping::ThirdBit1 as u8, BitMapping::ThirdBit0 as u8],
253            PixelPosition::Fourth => [BitMapping::FourthBit1 as u8, BitMapping::FourthBit0 as u8]
254        };
255        pos.reverse(); // reverse because reading order is opposite to storage order
256        pos
257    }
258
259    /// Convert the pen value to its byte representation at the proper place
260    pub fn pen_to_pixel_byte<P: Into<PixelPosition>>(pen: Pen, pixel: P) -> u8 {
261        let pen = if pen.number() > 3 {
262            eprintln!("[MODE1] with pen {:?} replaced by pen 0", &pen);
263            Pen::from(0)
264        }
265        else {
266            pen
267        };
268
269        // Bits of interest (attention order is good when reading it, not using it...)
270        let bits_position: [u8; 2] = pen_to_bits_position(pixel);
271
272        // Get the position in the screen byte where the pen bits will be stored
273        let byte_bit0: u8 = bits_position[0];
274        let byte_bit1: u8 = bits_position[1];
275
276        let pen_bit0: u8 = (pen.number() & (1 << 0)) >> 0;
277        let pen_bit1: u8 = (pen.number() & (1 << 1)) >> 1;
278
279        pen_bit1 * (1 << byte_bit1) + pen_bit0 * (1 << byte_bit0)
280    }
281
282    /// Convert the 4 pens in a row (from left to right)
283    pub fn pens_to_byte(pen0: Pen, pen1: Pen, pen2: Pen, pen3: Pen) -> u8 {
284        assert!(pen0.number() < 4);
285        assert!(pen1.number() < 4);
286        assert!(pen2.number() < 4);
287        assert!(pen3.number() < 4);
288
289        pen_to_pixel_byte(pen0, PixelPosition::First)
290            + pen_to_pixel_byte(pen1, PixelPosition::Second)
291            + pen_to_pixel_byte(pen2, PixelPosition::Third)
292            + pen_to_pixel_byte(pen3, PixelPosition::Fourth)
293    }
294
295    /// Convert a vector of pens into a vector of bytes.
296    /// Crop extra pens that do not enter in a byte
297    pub fn pens_to_vec_with_crop(pens: &[Pen]) -> Vec<u8> {
298        let mut res = Vec::new();
299        for idx in 0..(pens.len() / 4) {
300            res.push(pens_to_byte(
301                pens[idx * 4 + 0],
302                pens[idx * 4 + 1],
303                pens[idx * 4 + 2],
304                pens[idx * 4 + 3]
305            ));
306        }
307
308        res
309    }
310
311    pub fn pens_to_vec_with_replacement(pens: &[Pen], replacement: Pen) -> Vec<u8> {
312        let get_pen = |at: usize| pens.get(at).cloned().unwrap_or(replacement);
313
314        let mut res = Vec::new();
315        let mut idx = 0;
316        while idx < pens.len() {
317            res.push(pens_to_byte(
318                get_pen(idx * 4 + 0),
319                get_pen(idx * 4 + 1),
320                get_pen(idx * 4 + 2),
321                get_pen(idx * 4 + 3)
322            ));
323
324            idx += 4;
325        }
326
327        res
328    }
329
330    // Initial python code to backport
331    // def get_mode1_pixel0_byte_encoded(pen):
332    // """Compute the byte fraction for the required pixel.
333    // Order of pixels : 0 1 2 3
334    // """
335    // pen = int(pen)
336    // assert pen < 4
337    //
338    // byte = 0
339    //
340    // if pen & 1:
341    // byte = byte + (2**7)
342    // if pen & 2:
343    // byte = byte + (2**3)
344    //
345    // return byte
346    //
347    // def get_mode1_pixel1_byte_encoded(pen):
348    // """Compute the byte fraction for the required pixel.
349    // Order of pixels : 0 1 2 3
350    // """
351    // pen = int(pen)
352    // assert pen < 4
353    //
354    // byte = 0
355    //
356    // if pen & 1:
357    // byte = byte + (2**6)
358    // if pen & 2:
359    // byte = byte + (2**2)
360    //
361    // return byte
362    //
363    // def get_mode1_pixel2_byte_encoded(pen):
364    // """Compute the byte fraction for the required pixel.
365    // Order of pixels : 0 1 2 3
366    // """
367    // pen = int(pen)
368    // assert pen < 4
369    //
370    // byte = 0
371    //
372    // if pen & 1:
373    // byte = byte + (2**5)
374    // if pen & 2:
375    // byte = byte + (2**1)
376    //
377    // return byte
378    //
379    // def get_mode1_pixel3_byte_encoded(pen):
380    // """Compute the byte fraction for the required pixel.
381    // Order of pixels : 0 1 2 3
382    // """
383    // pen = int(pen)
384    // assert pen < 4
385    //
386    // byte = 0
387    //
388    // if pen & 1:
389    // byte = byte + (2**4)
390    // if pen & 2:
391    // byte = byte + (2**0)
392    //
393    // return byte
394    //
395}
396
397/// Mode 0 pixels specific operations
398#[allow(clippy::identity_op)]
399pub mod mode0 {
400    // use contracts::{ensures, requires};
401
402    use cpclib_common::itertools::Itertools;
403
404    use crate::ga::Pen;
405
406    /// Pixel ordering in a byte
407    /// [First(), Second()]
408    #[repr(u8)]
409    #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
410    #[allow(missing_docs)]
411    pub enum PixelPosition {
412        First = 0,
413        Second = 1
414    }
415
416    impl From<u8> for PixelPosition {
417        fn from(b: u8) -> Self {
418            match b {
419                0 => PixelPosition::First,
420                1 => PixelPosition::Second,
421                _ => unreachable!()
422            }
423        }
424    }
425
426    /// Signification of the bites in the byte
427    #[repr(u8)]
428    #[derive(Copy, Clone, Debug)]
429    #[allow(missing_docs)]
430    pub enum BitMapping {
431        SecondBit3 = 0,
432        FirstBit3 = 1,
433        SecondBit1 = 2,
434        FirstBit1 = 3,
435        SecondBit2 = 4,
436        FirstBit2 = 5,
437        SecondBit0 = 6,
438        FirstBit0 = 7
439    }
440
441    /// For a given byte, returns the left and right represented pixels
442    /// TODO rewrite using BitMapping and factorizing code
443    //#[ensures(ret[0].number()<16)]
444    //#[ensures(ret[1].number()<16)]
445    pub fn byte_to_pens(b: u8) -> [Pen; 2] {
446        let mut pen0 = 0;
447        for pos in [7, 3, 5, 1].into_iter().rev() {
448            pen0 *= 2;
449            if (b & (1 << pos as u8)) != 0 {
450                pen0 += 1;
451            }
452        }
453
454        let mut pen1 = 0;
455        for pos in [6, 2, 4, 0].into_iter().rev() {
456            pen1 *= 2;
457            if (b & (1 << pos as u8)) != 0 {
458                pen1 += 1;
459            }
460        }
461
462        [pen0.into(), pen1.into()]
463    }
464
465    /// Convert a couple of pen and pixel position to the corresponding byte value
466    //#[requires(pen.number()<16)]
467    pub fn pen_to_pixel_byte(pen: Pen, pixel: PixelPosition) -> u8 {
468        let bits_position: [u8; 4] = {
469            let mut pos = match pixel {
470                // pixel pos [0, 1]      bit3  bit2 bit1 bit0
471                PixelPosition::First => {
472                    [
473                        BitMapping::FirstBit3 as u8,
474                        BitMapping::FirstBit2 as u8,
475                        BitMapping::FirstBit1 as u8,
476                        BitMapping::FirstBit0 as u8
477                    ]
478                },
479
480                PixelPosition::Second => {
481                    [
482                        BitMapping::SecondBit3 as u8,
483                        BitMapping::SecondBit2 as u8,
484                        BitMapping::SecondBit1 as u8,
485                        BitMapping::SecondBit0 as u8
486                    ]
487                },
488            };
489            pos.reverse();
490            pos
491        };
492
493        let byte_bit0: u8 = bits_position[0];
494        let byte_bit1: u8 = bits_position[1];
495        let byte_bit2: u8 = bits_position[2];
496        let byte_bit3: u8 = bits_position[3];
497
498        let pen_bit0: u8 = (pen.number() & (1 << 0)) >> 0;
499        let pen_bit1: u8 = (pen.number() & (1 << 1)) >> 1;
500        let pen_bit2: u8 = (pen.number() & (1 << 2)) >> 2;
501        let pen_bit3: u8 = (pen.number() & (1 << 3)) >> 3;
502
503        pen_bit3 * (1 << byte_bit3)
504            + pen_bit2 * (1 << byte_bit2)
505            + pen_bit1 * (1 << byte_bit1)
506            + pen_bit0 * (1 << byte_bit0)
507    }
508
509    /// Convert the 2 pens in the corresponding byte
510    pub fn pens_to_byte(pen0: Pen, pen1: Pen) -> u8 {
511        pen_to_pixel_byte(pen0, PixelPosition::First)
512            + pen_to_pixel_byte(pen1, PixelPosition::Second)
513    }
514
515    /// Convert a vector of pens into a vector of bytes.
516    /// In case of an odd number of pens, the last one is lost
517    pub fn pens_to_vec_with_crop(pens: &[Pen]) -> Vec<u8> {
518        let mut res = Vec::with_capacity(pens.len());
519        for idx in 0..(pens.len() / 2) {
520            res.push(pens_to_byte(pens[idx * 2 + 0], pens[idx * 2 + 1]));
521        }
522
523        res
524    }
525
526    /// Convert a vector of pens into a vector of bytes.
527    /// In case of an odd number of pens, the missing ones are forced
528    pub fn pens_to_vec_with_replacement(pens: &[Pen], replacement: Pen) -> Vec<u8> {
529        let mut res = Vec::with_capacity(pens.len());
530        for idx in 0..(pens.len() / 2) {
531            res.push(pens_to_byte(pens[idx * 2 + 0], pens[idx * 2 + 1]));
532        }
533
534        // last pen is 0 if needed
535        if pens.len() % 2 == 1 {
536            res.push(pens_to_byte(pens[pens.len() - 1], replacement));
537        }
538
539        res
540    }
541
542    /// Convert a vector of bytes as a vector of pens
543    pub fn bytes_to_pens(bytes: &[u8]) -> Vec<Pen> {
544        super::bytes_to_pens(bytes, crate::image::Mode::Zero).collect_vec()
545    }
546
547    /// Returns a pen that corresponds to first argument in mode 0 and second in mode3
548    pub fn mix_mode0_mode3(p0: Pen, p3: Pen) -> Pen {
549        (match (p0.number(), p3.number()) {
550            (0, 0) => 0,
551
552            (0, 1) => 5,
553            (0, 2) => 6,
554            (0, 3) => 7,
555
556            (1, 0) => 8,
557            (1, 1) => 1,
558            (1, 2) => 10,
559            (1, 3) => 11,
560
561            (2, 0) => 12,
562            (2, 1) => 13,
563            (2, 2) => 2,
564            (2, 3) => 15,
565
566            (3, 0) => 4,
567            (3, 1) => 9,
568            (3, 2) => 14,
569            (3, 3) => 3,
570
571            _ => panic!()
572        })
573        .into()
574    }
575
576    /// Generate the needed table to write a masked sprite on screen when mask_pen corresponds to the pen of the background.
577    ///
578    /// Code for the display
579    /// ld e, sprite byte to display
580    /// ld d, mask_table / 256
581    /// ld a, (de) ; get the mask
582    /// and (hl) ; set to 0 all pixels that will be replaced
583    /// add e ; add the sprite value
584    /// ld (hl), a
585    ///
586    /// ld a, background
587    ///
588    /// untested code ..
589    pub fn generate_sprite_transparency_for_pen0() -> [u8; 256] {
590        // Build the bit mask for the given pen
591        let pen_to_mask = |pen: Pen| -> Pen {
592            if pen.number() == 0 {
593                // scren pen must be reseted when sprite as pixels to be drawn
594                0xF.into()
595            }
596            else {
597                0x0.into()
598            }
599        };
600
601        // Masking table to construct
602        let mut table = [0; 256];
603
604        // Generate the table
605        for (idx, byte) in (0..=255).enumerate() {
606            let [pen0, pen1] = byte_to_pens(byte);
607            table[idx] = pens_to_byte(pen_to_mask(pen0), pen_to_mask(pen1))
608        }
609
610        table
611    }
612}
613
614#[cfg(test)]
615#[allow(clippy::pedantic)]
616mod tests {
617    use super::*;
618    use crate::ga::Pen;
619
620    #[allow(clippy::similar_names)]
621    fn test_couple(a: u8, b: u8) {
622        let pa: Pen = a.into();
623        let pb: Pen = b.into();
624
625        assert_eq!(a, pa.number());
626        assert_eq!(b, pb.number());
627
628        let b = mode0::pens_to_byte(pa, pb);
629        let [pa2, pb2] = mode0::byte_to_pens(b);
630
631        assert_eq!(pa2.number(), pa2.number());
632        assert_eq!(pb2.number(), pb2.number());
633    }
634
635    #[test]
636    fn mode0() {
637        for a in 0..16 {
638            for b in 0..16 {
639                test_couple(a, b);
640            }
641        }
642    }
643
644    #[test]
645    fn bytes_to_pen() {
646        // 1000000
647        let res = crate::pixels::mode0::byte_to_pens(64);
648        assert!(res[0].number() != res[1].number());
649
650        let res = crate::pixels::mode1::byte_to_pens(0b10001000);
651        assert_eq!(res[0], Pen::from(3));
652        assert_eq!(res[1], Pen::from(0));
653        assert_eq!(res[2], Pen::from(0));
654        assert_eq!(res[3], Pen::from(0));
655
656        let res = crate::pixels::mode1::byte_to_pens(0b01000100);
657        assert_eq!(res[0], Pen::from(0));
658        assert_eq!(res[1], Pen::from(3));
659        assert_eq!(res[2], Pen::from(0));
660        assert_eq!(res[3], Pen::from(0));
661
662        let res = crate::pixels::mode1::byte_to_pens(0b00100010);
663        assert_eq!(res[0], Pen::from(0));
664        assert_eq!(res[1], Pen::from(0));
665        assert_eq!(res[2], Pen::from(3));
666        assert_eq!(res[3], Pen::from(0));
667
668        let res = crate::pixels::mode1::byte_to_pens(0b00010001);
669        assert_eq!(res[0], Pen::from(0));
670        assert_eq!(res[1], Pen::from(0));
671        assert_eq!(res[2], Pen::from(0));
672        assert_eq!(res[3], Pen::from(3));
673    }
674
675    fn test_mode3(a: Pen, b: Pen, c: Pen) {
676        let d = mode0::mix_mode0_mode3(a, b);
677        assert_eq!(d.number(), c.number());
678    }
679
680    #[test]
681    fn mode3() {
682        test_mode3(0.into(), 0.into(), 0.into());
683
684        test_mode3(0.into(), 1.into(), 5.into());
685        test_mode3(0.into(), 2.into(), 6.into());
686        test_mode3(0.into(), 3.into(), 7.into());
687
688        test_mode3(3.into(), 0.into(), 4.into());
689        test_mode3(3.into(), 1.into(), 9.into());
690        test_mode3(3.into(), 2.into(), 14.into());
691        test_mode3(3.into(), 3.into(), 3.into());
692    }
693
694    #[test]
695    fn mode2() {
696        let res = mode2::byte_to_pens(0b11000100);
697        assert_eq!(res[0], Pen::from(1));
698        assert_eq!(res[1], Pen::from(1));
699        assert_eq!(res[2], Pen::from(0));
700        assert_eq!(res[3], Pen::from(0));
701        assert_eq!(res[4], Pen::from(0));
702        assert_eq!(res[5], Pen::from(1));
703        assert_eq!(res[6], Pen::from(0));
704        assert_eq!(res[7], Pen::from(0));
705    }
706}