Skip to main content

ass_renderer/debug/
color_diagnostic.rs

1//! Color diagnostic tool for debugging rendering issues
2
3use ass_core::parser::ast::{Section, SectionType};
4use ass_core::Script;
5
6/// Diagnose color issues in ASS scripts
7pub struct ColorDiagnostic;
8
9impl ColorDiagnostic {
10    /// Analyze colors in a script and report potential issues
11    pub fn analyze_script(script: &Script) -> ColorReport {
12        let mut report = ColorReport::default();
13
14        // Check styles section
15        if let Some(Section::Styles(styles)) = script.find_section(SectionType::Styles) {
16            for style in styles {
17                let name = style.name;
18                let primary = style.primary_colour;
19                let secondary = style.secondary_colour;
20                let outline = style.outline_colour;
21                let back = style.back_colour;
22
23                report.styles.push(StyleColors {
24                    name: name.to_string(),
25                    primary_raw: primary.to_string(),
26                    primary_parsed: parse_color_debug(primary),
27                    secondary_raw: secondary.to_string(),
28                    outline_raw: outline.to_string(),
29                    back_raw: back.to_string(),
30                });
31            }
32        }
33
34        // Check events for color overrides
35        if let Some(Section::Events(events)) = script.find_section(SectionType::Events) {
36            for event in events {
37                if event.text.contains("\\c") || event.text.contains("\\1c") {
38                    report.has_color_overrides = true;
39                }
40            }
41        }
42
43        report
44    }
45
46    /// Create a test script with white text
47    pub fn create_white_text_test() -> String {
48        r#"[Script Info]
49Title: White Text Test
50PlayResX: 640
51PlayResY: 480
52
53[V4+ Styles]
54Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
55Style: WhiteText,Arial,50,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,0,0,0,0,100,100,0,0,1,2,0,2,10,10,10,1
56
57[Events]
58Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
59Dialogue: 0,0:00:00.00,0:00:05.00,WhiteText,,0,0,0,,This should be WHITE text"#.to_string()
60    }
61
62    /// Create a color reference test with multiple colors
63    pub fn create_color_reference_test() -> String {
64        r#"[Script Info]
65Title: Color Reference Test
66PlayResX: 640
67PlayResY: 480
68
69[V4+ Styles]
70Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
71Style: White,Arial,40,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,-1,0,0,0,100,100,0,0,1,2,0,2,10,10,10,1
72Style: Red,Arial,40,&H000000FF,&H000000FF,&H00000000,&H00000000,-1,0,0,0,100,100,0,0,1,2,0,2,10,10,10,1
73Style: Green,Arial,40,&H0000FF00,&H000000FF,&H00000000,&H00000000,-1,0,0,0,100,100,0,0,1,2,0,2,10,10,10,1
74Style: Blue,Arial,40,&H00FF0000,&H000000FF,&H00000000,&H00000000,-1,0,0,0,100,100,0,0,1,2,0,2,10,10,10,1
75Style: Yellow,Arial,40,&H0000FFFF,&H000000FF,&H00000000,&H00000000,-1,0,0,0,100,100,0,0,1,2,0,2,10,10,10,1
76Style: Cyan,Arial,40,&H00FFFF00,&H000000FF,&H00000000,&H00000000,-1,0,0,0,100,100,0,0,1,2,0,2,10,10,10,1
77Style: Magenta,Arial,40,&H00FF00FF,&H000000FF,&H00000000,&H00000000,-1,0,0,0,100,100,0,0,1,2,0,2,10,10,10,1
78
79[Events]
80Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
81Dialogue: 0,0:00:00.00,0:00:10.00,White,,0,0,0,,WHITE
82Dialogue: 0,0:00:00.00,0:00:10.00,Red,,0,0,60,,RED
83Dialogue: 0,0:00:00.00,0:00:10.00,Green,,0,0,120,,GREEN
84Dialogue: 0,0:00:00.00,0:00:10.00,Blue,,0,0,180,,BLUE
85Dialogue: 0,0:00:00.00,0:00:10.00,Yellow,,0,0,240,,YELLOW
86Dialogue: 0,0:00:00.00,0:00:10.00,Cyan,,0,0,300,,CYAN
87Dialogue: 0,0:00:00.00,0:00:10.00,Magenta,,0,0,360,,MAGENTA"#.to_string()
88    }
89}
90
91#[derive(Debug, Default)]
92pub struct ColorReport {
93    pub styles: Vec<StyleColors>,
94    pub has_color_overrides: bool,
95}
96
97#[derive(Debug)]
98pub struct StyleColors {
99    pub name: String,
100    pub primary_raw: String,
101    pub primary_parsed: ColorDebugInfo,
102    pub secondary_raw: String,
103    pub outline_raw: String,
104    pub back_raw: String,
105}
106
107#[derive(Debug)]
108pub struct ColorDebugInfo {
109    pub hex_value: String,
110    pub has_alpha: bool,
111    pub alpha: u8,
112    pub red: u8,
113    pub green: u8,
114    pub blue: u8,
115    pub expected_color: String,
116}
117
118fn parse_color_debug(color: &str) -> ColorDebugInfo {
119    let color_trimmed = color.trim_end_matches('&');
120
121    if let Some(hex) = color_trimmed.strip_prefix("&H") {
122        let has_alpha = hex.len() >= 8;
123
124        if let Ok(value) = u32::from_str_radix(hex, 16) {
125            let (alpha, bgr_value) = if has_alpha {
126                let alpha = ((value >> 24) & 0xFF) as u8;
127                (alpha, value & 0xFFFFFF)
128            } else {
129                (0x00, value) // Default to opaque
130            };
131
132            let r = (bgr_value & 0xFF) as u8;
133            let g = ((bgr_value >> 8) & 0xFF) as u8;
134            let b = ((bgr_value >> 16) & 0xFF) as u8;
135
136            let expected = match (r, g, b) {
137                (255, 255, 255) => "WHITE",
138                (255, 0, 0) => "RED",
139                (0, 255, 0) => "GREEN",
140                (0, 0, 255) => "BLUE",
141                (255, 255, 0) => "YELLOW",
142                (0, 255, 255) => "CYAN",
143                (255, 0, 255) => "MAGENTA",
144                (0, 0, 0) => "BLACK",
145                _ => "CUSTOM",
146            };
147
148            return ColorDebugInfo {
149                hex_value: hex.to_string(),
150                has_alpha,
151                alpha,
152                red: r,
153                green: g,
154                blue: b,
155                expected_color: expected.to_string(),
156            };
157        }
158    }
159
160    ColorDebugInfo {
161        hex_value: "INVALID".to_string(),
162        has_alpha: false,
163        alpha: 0,
164        red: 0,
165        green: 0,
166        blue: 0,
167        expected_color: "ERROR".to_string(),
168    }
169}
170
171impl ColorReport {
172    pub fn print_diagnostic(&self) {
173        println!("=== ASS Color Diagnostic Report ===\n");
174
175        for style in &self.styles {
176            println!("Style: {}", style.name);
177            println!("  Primary Color: {}", style.primary_raw);
178            println!("    Hex: {}", style.primary_parsed.hex_value);
179            println!(
180                "    RGBA: ({}, {}, {}, {})",
181                style.primary_parsed.red,
182                style.primary_parsed.green,
183                style.primary_parsed.blue,
184                if style.primary_parsed.has_alpha {
185                    format!("{}", 255 - style.primary_parsed.alpha) // Convert ASS to RGBA alpha
186                } else {
187                    "255".to_string()
188                }
189            );
190            println!("    Expected: {}", style.primary_parsed.expected_color);
191
192            // Diagnostic for common issues
193            if style.primary_parsed.expected_color == "YELLOW" && style.name.contains("White") {
194                println!(
195                    "    ⚠️  WARNING: Style named '{}' is YELLOW but should probably be WHITE!",
196                    style.name
197                );
198                println!("       Correct white: &H00FFFFFF");
199                println!("       Current value might be: &H0000FFFF (yellow)");
200            }
201            println!();
202        }
203
204        if self.has_color_overrides {
205            println!("Note: Script contains color override tags (\\c or \\1c) in events");
206        }
207    }
208}