1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// raster.rs    A 2D raster image.
//
// Copyright (c) 2017-2019  Douglas P Lau
//
use pix::Raster;
use crate::Blend;

/// Blend targets with `over` operation.
///
/// * `dst` Destination target.
/// * `src` Source target.
/// * `clr` Default blend color.
/// * `x` Left position of source on destination.
/// * `y` Top position of source on destination.
pub fn raster_over<A, B, C>(dst: &mut Raster<A>, src: &Raster<B>, clr: C,
    x: i32, y: i32)
    where A: Blend, B: Blend, C: Blend, A: From<B>, A: From<C>
{
    let clr = Into::<A>::into(clr);
    if x == 0 && dst.width() == src.width() &&
       y == 0 && dst.height() == src.height()
    {
        A::over_slice(dst.as_slice_mut(), src.as_slice(), clr);
        return;
    }
    if x + (src.width() as i32) < 0 || x >= dst.width() as i32 {
        return; // positioned off left or right edge
    }
    if y + (src.height() as i32) < 0 || y >= dst.height() as i32 {
        return; // positioned off top or bottom edge
    }
    let mx = 0.max(-x) as usize;
    let my = 0.max(-y) as u32;
    let mw = src.width() as usize;
    let dx = 0.max(x) as usize;
    let dy = 0.max(y) as u32;
    let dw = dst.width() as usize;
    let h = (dst.height() - dy).min(src.height() - my);
    for yi in 0..h {
        let mut row = &mut dst.as_slice_row_mut(dy + yi)[dx..dw];
        let m = &src.as_slice_row(my + yi)[mx..mw];
        A::over_slice(&mut row, m, clr);
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use pix::*;
    #[test]
    fn raster_mask() {
        let mut r = RasterBuilder::<Rgba8>::new().with_clear(3, 3);
        let mut m = RasterBuilder::<Mask8>::new().with_clear(3, 3);
        let c: Rgb8 = Rgb::new(0xFF, 0x80, 0x40);
        m.set_pixel(0, 0, 0xFF);
        m.set_pixel(1, 1, 0x80);
        m.set_pixel(2, 2, 0x40);
        raster_over(&mut r, &m, c, 0, 0);
        let v = vec![
            0xFF,0x80,0x40,0xFF, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
            0x00,0x00,0x00,0x00, 0x80,0x40,0x20,0x80, 0x00,0x00,0x00,0x00,
            0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x40,0x20,0x10,0x40,
        ];
        let left = r.as_u8_slice();
        // NOTE: fallback version     SIMD version
        assert!(left[0] == 0xFF || left[0] == 0xFE);
        assert!(left[1] == 0x80 || left[1] == 0x7F);
        assert!(left[2] == 0x40 || left[2] == 0x3F);
        assert!(left[3] == 0xFF || left[3] == 0xFE);
        assert_eq!(&left[4..], &v[4..]);
    }
    #[test]
    fn smaller_mask() {
        let mut r = RasterBuilder::<Rgba8>::new().with_clear(3, 3);
        let mut m = RasterBuilder::<Mask8>::new().with_clear(2, 2);
        let c: Rgba8 = Rgb::with_alpha(0x40, 0xFF, 0x80, Translucent::new(
            Ch8::new(0x80)));
        m.set_pixel(0, 0, 0xFF);
        m.set_pixel(1, 0, 0x80);
        m.set_pixel(0, 1, 0x40);
        m.set_pixel(1, 1, 0x20);
        raster_over(&mut r, &m, c, 1, 1);
        let v = vec![
            0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
            0x00,0x00,0x00,0x00, 0x40,0xFF,0x80,0x80, 0x20,0x80,0x40,0x40,
            0x00,0x00,0x00,0x00, 0x10,0x40,0x20,0x20, 0x08,0x20,0x10,0x10,
        ];
        assert_eq!(r.as_u8_slice(), &v[..]);
    }
    #[test]
    fn top_left() {
        let mut r = RasterBuilder::<Rgba8>::new().with_clear(3, 3);
        let mut m = RasterBuilder::<Mask8>::new().with_clear(2, 2);
        let c: Rgb8 = Rgb::new(0x20, 0x40, 0x80);
        m.set_pixel(0, 0, 0xFF);
        m.set_pixel(1, 0, 0xFF);
        m.set_pixel(0, 1, 0xFF);
        m.set_pixel(1, 1, 0xFF);
        raster_over(&mut r, &m, c, -1, -1);
        let v = vec![
            0x20,0x40,0x80,0xFF, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
            0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
            0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
        ];
        assert_eq!(r.as_u8_slice(), &v[..]);
    }
    #[test]
    fn bottom_right() {
        let mut r = RasterBuilder::<Rgba8>::new().with_clear(3, 3);
        let mut m = RasterBuilder::<Mask8>::new().with_clear(2, 2);
        let c: Rgb8 = Rgb::new(0x20, 0x40, 0x80);
        m.set_pixel(0, 0, 0xFF);
        m.set_pixel(1, 0, 0xFF);
        m.set_pixel(0, 1, 0xFF);
        m.set_pixel(1, 1, 0xFF);
        raster_over(&mut r, &m, c, 2, 2);
        let v = vec![
            0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
            0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
            0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x20,0x40,0x80,0xFF,
        ];
        assert_eq!(r.as_u8_slice(), &v[..]);
    }
}