qr_image_core/renderer/
mod.rs

1use crate::{QrCode, QrImage, QrResult, Version};
2use image::{
3    imageops::{resize, FilterType},
4    DynamicImage, GenericImage, GenericImageView, Rgb, RgbImage,
5};
6
7impl QrImage {
8    fn target_qr(&self, data: &[u8]) -> QrResult<QrCode> {
9        if self.auto_size {
10            match QrCode::with_version(data, self.qr_version, self.ec_level) {
11                Ok(o) => Ok(o),
12                Err(_) => match QrCode::with_error_correction_level(data, self.ec_level) {
13                    Ok(o) => Ok(o),
14                    Err(_) => QrCode::new(data),
15                },
16            }
17        }
18        else {
19            QrCode::with_version(data, self.qr_version, self.ec_level)
20        }
21    }
22    pub fn render(&self, data: &[u8], img: &DynamicImage) -> QrResult<DynamicImage> {
23        let qr = self.target_qr(data)?;
24        let size = qr.width() as u32;
25        let out = resize(img, 3 * size, 3 * size, FilterType::Triangle);
26        let rgb = unsafe {
27            redraw_locations(
28                &qr,
29                DynamicImage::ImageRgba8(out).into_rgb8(),
30                self.dark_color,
31                self.light_color,
32                self.enhanced,
33                !self.enhanced,
34            )
35        };
36        return Ok(DynamicImage::ImageRgb8(rgb));
37    }
38    pub fn render_frames(&self) {
39        unimplemented!()
40    }
41}
42
43pub unsafe fn get_align_locations(qr: &QrCode) -> Vec<(usize, usize)> {
44    let mut aligns = vec![];
45    match qr.version() {
46        Version::Normal(ver) => {
47            let align_location: &[Vec<usize>; 40] = &[
48                vec![],
49                vec![6, 18],
50                vec![6, 22],
51                vec![6, 26],
52                vec![6, 30],
53                vec![6, 34],
54                vec![6, 22, 38],
55                vec![6, 24, 42],
56                vec![6, 26, 46],
57                vec![6, 28, 50],
58                vec![6, 30, 54],
59                vec![6, 32, 58],
60                vec![6, 34, 62],
61                vec![6, 26, 46, 66],
62                vec![6, 26, 48, 70],
63                vec![6, 26, 50, 74],
64                vec![6, 30, 54, 78],
65                vec![6, 30, 56, 82],
66                vec![6, 30, 58, 86],
67                vec![6, 34, 62, 90],
68                vec![6, 28, 50, 72, 94],
69                vec![6, 26, 50, 74, 98],
70                vec![6, 30, 54, 78, 102],
71                vec![6, 28, 54, 80, 106],
72                vec![6, 32, 58, 84, 110],
73                vec![6, 30, 58, 86, 114],
74                vec![6, 34, 62, 90, 118],
75                vec![6, 26, 50, 74, 98, 122],
76                vec![6, 30, 54, 78, 102, 126],
77                vec![6, 26, 52, 78, 104, 130],
78                vec![6, 30, 56, 82, 108, 134],
79                vec![6, 34, 60, 86, 112, 138],
80                vec![6, 30, 58, 86, 114, 142],
81                vec![6, 34, 62, 90, 118, 146],
82                vec![6, 30, 54, 78, 102, 126, 150],
83                vec![6, 24, 50, 76, 102, 128, 154],
84                vec![6, 28, 54, 80, 106, 132, 158],
85                vec![6, 32, 58, 84, 110, 136, 162],
86                vec![6, 26, 54, 82, 110, 138, 166],
87                vec![6, 30, 58, 86, 114, 142, 170],
88            ];
89            let loc = align_location.get_unchecked(ver as usize - 1);
90            for a in 0..loc.len() {
91                for b in 0..loc.len() {
92                    if !((a == 0 || b == 0) || (a == loc.len() - 1 && b == 0) || (a == 0 && b == loc.len() - 1)) {
93                        for i in (loc.get_unchecked(a) * 3 - 6)..(loc.get_unchecked(a) * 3 + 9) {
94                            for j in (loc.get_unchecked(b) * 3 - 6)..(loc.get_unchecked(b) * 3 + 9) {
95                                aligns.push((i, j))
96                            }
97                        }
98                    }
99                }
100            }
101        }
102        Version::Micro(ver) => {
103            let _ = ver;
104            unimplemented!()
105        }
106    }
107    return aligns;
108}
109
110#[rustfmt::skip]
111pub unsafe fn redraw_locations(qr: &QrCode, bg: RgbImage, dark: Rgb<u8>, light: Rgb<u8>, enhanced: bool, skip_bg: bool) -> RgbImage {
112    let aligns = get_align_locations(qr);
113    let mut qr_img = qr_render_rgb(qr, dark, light);
114    // FIXME:
115    // Too slow, maybe the target image should be modified
116    for i in 0..qr_img.width() - 0 {
117        for j in 0..qr_img.width() - 0 {
118            let _ = skip_bg;
119            if (i < 21 && j < 21)
120            || (i < 21 && j > qr_img.width() - 22)
121            || (i > qr_img.width() - 22 && j < 21)
122            || (enhanced && [18, 19, 20].contains(&i))
123            || (enhanced && [18, 19, 20].contains(&j))
124            || (enhanced && aligns.contains(&(i as usize + 0, j as usize + 0)))
125            || (i % 3 == 1 && j % 3 == 1)
126            //|| (!skip_bg && bg.unsafe_get_pixel(i, j) == dark)
127            {
128                continue;
129            }
130            else {
131                qr_img.unsafe_put_pixel(i, j, bg.unsafe_get_pixel(i, j))
132            }
133        }
134    }
135    return qr_img;
136}
137
138pub fn qr_render_rgb(qr: &QrCode, dark: Rgb<u8>, light: Rgb<u8>) -> RgbImage {
139    qr.render().quiet_zone(false).module_dimensions(3, 3).dark_color(dark).light_color(light).build()
140}