qrcode2 0.18.0

A QR code encoding library
Documentation
// SPDX-FileCopyrightText: 2014 kennytm
// SPDX-FileCopyrightText: 2018 Ignas Anikevicius
// SPDX-FileCopyrightText: 2023 Nakanishi
// SPDX-FileCopyrightText: 2025 Shun Sakai
//
// SPDX-License-Identifier: Apache-2.0 OR MIT

//! Implementation of features related to the timing patterns.

use super::{Canvas, alignment::ALIGNMENT_PATTERN_POSITIONS};
use crate::types::{Color, Version};

impl Canvas {
    /// Draws a line from (x1, y1) to (x2, y2), inclusively.
    ///
    /// The line must be either horizontal or vertical, i.e. `x1 == x2 || y1 ==
    /// y2`. Additionally, the first coordinates must be less then the second
    /// ones.
    ///
    /// On even coordinates, `color_even` will be plotted; on odd coordinates,
    /// `color_odd` will be plotted instead. Thus the timing pattern can be
    /// drawn using this method.
    fn draw_line(
        &mut self,
        x1: i16,
        y1: i16,
        x2: i16,
        y2: i16,
        color_even: Color,
        color_odd: Color,
    ) {
        debug_assert!(x1 == x2 || y1 == y2);

        if y1 == y2 {
            // Horizontal line.
            for x in x1..=x2 {
                self.put(x, y1, if x % 2 == 0 { color_even } else { color_odd });
            }
        } else {
            // Vertical line.
            for y in y1..=y2 {
                self.put(x1, y, if y % 2 == 0 { color_even } else { color_odd });
            }
        }
    }

    fn draw_rmqr_line(&mut self) {
        let (width, height) = (self.width, self.height);

        // Top.
        self.draw_line(8, 0, width - 3, 0, Color::Dark, Color::Light);

        // Bottom.
        if height == 7 {
            self.draw_line(
                8,
                height - 1,
                width - 6,
                height - 1,
                Color::Dark,
                Color::Light,
            );
        } else {
            self.draw_line(
                3,
                height - 1,
                width - 6,
                height - 1,
                Color::Dark,
                Color::Light,
            );
        }

        // Left.
        if height >= 11 {
            self.draw_line(0, 8, 0, height - 3, Color::Dark, Color::Light);
        }

        // Right.
        if height >= 9 {
            self.draw_line(
                width - 1,
                2,
                width - 1,
                height - 6,
                Color::Dark,
                Color::Light,
            );
        }

        let position_index = self.version.rect_micro_width_index().unwrap() + 34;
        for x in ALIGNMENT_PATTERN_POSITIONS[position_index] {
            self.draw_line(*x, 3, *x, height - 4, Color::Dark, Color::Light);
        }
    }

    /// Draws the timing patterns.
    ///
    /// The timing patterns are checkboard-colored lines near the edge of the QR
    /// code symbol, to establish the fine-grained module coordinates when
    /// scanning.
    pub(super) fn draw_timing_patterns(&mut self) {
        if let Version::RectMicro(..) = self.version {
            self.draw_rmqr_line();
        } else {
            let width = self.width;
            let (y, x1, x2) = match self.version {
                Version::Micro(_) => (0, 8, width - 1),
                Version::Normal(_) => (6, 8, width - 9),
                Version::RectMicro(..) => unreachable!(),
            };
            self.draw_line(x1, y, x2, y, Color::Dark, Color::Light);
            self.draw_line(y, x1, y, x2, Color::Dark, Color::Light);
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::types::EcLevel;

    #[test]
    fn test_draw_timing_patterns_qr() {
        let mut c = Canvas::new(Version::Normal(1), EcLevel::L);
        c.draw_timing_patterns();
        assert_eq!(
            c.to_debug_str(),
            concat!(
                "\n",
                "?????????????????????\n",
                "?????????????????????\n",
                "?????????????????????\n",
                "?????????????????????\n",
                "?????????????????????\n",
                "?????????????????????\n",
                "????????#.#.#????????\n",
                "?????????????????????\n",
                "??????#??????????????\n",
                "??????.??????????????\n",
                "??????#??????????????\n",
                "??????.??????????????\n",
                "??????#??????????????\n",
                "?????????????????????\n",
                "?????????????????????\n",
                "?????????????????????\n",
                "?????????????????????\n",
                "?????????????????????\n",
                "?????????????????????\n",
                "?????????????????????\n",
                "?????????????????????"
            )
        );
    }

    #[test]
    fn test_draw_timing_patterns_micro_qr() {
        let mut c = Canvas::new(Version::Micro(1), EcLevel::L);
        c.draw_timing_patterns();
        assert_eq!(
            c.to_debug_str(),
            concat!(
                "\n",
                "????????#.#\n",
                "???????????\n",
                "???????????\n",
                "???????????\n",
                "???????????\n",
                "???????????\n",
                "???????????\n",
                "???????????\n",
                "#??????????\n",
                ".??????????\n",
                "#??????????"
            )
        );
    }

    #[test]
    fn test_draw_timing_patterns_rmqr_7x77() {
        let mut c = Canvas::new(Version::RectMicro(7, 77), EcLevel::L);
        c.draw_timing_patterns();
        assert_eq!(
            c.to_debug_str(),
            concat!(
                "\n",
                "????????#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#??\n",
                "?????????????????????????????????????????????????????????????????????????????\n",
                "?????????????????????????????????????????????????????????????????????????????\n",
                "?????????????????????????.?????????????????????????.?????????????????????????\n",
                "?????????????????????????????????????????????????????????????????????????????\n",
                "?????????????????????????????????????????????????????????????????????????????\n",
                "????????#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.?????"
            )
        );
    }

    #[test]
    fn test_draw_timing_patterns_rmqr_9x77() {
        let mut c = Canvas::new(Version::RectMicro(9, 77), EcLevel::L);
        c.draw_timing_patterns();
        assert_eq!(
            c.to_debug_str(),
            concat!(
                "\n",
                "????????#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#??\n",
                "?????????????????????????????????????????????????????????????????????????????\n",
                "????????????????????????????????????????????????????????????????????????????#\n",
                "?????????????????????????.?????????????????????????.????????????????????????.\n",
                "?????????????????????????#?????????????????????????#?????????????????????????\n",
                "?????????????????????????.?????????????????????????.?????????????????????????\n",
                "?????????????????????????????????????????????????????????????????????????????\n",
                "?????????????????????????????????????????????????????????????????????????????\n",
                "???.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.?????"
            )
        );
    }

    #[test]
    fn test_draw_timing_patterns_rmqr_11x77() {
        let mut c = Canvas::new(Version::RectMicro(11, 77), EcLevel::L);
        c.draw_timing_patterns();
        assert_eq!(
            c.to_debug_str(),
            concat!(
                "\n",
                "????????#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#??\n",
                "?????????????????????????????????????????????????????????????????????????????\n",
                "????????????????????????????????????????????????????????????????????????????#\n",
                "?????????????????????????.?????????????????????????.????????????????????????.\n",
                "?????????????????????????#?????????????????????????#????????????????????????#\n",
                "?????????????????????????.?????????????????????????.????????????????????????.\n",
                "?????????????????????????#?????????????????????????#?????????????????????????\n",
                "?????????????????????????.?????????????????????????.?????????????????????????\n",
                "#????????????????????????????????????????????????????????????????????????????\n",
                "?????????????????????????????????????????????????????????????????????????????\n",
                "???.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.?????"
            )
        );
    }
}