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
use image::Rgb;
use syntect::highlighting::Style;

/// Determine the foreground pixel color.
#[derive(clap::ValueEnum, Clone, Copy, Debug)]
pub enum FgColor {
    /// Use the style of the syntax to color the foreground pixel.
    Style,
    /// Encode the ascii value into the brightness of the style color
    StyleAsciiBrightness,
}

/// Determine the background pixel color.
#[derive(clap::ValueEnum, Clone, Copy, Debug, Eq, PartialEq)]
pub enum BgColor {
    /// Use the style of the syntax to color the background pixel.
    Style,
    /// Use the style of the syntax to color the background pixel and modulate it in an even-odd pattern
    /// to make file borders visible.
    StyleCheckerboardDarken,
    /// Use the style of the syntax to color the background pixel and modulate it in an even-odd pattern
    /// to make file borders visible.
    StyleCheckerboardBrighten,
    /// The purple color of the Helix Editor.
    HelixEditor,
}

impl BgColor {
    pub fn to_rgb(&self, style: Style, file_index: usize, color_modulation: f32) -> Rgb<u8> {
        match self {
            BgColor::Style => Rgb([style.background.r, style.background.g, style.background.b]),
            BgColor::HelixEditor => Rgb([59, 34, 76]),
            BgColor::StyleCheckerboardDarken | BgColor::StyleCheckerboardBrighten => {
                let m = if self == &BgColor::StyleCheckerboardBrighten {
                    if file_index % 2 == 0 {
                        1.0 + color_modulation
                    } else {
                        1.0
                    }
                } else {
                    (file_index % 2 == 0)
                        .then_some(1.0)
                        .unwrap_or_else(|| (1.0_f32 - color_modulation).max(0.0))
                };
                Rgb([
                    (style.background.r as f32 * m).min(255.0) as u8,
                    (style.background.g as f32 * m).min(255.0) as u8,
                    (style.background.b as f32 * m).min(255.0) as u8,
                ])
            }
        }
    }
}

/// Configure how to render an image.
#[derive(Debug, Copy, Clone)]
pub struct Options<'a> {
    /// How many characters wide each column is.
    pub column_width: u32,
    /// How many pixels high each line is.
    pub line_height: u32,
    /// Whether to render the image in a readable way.
    pub readable: bool,

    /// Whether or not to write the file path and name at the top of each file.
    pub show_filenames: bool,

    pub target_aspect_ratio: f64,

    /// The number of threads to use for rendering.
    pub threads: usize,
    pub highlight_truncated_lines: bool,

    pub fg_color: FgColor,
    pub bg_color: BgColor,
    /// The color theme to use.
    pub theme: &'a str,

    /// Sacrifice aspect ratio to fill the image with full columns.
    pub force_full_columns: bool,
    /// Whether to ignore files without syntactic highlighting.
    pub ignore_files_without_syntax: bool,
    pub plain: bool,
    pub display_to_be_processed_file: bool,
    pub color_modulation: f32,
    /// The number of spaces to use for a tab character.
    pub tab_spaces: u32,
    pub line_nums: bool,
}

impl Default for Options<'_> {
    fn default() -> Self {
        Options {
            column_width: 100,
            line_height: 2,
            readable: false,
            show_filenames: false,
            target_aspect_ratio: 16. / 9.,
            threads: num_cpus::get(),
            highlight_truncated_lines: false,
            fg_color: FgColor::StyleAsciiBrightness,
            bg_color: BgColor::Style,
            theme: "Solarized (dark)",
            force_full_columns: true,
            ignore_files_without_syntax: false,
            plain: false,
            display_to_be_processed_file: false,
            color_modulation: 0.3,
            tab_spaces: 4,
            line_nums: false,
        }
    }
}

mod highlight;
use highlight::Cache;

pub(crate) mod function;

mod chunk;

mod dimension;
use dimension::Dimension;