devela/media/image/sixel/quant/
diffuse_fns.rs

1// devela::media::image::sixel::quant::diffuse_fns
2
3#![allow(clippy::erasing_op, clippy::identity_op, reason = "symmetry")]
4
5/// Diffuses error energy to surround pixels.
6fn error_diffuse(
7    data: &mut [u8],  /* base address of pixel buffer */
8    pos: i32,         /* address of the destination pixel */
9    depth: i32,       /* color depth in bytes */
10    error: i32,       /* error energy */
11    numerator: i32,   /* numerator of diffusion coefficient */
12    denominator: i32, /* denominator of diffusion coefficient */
13) {
14    let offset = (pos * depth) as usize;
15
16    let mut c = data[offset] as i32 + error * numerator / denominator;
17    if c < 0 {
18        c = 0;
19    }
20    if c >= 1 << 8 {
21        c = (1 << 8) - 1;
22    }
23    data[offset] = c as u8;
24}
25
26/// Floyd Steinberg diffuse
27///
28/// ```txt
29///          curr    7/16
30///  3/16    5/48    1/16
31/// ```
32pub(super) fn diffuse_fs(
33    data: &mut [u8],
34    width: i32,
35    height: i32,
36    x: i32,
37    y: i32,
38    depth: i32,
39    error: i32,
40) {
41    let pos = y * width + x;
42    if x < width - 1 && y < height - 1 {
43        // add error to the right cell
44        error_diffuse(data, pos + width * 0 + 1, depth, error, 7, 16);
45        // add error to the left-bottom cell
46        error_diffuse(data, pos + width * 1 - 1, depth, error, 3, 16);
47        // add error to the bottom cell
48        error_diffuse(data, pos + width * 1 + 0, depth, error, 5, 16);
49        // add error to the right-bottom cell
50        error_diffuse(data, pos + width * 1 + 1, depth, error, 1, 16);
51    }
52}
53
54/// Atkinson's diffuse
55///
56/// ```txt
57///          curr    1/8    1/8
58///   1/8     1/8    1/8
59///           1/8
60/// ```
61pub(super) fn diffuse_atkinson(
62    data: &mut [u8],
63    width: i32,
64    height: i32,
65    x: i32,
66    y: i32,
67    depth: i32,
68    error: i32,
69) {
70    let pos = y * width + x;
71    if y < height - 2 {
72        // add error to the right cell
73        error_diffuse(data, pos + width * 0 + 1, depth, error, 1, 8);
74        // add error to the 2th right cell
75        error_diffuse(data, pos + width * 0 + 2, depth, error, 1, 8);
76        // add error to the left-bottom cell
77        error_diffuse(data, pos + width * 1 - 1, depth, error, 1, 8);
78        // add error to the bottom cell
79        error_diffuse(data, pos + width * 1 + 0, depth, error, 1, 8);
80        // add error to the right-bottom cell
81        error_diffuse(data, pos + width * 1 + 1, depth, error, 1, 8);
82        // add error to the 2th bottom cell
83        error_diffuse(data, pos + width * 2 + 0, depth, error, 1, 8);
84    }
85}
86
87/// Jarvis, Judice & Ninke diffusion
88///
89/// ```txt
90///                  curr    7/48    5/48
91///  3/48    5/48    7/48    5/48    3/48
92///  1/48    3/48    5/48    3/48    1/48
93/// ```
94pub(super) fn diffuse_jajuni(
95    data: &mut [u8],
96    width: i32,
97    height: i32,
98    x: i32,
99    y: i32,
100    depth: i32,
101    error: i32,
102) {
103    let pos = y * width + x;
104    if pos < (height - 2) * width - 2 {
105        error_diffuse(data, pos + width * 0 + 1, depth, error, 7, 48);
106        error_diffuse(data, pos + width * 0 + 2, depth, error, 5, 48);
107        error_diffuse(data, pos + width * 1 - 2, depth, error, 3, 48);
108        error_diffuse(data, pos + width * 1 - 1, depth, error, 5, 48);
109        error_diffuse(data, pos + width * 1 + 0, depth, error, 7, 48);
110        error_diffuse(data, pos + width * 1 + 1, depth, error, 5, 48);
111        error_diffuse(data, pos + width * 1 + 2, depth, error, 3, 48);
112        error_diffuse(data, pos + width * 2 - 2, depth, error, 1, 48);
113        error_diffuse(data, pos + width * 2 - 1, depth, error, 3, 48);
114        error_diffuse(data, pos + width * 2 + 0, depth, error, 5, 48);
115        error_diffuse(data, pos + width * 2 + 1, depth, error, 3, 48);
116        error_diffuse(data, pos + width * 2 + 2, depth, error, 1, 48);
117    }
118}
119
120/// Stucki's diffusion
121///
122/// ```txt
123///                  curr    8/48    4/48
124///  2/48    4/48    8/48    4/48    2/48
125///  1/48    2/48    4/48    2/48    1/48
126/// ```
127pub(super) fn diffuse_stucki(
128    data: &mut [u8],
129    width: i32,
130    height: i32,
131    x: i32,
132    y: i32,
133    depth: i32,
134    error: i32,
135) {
136    let pos = y * width + x;
137    if pos < (height - 2) * width - 2 {
138        error_diffuse(data, pos + width * 0 + 1, depth, error, 1, 6);
139        error_diffuse(data, pos + width * 0 + 2, depth, error, 1, 12);
140        error_diffuse(data, pos + width * 1 - 2, depth, error, 1, 24);
141        error_diffuse(data, pos + width * 1 - 1, depth, error, 1, 12);
142        error_diffuse(data, pos + width * 1 + 0, depth, error, 1, 6);
143        error_diffuse(data, pos + width * 1 + 1, depth, error, 1, 12);
144        error_diffuse(data, pos + width * 1 + 2, depth, error, 1, 24);
145        error_diffuse(data, pos + width * 2 - 2, depth, error, 1, 48);
146        error_diffuse(data, pos + width * 2 - 1, depth, error, 1, 24);
147        error_diffuse(data, pos + width * 2 + 0, depth, error, 1, 12);
148        error_diffuse(data, pos + width * 2 + 1, depth, error, 1, 24);
149        error_diffuse(data, pos + width * 2 + 2, depth, error, 1, 48);
150    }
151}
152
153/// Burkes' diffusion
154///
155/// ```txt
156///                  curr    4/16    2/16
157///  1/16    2/16    4/16    2/16    1/16
158/// ```
159pub(super) fn diffuse_burkes(
160    data: &mut [u8],
161    width: i32,
162    height: i32,
163    x: i32,
164    y: i32,
165    depth: i32,
166    error: i32,
167) {
168    let pos = y * width + x;
169    if pos < (height - 1) * width - 2 {
170        error_diffuse(data, pos + width * 0 + 1, depth, error, 1, 4);
171        error_diffuse(data, pos + width * 0 + 2, depth, error, 1, 8);
172        error_diffuse(data, pos + width * 1 - 2, depth, error, 1, 16);
173        error_diffuse(data, pos + width * 1 - 1, depth, error, 1, 8);
174        error_diffuse(data, pos + width * 1 + 0, depth, error, 1, 4);
175        error_diffuse(data, pos + width * 1 + 1, depth, error, 1, 8);
176        error_diffuse(data, pos + width * 1 + 2, depth, error, 1, 16);
177    }
178}