Skip to main content

agg_rust/
alpha_mask_u8.rs

1//! Alpha masking with clipping support.
2//!
3//! Port of `agg_alpha_mask_u8.h` — provides alpha mask functionality where
4//! pixel coverage values are modulated by a grayscale mask buffer.
5
6use crate::rendering_buffer::RowAccessor;
7
8// ============================================================================
9// MaskFunction trait
10// ============================================================================
11
12/// Trait for computing a mask value from pixel data.
13///
14/// Port of C++ `one_component_mask_u8` / `rgb_to_gray_mask_u8` patterns.
15pub trait MaskFunction {
16    fn calculate(&self, p: &[u8]) -> u8;
17}
18
19/// Single-component mask: returns the first byte.
20///
21/// Port of C++ `one_component_mask_u8`.
22#[derive(Clone, Copy, Default)]
23pub struct OneComponentMask;
24
25impl MaskFunction for OneComponentMask {
26    #[inline]
27    fn calculate(&self, p: &[u8]) -> u8 {
28        p[0]
29    }
30}
31
32/// RGB-to-gray mask: weighted sum of R, G, B channels.
33///
34/// Port of C++ `rgb_to_gray_mask_u8<R, G, B>`.
35/// Uses luminance weights: R*77 + G*150 + B*29 >> 8.
36#[derive(Clone, Copy)]
37pub struct RgbToGrayMask {
38    pub r_offset: usize,
39    pub g_offset: usize,
40    pub b_offset: usize,
41}
42
43impl RgbToGrayMask {
44    pub const fn new(r: usize, g: usize, b: usize) -> Self {
45        Self {
46            r_offset: r,
47            g_offset: g,
48            b_offset: b,
49        }
50    }
51}
52
53impl MaskFunction for RgbToGrayMask {
54    #[inline]
55    fn calculate(&self, p: &[u8]) -> u8 {
56        ((p[self.r_offset] as u32 * 77
57            + p[self.g_offset] as u32 * 150
58            + p[self.b_offset] as u32 * 29)
59            >> 8) as u8
60    }
61}
62
63// ============================================================================
64// AlphaMask trait
65// ============================================================================
66
67/// Alpha mask interface for coverage modulation.
68pub trait AlphaMask {
69    fn pixel(&self, x: i32, y: i32) -> u8;
70    fn combine_pixel(&self, x: i32, y: i32, val: u8) -> u8;
71    fn fill_hspan(&self, x: i32, y: i32, dst: &mut [u8]);
72    fn combine_hspan(&self, x: i32, y: i32, dst: &mut [u8]);
73    fn fill_vspan(&self, x: i32, y: i32, dst: &mut [u8]);
74    fn combine_vspan(&self, x: i32, y: i32, dst: &mut [u8]);
75}
76
77// ============================================================================
78// AlphaMaskU8 — clipped alpha mask
79// ============================================================================
80
81const COVER_SHIFT: u32 = 8;
82const COVER_FULL: u32 = 255;
83
84/// Alpha mask with bounds-checked access to a rendering buffer.
85///
86/// `STEP` is the number of bytes per pixel, `OFFSET` is the byte offset
87/// within each pixel to the mask component.
88///
89/// Port of C++ `alpha_mask_u8<Step, Offset, MaskF>`.
90pub struct AlphaMaskU8<'a, const STEP: usize, const OFFSET: usize, MF: MaskFunction> {
91    rbuf: &'a RowAccessor,
92    mask_function: MF,
93}
94
95impl<'a, const STEP: usize, const OFFSET: usize, MF: MaskFunction>
96    AlphaMaskU8<'a, STEP, OFFSET, MF>
97{
98    pub fn new(rbuf: &'a RowAccessor, mask_function: MF) -> Self {
99        Self {
100            rbuf,
101            mask_function,
102        }
103    }
104
105    pub fn mask_function(&self) -> &MF {
106        &self.mask_function
107    }
108}
109
110impl<const STEP: usize, const OFFSET: usize, MF: MaskFunction> AlphaMask
111    for AlphaMaskU8<'_, STEP, OFFSET, MF>
112{
113    fn pixel(&self, x: i32, y: i32) -> u8 {
114        if x >= 0 && y >= 0 && x < self.rbuf.width() as i32 && y < self.rbuf.height() as i32 {
115            let row = self.rbuf.row_slice(y as u32);
116            let off = x as usize * STEP + OFFSET;
117            self.mask_function.calculate(&row[off..])
118        } else {
119            0
120        }
121    }
122
123    fn combine_pixel(&self, x: i32, y: i32, val: u8) -> u8 {
124        if x >= 0 && y >= 0 && x < self.rbuf.width() as i32 && y < self.rbuf.height() as i32 {
125            let row = self.rbuf.row_slice(y as u32);
126            let off = x as usize * STEP + OFFSET;
127            ((COVER_FULL + val as u32 * self.mask_function.calculate(&row[off..]) as u32)
128                >> COVER_SHIFT) as u8
129        } else {
130            0
131        }
132    }
133
134    fn fill_hspan(&self, x: i32, y: i32, dst: &mut [u8]) {
135        let num_pix = dst.len() as i32;
136        let xmax = self.rbuf.width() as i32 - 1;
137        let ymax = self.rbuf.height() as i32 - 1;
138
139        let mut count = num_pix;
140        let mut covers_off: usize = 0;
141        let mut x = x;
142
143        if y < 0 || y > ymax {
144            dst.iter_mut().for_each(|d| *d = 0);
145            return;
146        }
147
148        if x < 0 {
149            count += x;
150            if count <= 0 {
151                dst.iter_mut().for_each(|d| *d = 0);
152                return;
153            }
154            dst[..(-x) as usize].iter_mut().for_each(|d| *d = 0);
155            covers_off = (-x) as usize;
156            x = 0;
157        }
158
159        if x + count > xmax + 1 {
160            let rest = x + count - xmax - 1;
161            count -= rest;
162            if count <= 0 {
163                dst.iter_mut().for_each(|d| *d = 0);
164                return;
165            }
166            dst[(covers_off + count as usize)..]
167                .iter_mut()
168                .for_each(|d| *d = 0);
169        }
170
171        let row = self.rbuf.row_slice(y as u32);
172        let mut mask_off = x as usize * STEP + OFFSET;
173        for i in 0..count as usize {
174            dst[covers_off + i] = self.mask_function.calculate(&row[mask_off..]);
175            mask_off += STEP;
176        }
177    }
178
179    fn combine_hspan(&self, x: i32, y: i32, dst: &mut [u8]) {
180        let num_pix = dst.len() as i32;
181        let xmax = self.rbuf.width() as i32 - 1;
182        let ymax = self.rbuf.height() as i32 - 1;
183
184        let mut count = num_pix;
185        let mut covers_off: usize = 0;
186        let mut x = x;
187
188        if y < 0 || y > ymax {
189            dst.iter_mut().for_each(|d| *d = 0);
190            return;
191        }
192
193        if x < 0 {
194            count += x;
195            if count <= 0 {
196                dst.iter_mut().for_each(|d| *d = 0);
197                return;
198            }
199            dst[..(-x) as usize].iter_mut().for_each(|d| *d = 0);
200            covers_off = (-x) as usize;
201            x = 0;
202        }
203
204        if x + count > xmax + 1 {
205            let rest = x + count - xmax - 1;
206            count -= rest;
207            if count <= 0 {
208                dst.iter_mut().for_each(|d| *d = 0);
209                return;
210            }
211            dst[(covers_off + count as usize)..]
212                .iter_mut()
213                .for_each(|d| *d = 0);
214        }
215
216        let row = self.rbuf.row_slice(y as u32);
217        let mut mask_off = x as usize * STEP + OFFSET;
218        for i in 0..count as usize {
219            let idx = covers_off + i;
220            dst[idx] = ((COVER_FULL
221                + dst[idx] as u32 * self.mask_function.calculate(&row[mask_off..]) as u32)
222                >> COVER_SHIFT) as u8;
223            mask_off += STEP;
224        }
225    }
226
227    fn fill_vspan(&self, x: i32, y: i32, dst: &mut [u8]) {
228        let num_pix = dst.len() as i32;
229        let xmax = self.rbuf.width() as i32 - 1;
230        let ymax = self.rbuf.height() as i32 - 1;
231
232        let mut count = num_pix;
233        let mut covers_off: usize = 0;
234        let mut y = y;
235
236        if x < 0 || x > xmax {
237            dst.iter_mut().for_each(|d| *d = 0);
238            return;
239        }
240
241        if y < 0 {
242            count += y;
243            if count <= 0 {
244                dst.iter_mut().for_each(|d| *d = 0);
245                return;
246            }
247            dst[..(-y) as usize].iter_mut().for_each(|d| *d = 0);
248            covers_off = (-y) as usize;
249            y = 0;
250        }
251
252        if y + count > ymax + 1 {
253            let rest = y + count - ymax - 1;
254            count -= rest;
255            if count <= 0 {
256                dst.iter_mut().for_each(|d| *d = 0);
257                return;
258            }
259            dst[(covers_off + count as usize)..]
260                .iter_mut()
261                .for_each(|d| *d = 0);
262        }
263
264        let col = x as usize * STEP + OFFSET;
265        for i in 0..count as usize {
266            let row = self.rbuf.row_slice((y + i as i32) as u32);
267            dst[covers_off + i] = self.mask_function.calculate(&row[col..]);
268        }
269    }
270
271    fn combine_vspan(&self, x: i32, y: i32, dst: &mut [u8]) {
272        let num_pix = dst.len() as i32;
273        let xmax = self.rbuf.width() as i32 - 1;
274        let ymax = self.rbuf.height() as i32 - 1;
275
276        let mut count = num_pix;
277        let mut covers_off: usize = 0;
278        let mut y = y;
279
280        if x < 0 || x > xmax {
281            dst.iter_mut().for_each(|d| *d = 0);
282            return;
283        }
284
285        if y < 0 {
286            count += y;
287            if count <= 0 {
288                dst.iter_mut().for_each(|d| *d = 0);
289                return;
290            }
291            dst[..(-y) as usize].iter_mut().for_each(|d| *d = 0);
292            covers_off = (-y) as usize;
293            y = 0;
294        }
295
296        if y + count > ymax + 1 {
297            let rest = y + count - ymax - 1;
298            count -= rest;
299            if count <= 0 {
300                dst.iter_mut().for_each(|d| *d = 0);
301                return;
302            }
303            dst[(covers_off + count as usize)..]
304                .iter_mut()
305                .for_each(|d| *d = 0);
306        }
307
308        let col = x as usize * STEP + OFFSET;
309        for i in 0..count as usize {
310            let row = self.rbuf.row_slice((y + i as i32) as u32);
311            let idx = covers_off + i;
312            dst[idx] = ((COVER_FULL
313                + dst[idx] as u32 * self.mask_function.calculate(&row[col..]) as u32)
314                >> COVER_SHIFT) as u8;
315        }
316    }
317}
318
319// ============================================================================
320// AmaskNoClipU8 — unchecked alpha mask
321// ============================================================================
322
323/// Alpha mask without bounds checking — faster but caller must ensure in-range.
324///
325/// Port of C++ `amask_no_clip_u8<Step, Offset, MaskF>`.
326pub struct AmaskNoClipU8<'a, const STEP: usize, const OFFSET: usize, MF: MaskFunction> {
327    rbuf: &'a RowAccessor,
328    mask_function: MF,
329}
330
331impl<'a, const STEP: usize, const OFFSET: usize, MF: MaskFunction>
332    AmaskNoClipU8<'a, STEP, OFFSET, MF>
333{
334    pub fn new(rbuf: &'a RowAccessor, mask_function: MF) -> Self {
335        Self {
336            rbuf,
337            mask_function,
338        }
339    }
340
341    pub fn mask_function(&self) -> &MF {
342        &self.mask_function
343    }
344}
345
346impl<const STEP: usize, const OFFSET: usize, MF: MaskFunction> AlphaMask
347    for AmaskNoClipU8<'_, STEP, OFFSET, MF>
348{
349    fn pixel(&self, x: i32, y: i32) -> u8 {
350        let row = self.rbuf.row_slice(y as u32);
351        let off = x as usize * STEP + OFFSET;
352        self.mask_function.calculate(&row[off..])
353    }
354
355    fn combine_pixel(&self, x: i32, y: i32, val: u8) -> u8 {
356        let row = self.rbuf.row_slice(y as u32);
357        let off = x as usize * STEP + OFFSET;
358        ((COVER_FULL + val as u32 * self.mask_function.calculate(&row[off..]) as u32)
359            >> COVER_SHIFT) as u8
360    }
361
362    fn fill_hspan(&self, x: i32, y: i32, dst: &mut [u8]) {
363        let row = self.rbuf.row_slice(y as u32);
364        let mut mask_off = x as usize * STEP + OFFSET;
365        for d in dst.iter_mut() {
366            *d = self.mask_function.calculate(&row[mask_off..]);
367            mask_off += STEP;
368        }
369    }
370
371    fn combine_hspan(&self, x: i32, y: i32, dst: &mut [u8]) {
372        let row = self.rbuf.row_slice(y as u32);
373        let mut mask_off = x as usize * STEP + OFFSET;
374        for d in dst.iter_mut() {
375            *d = ((COVER_FULL + *d as u32 * self.mask_function.calculate(&row[mask_off..]) as u32)
376                >> COVER_SHIFT) as u8;
377            mask_off += STEP;
378        }
379    }
380
381    fn fill_vspan(&self, x: i32, y: i32, dst: &mut [u8]) {
382        let col = x as usize * STEP + OFFSET;
383        for (i, d) in dst.iter_mut().enumerate() {
384            let row = self.rbuf.row_slice((y + i as i32) as u32);
385            *d = self.mask_function.calculate(&row[col..]);
386        }
387    }
388
389    fn combine_vspan(&self, x: i32, y: i32, dst: &mut [u8]) {
390        let col = x as usize * STEP + OFFSET;
391        for (i, d) in dst.iter_mut().enumerate() {
392            let row = self.rbuf.row_slice((y + i as i32) as u32);
393            *d = ((COVER_FULL + *d as u32 * self.mask_function.calculate(&row[col..]) as u32)
394                >> COVER_SHIFT) as u8;
395        }
396    }
397}
398
399// ============================================================================
400// Type aliases for common configurations
401// ============================================================================
402
403/// Gray8 alpha mask (1 byte per pixel, offset 0).
404pub type AlphaMaskGray8<'a> = AlphaMaskU8<'a, 1, 0, OneComponentMask>;
405
406/// RGBA32 red channel mask.
407pub type AlphaMaskRgba32r<'a> = AlphaMaskU8<'a, 4, 0, OneComponentMask>;
408/// RGBA32 green channel mask.
409pub type AlphaMaskRgba32g<'a> = AlphaMaskU8<'a, 4, 1, OneComponentMask>;
410/// RGBA32 blue channel mask.
411pub type AlphaMaskRgba32b<'a> = AlphaMaskU8<'a, 4, 2, OneComponentMask>;
412/// RGBA32 alpha channel mask.
413pub type AlphaMaskRgba32a<'a> = AlphaMaskU8<'a, 4, 3, OneComponentMask>;
414
415/// RGB24 red channel mask.
416pub type AlphaMaskRgb24r<'a> = AlphaMaskU8<'a, 3, 0, OneComponentMask>;
417/// RGB24 green channel mask.
418pub type AlphaMaskRgb24g<'a> = AlphaMaskU8<'a, 3, 1, OneComponentMask>;
419/// RGB24 blue channel mask.
420pub type AlphaMaskRgb24b<'a> = AlphaMaskU8<'a, 3, 2, OneComponentMask>;
421
422// No-clip variants
423/// Gray8 alpha mask, no clipping.
424pub type AmaskNoClipGray8<'a> = AmaskNoClipU8<'a, 1, 0, OneComponentMask>;
425/// RGBA32 alpha channel mask, no clipping.
426pub type AmaskNoClipRgba32a<'a> = AmaskNoClipU8<'a, 4, 3, OneComponentMask>;
427
428// ============================================================================
429// Tests
430// ============================================================================
431
432#[cfg(test)]
433mod tests {
434    use super::*;
435
436    fn make_gray_buffer(width: u32, height: u32, data: &mut Vec<u8>) -> RowAccessor {
437        data.resize((width * height) as usize, 0);
438        unsafe { RowAccessor::new_with_buf(data.as_mut_ptr(), width, height, width as i32) }
439    }
440
441    fn make_rgba_buffer(width: u32, height: u32, data: &mut Vec<u8>) -> RowAccessor {
442        let stride = width * 4;
443        data.resize((stride * height) as usize, 0);
444        unsafe { RowAccessor::new_with_buf(data.as_mut_ptr(), width, height, stride as i32) }
445    }
446
447    #[test]
448    fn test_one_component_mask() {
449        let m = OneComponentMask;
450        assert_eq!(m.calculate(&[128, 0, 0, 0]), 128);
451        assert_eq!(m.calculate(&[255]), 255);
452        assert_eq!(m.calculate(&[0, 99]), 0);
453    }
454
455    #[test]
456    fn test_rgb_to_gray_mask() {
457        let m = RgbToGrayMask::new(0, 1, 2);
458        // Pure red: 255*77/256 ≈ 76
459        let val = m.calculate(&[255, 0, 0]);
460        assert_eq!(val, ((255u32 * 77) >> 8) as u8);
461
462        // Pure green: 255*150/256 ≈ 149
463        let val = m.calculate(&[0, 255, 0]);
464        assert_eq!(val, ((255u32 * 150) >> 8) as u8);
465
466        // Pure blue: 255*29/256 ≈ 28
467        let val = m.calculate(&[0, 0, 255]);
468        assert_eq!(val, ((255u32 * 29) >> 8) as u8);
469
470        // White: (255*77 + 255*150 + 255*29)/256 = 255*256/256 = 255
471        let val = m.calculate(&[255, 255, 255]);
472        assert_eq!(val, 255);
473    }
474
475    #[test]
476    fn test_pixel_in_bounds() {
477        let mut data = Vec::new();
478        let rbuf = make_gray_buffer(4, 4, &mut data);
479        // Set pixel (2,1) to 200
480        data[1 * 4 + 2] = 200;
481        let mask = AlphaMaskGray8::new(&rbuf, OneComponentMask);
482        assert_eq!(mask.pixel(2, 1), 200);
483    }
484
485    #[test]
486    fn test_pixel_out_of_bounds() {
487        let mut data = Vec::new();
488        let rbuf = make_gray_buffer(4, 4, &mut data);
489        let mask = AlphaMaskGray8::new(&rbuf, OneComponentMask);
490        assert_eq!(mask.pixel(-1, 0), 0);
491        assert_eq!(mask.pixel(0, -1), 0);
492        assert_eq!(mask.pixel(4, 0), 0);
493        assert_eq!(mask.pixel(0, 4), 0);
494    }
495
496    #[test]
497    fn test_combine_pixel() {
498        let mut data = Vec::new();
499        let rbuf = make_gray_buffer(4, 4, &mut data);
500        data[0] = 128;
501        let mask = AlphaMaskGray8::new(&rbuf, OneComponentMask);
502        // combine: (255 + val * mask_val) >> 8 = (255 + 200 * 128) >> 8
503        let result = mask.combine_pixel(0, 0, 200);
504        assert_eq!(result, ((255 + 200u32 * 128) >> 8) as u8);
505    }
506
507    #[test]
508    fn test_combine_pixel_out_of_bounds() {
509        let mut data = Vec::new();
510        let rbuf = make_gray_buffer(4, 4, &mut data);
511        let mask = AlphaMaskGray8::new(&rbuf, OneComponentMask);
512        assert_eq!(mask.combine_pixel(-1, 0, 200), 0);
513    }
514
515    #[test]
516    fn test_fill_hspan_in_bounds() {
517        let mut data = vec![0u8; 16];
518        for i in 0..4 {
519            data[i] = (i as u8 + 1) * 50;
520        }
521        let rbuf = unsafe { RowAccessor::new_with_buf(data.as_mut_ptr(), 4, 4, 4) };
522        let mask = AlphaMaskGray8::new(&rbuf, OneComponentMask);
523
524        let mut dst = vec![0u8; 4];
525        mask.fill_hspan(0, 0, &mut dst);
526        assert_eq!(dst, vec![50, 100, 150, 200]);
527    }
528
529    #[test]
530    fn test_fill_hspan_y_out_of_range() {
531        let mut data = Vec::new();
532        let rbuf = make_gray_buffer(4, 4, &mut data);
533        let mask = AlphaMaskGray8::new(&rbuf, OneComponentMask);
534
535        let mut dst = vec![99u8; 4];
536        mask.fill_hspan(0, -1, &mut dst);
537        assert_eq!(dst, vec![0, 0, 0, 0]);
538
539        let mut dst = vec![99u8; 4];
540        mask.fill_hspan(0, 4, &mut dst);
541        assert_eq!(dst, vec![0, 0, 0, 0]);
542    }
543
544    #[test]
545    fn test_fill_hspan_left_clip() {
546        let mut data = vec![0u8; 16];
547        for i in 0..4 {
548            data[i] = (i as u8 + 1) * 50;
549        }
550        let rbuf = unsafe { RowAccessor::new_with_buf(data.as_mut_ptr(), 4, 4, 4) };
551        let mask = AlphaMaskGray8::new(&rbuf, OneComponentMask);
552
553        let mut dst = vec![99u8; 3];
554        mask.fill_hspan(-1, 0, &mut dst);
555        // First element should be 0 (clipped), rest from x=0,1
556        assert_eq!(dst[0], 0);
557        assert_eq!(dst[1], 50); // data[0]
558        assert_eq!(dst[2], 100); // data[1]
559    }
560
561    #[test]
562    fn test_fill_hspan_right_clip() {
563        let mut data = vec![0u8; 16];
564        for i in 0..4 {
565            data[i] = (i as u8 + 1) * 50;
566        }
567        let rbuf = unsafe { RowAccessor::new_with_buf(data.as_mut_ptr(), 4, 4, 4) };
568        let mask = AlphaMaskGray8::new(&rbuf, OneComponentMask);
569
570        let mut dst = vec![99u8; 3];
571        mask.fill_hspan(2, 0, &mut dst);
572        // x=2,3 valid, x=4 clipped
573        assert_eq!(dst[0], 150); // data[2]
574        assert_eq!(dst[1], 200); // data[3]
575        assert_eq!(dst[2], 0); // clipped
576    }
577
578    #[test]
579    fn test_combine_hspan() {
580        let mut data = vec![0u8; 16];
581        data[0] = 128;
582        data[1] = 255;
583        let rbuf = unsafe { RowAccessor::new_with_buf(data.as_mut_ptr(), 4, 4, 4) };
584        let mask = AlphaMaskGray8::new(&rbuf, OneComponentMask);
585
586        let mut dst = vec![200u8, 100u8];
587        mask.combine_hspan(0, 0, &mut dst);
588        assert_eq!(dst[0], ((255 + 200u32 * 128) >> 8) as u8);
589        assert_eq!(dst[1], ((255 + 100u32 * 255) >> 8) as u8);
590    }
591
592    #[test]
593    fn test_fill_vspan() {
594        let mut data = vec![0u8; 16];
595        // Set column 1, rows 0-3
596        data[1] = 10;
597        data[4 + 1] = 20;
598        data[8 + 1] = 30;
599        data[12 + 1] = 40;
600        let rbuf = unsafe { RowAccessor::new_with_buf(data.as_mut_ptr(), 4, 4, 4) };
601        let mask = AlphaMaskGray8::new(&rbuf, OneComponentMask);
602
603        let mut dst = vec![0u8; 4];
604        mask.fill_vspan(1, 0, &mut dst);
605        assert_eq!(dst, vec![10, 20, 30, 40]);
606    }
607
608    #[test]
609    fn test_fill_vspan_x_out_of_range() {
610        let mut data = Vec::new();
611        let rbuf = make_gray_buffer(4, 4, &mut data);
612        let mask = AlphaMaskGray8::new(&rbuf, OneComponentMask);
613
614        let mut dst = vec![99u8; 4];
615        mask.fill_vspan(-1, 0, &mut dst);
616        assert_eq!(dst, vec![0, 0, 0, 0]);
617    }
618
619    #[test]
620    fn test_rgba_mask() {
621        let mut data = Vec::new();
622        let rbuf = make_rgba_buffer(4, 4, &mut data);
623        // Set pixel (0,0) RGBA = (100, 150, 200, 255)
624        data[0] = 100;
625        data[1] = 150;
626        data[2] = 200;
627        data[3] = 255;
628
629        // Alpha channel mask (step=4, offset=3)
630        let mask = AlphaMaskRgba32a::new(&rbuf, OneComponentMask);
631        assert_eq!(mask.pixel(0, 0), 255);
632
633        // Red channel mask (step=4, offset=0)
634        let rmask = AlphaMaskRgba32r::new(&rbuf, OneComponentMask);
635        assert_eq!(rmask.pixel(0, 0), 100);
636    }
637
638    #[test]
639    fn test_no_clip_pixel() {
640        let mut data = vec![0u8; 16];
641        data[5] = 42; // row 1, pixel 1
642        let rbuf = unsafe { RowAccessor::new_with_buf(data.as_mut_ptr(), 4, 4, 4) };
643        let mask = AmaskNoClipGray8::new(&rbuf, OneComponentMask);
644        assert_eq!(mask.pixel(1, 1), 42);
645    }
646
647    #[test]
648    fn test_no_clip_fill_hspan() {
649        let mut data = vec![0u8; 16];
650        data[0] = 10;
651        data[1] = 20;
652        data[2] = 30;
653        let rbuf = unsafe { RowAccessor::new_with_buf(data.as_mut_ptr(), 4, 4, 4) };
654        let mask = AmaskNoClipGray8::new(&rbuf, OneComponentMask);
655
656        let mut dst = vec![0u8; 3];
657        mask.fill_hspan(0, 0, &mut dst);
658        assert_eq!(dst, vec![10, 20, 30]);
659    }
660
661    #[test]
662    fn test_no_clip_combine_pixel() {
663        let mut data = vec![0u8; 16];
664        data[0] = 128;
665        let rbuf = unsafe { RowAccessor::new_with_buf(data.as_mut_ptr(), 4, 4, 4) };
666        let mask = AmaskNoClipGray8::new(&rbuf, OneComponentMask);
667        let result = mask.combine_pixel(0, 0, 200);
668        assert_eq!(result, ((255 + 200u32 * 128) >> 8) as u8);
669    }
670}