1
use super::f26dot6::{F2Dot14, F26Dot6};
2

            
3
/// Rounding mode for the TrueType interpreter.
4
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
5
pub enum RoundState {
6
    /// Round to half grid (0.5 pixel boundaries).
7
    HalfGrid,
8
    /// Round to grid (integer pixel boundaries).
9
    Grid,
10
    /// Round to double grid (0.5 pixel boundaries, same as half grid but different phase).
11
    DoubleGrid,
12
    /// Round down to grid.
13
    DownToGrid,
14
    /// Round up to grid.
15
    UpToGrid,
16
    /// No rounding.
17
    Off,
18
    /// Super rounding (configurable period, phase, threshold).
19
    Super,
20
    /// Super 45-degree rounding.
21
    Super45,
22
}
23

            
24
/// Graphics state for the TrueType bytecode interpreter.
25
///
26
/// Contains all ~30 state variables that control how instructions behave.
27
/// Reset to defaults before each glyph program (but prep can modify defaults).
28
#[derive(Clone, Debug)]
29
pub struct GraphicsState {
30
    /// Freedom vector: direction along which points are moved.
31
    pub freedom_vector: (F2Dot14, F2Dot14),
32
    /// Projection vector: direction along which distances are measured.
33
    pub projection_vector: (F2Dot14, F2Dot14),
34
    /// Dual projection vector: used for measuring original outline distances.
35
    pub dual_projection_vector: (F2Dot14, F2Dot14),
36
    /// Reference point 0.
37
    pub rp0: u32,
38
    /// Reference point 1.
39
    pub rp1: u32,
40
    /// Reference point 2.
41
    pub rp2: u32,
42
    /// Zone pointer 0 (0 = twilight, 1 = glyph).
43
    pub zp0: u32,
44
    /// Zone pointer 1.
45
    pub zp1: u32,
46
    /// Zone pointer 2.
47
    pub zp2: u32,
48
    /// Loop variable: how many times certain instructions repeat.
49
    pub loop_value: u32,
50
    /// Rounding mode.
51
    pub round_state: RoundState,
52
    /// Minimum distance (F26Dot6): smallest distance after rounding.
53
    pub minimum_distance: F26Dot6,
54
    /// Control value cut-in (F26Dot6): threshold for using CVT vs actual distance.
55
    pub control_value_cut_in: F26Dot6,
56
    /// Single width cut-in (F26Dot6).
57
    pub single_width_cut_in: F26Dot6,
58
    /// Single width value (F26Dot6).
59
    pub single_width_value: F26Dot6,
60
    /// Auto flip: whether MIRP auto-corrects direction.
61
    pub auto_flip: bool,
62
    /// Delta base: ppem value at which DELTA instructions start.
63
    pub delta_base: u16,
64
    /// Delta shift: number of bits to shift DELTA arguments.
65
    pub delta_shift: u16,
66
    /// Instruction control flags.
67
    pub instruct_control: u8,
68
    /// Scan control flag.
69
    pub scan_control: u32,
70
    /// Scan type.
71
    pub scan_type: i32,
72

            
73
    // Super rounding parameters (used when round_state is Super or Super45)
74
    /// Super round period (F26Dot6).
75
    pub super_round_period: F26Dot6,
76
    /// Super round phase (F26Dot6).
77
    pub super_round_phase: F26Dot6,
78
    /// Super round threshold (F26Dot6).
79
    pub super_round_threshold: F26Dot6,
80
}
81

            
82
impl Default for GraphicsState {
83
65610
    fn default() -> Self {
84
65610
        GraphicsState {
85
65610
            // Default vectors along x-axis
86
65610
            freedom_vector: (F2Dot14::ONE, F2Dot14::ZERO),
87
65610
            projection_vector: (F2Dot14::ONE, F2Dot14::ZERO),
88
65610
            dual_projection_vector: (F2Dot14::ONE, F2Dot14::ZERO),
89
65610
            rp0: 0,
90
65610
            rp1: 0,
91
65610
            rp2: 0,
92
65610
            zp0: 1, // glyph zone
93
65610
            zp1: 1,
94
65610
            zp2: 1,
95
65610
            loop_value: 1,
96
65610
            round_state: RoundState::Grid,
97
65610
            minimum_distance: F26Dot6::ONE,        // 1 pixel
98
65610
            control_value_cut_in: F26Dot6(68),     // 17/16 pixel = 68/64
99
65610
            single_width_cut_in: F26Dot6::ZERO,
100
65610
            single_width_value: F26Dot6::ZERO,
101
65610
            auto_flip: true,
102
65610
            delta_base: 9,
103
65610
            delta_shift: 3,
104
65610
            instruct_control: 0,
105
65610
            scan_control: 0,
106
65610
            scan_type: 0,
107
65610
            super_round_period: F26Dot6(64), // 1 pixel
108
65610
            super_round_phase: F26Dot6::ZERO,
109
65610
            super_round_threshold: F26Dot6(32), // 0.5 pixel (half of period)
110
65610
        }
111
65610
    }
112
}
113

            
114
impl GraphicsState {
115
    /// Apply rounding according to the current round_state.
116
648
    pub fn round(&self, distance: F26Dot6) -> F26Dot6 {
117
648
        let sign = if distance.0 >= 0 { 1i32 } else { -1i32 };
118
648
        let val = distance.abs();
119

            
120
648
        let result = match self.round_state {
121
            RoundState::Off => return distance,
122
648
            RoundState::Grid => val.round(),
123
            RoundState::HalfGrid => {
124
                // Round to nearest half pixel (n + 0.5)
125
                let floored = val.floor();
126
                F26Dot6(floored.0 + 32)
127
            }
128
            RoundState::DoubleGrid => {
129
                // Round to nearest half pixel
130
                F26Dot6((val.0 + 16) & !31)
131
            }
132
            RoundState::DownToGrid => val.floor(),
133
            RoundState::UpToGrid => {
134
                if val.0 & 63 == 0 {
135
                    val
136
                } else {
137
                    val.ceil()
138
                }
139
            }
140
            RoundState::Super | RoundState::Super45 => {
141
                self.super_round(val)
142
            }
143
        };
144

            
145
        // Ensure minimum distance of 0 after rounding (result is non-negative)
146
648
        let result = if result.0 < 0 { F26Dot6::ZERO } else { result };
147

            
148
648
        F26Dot6(result.0 * sign)
149
648
    }
150

            
151
    fn super_round(&self, val: F26Dot6) -> F26Dot6 {
152
        let period = self.super_round_period;
153
        let phase = self.super_round_phase;
154
        let threshold = self.super_round_threshold;
155

            
156
        if period.0 == 0 {
157
            return val;
158
        }
159

            
160
        let val_minus_phase = F26Dot6(val.0 - phase.0);
161

            
162
        let rounded = if val_minus_phase.0 >= 0 {
163
            let n = (val_minus_phase.0 + threshold.0) / period.0;
164
            F26Dot6(n * period.0 + phase.0)
165
        } else {
166
            let n = -((-val_minus_phase.0 + threshold.0) / period.0);
167
            F26Dot6(n * period.0 + phase.0)
168
        };
169

            
170
        if rounded.0 < phase.0 {
171
            F26Dot6(phase.0)
172
        } else {
173
            rounded
174
        }
175
    }
176

            
177
    /// Set the super rounding parameters from an opcode argument.
178
    ///
179
    /// `is_45` selects Super45 mode (period is sqrt(2)/2 instead of 1).
180
    pub fn set_super_round(&mut self, n: u32, is_45: bool) {
181
        // Period (bits 7-6)
182
        let period_bits = (n >> 6) & 0x03;
183
        self.super_round_period = match period_bits {
184
            0 => F26Dot6(32),  // 1/2 pixel
185
            1 => F26Dot6(64),  // 1 pixel
186
            2 => F26Dot6(128), // 2 pixels
187
            _ => F26Dot6(64),  // reserved, default to 1 pixel
188
        };
189

            
190
        if is_45 {
191
            // For 45-degree rounding, multiply period by sqrt(2)/2 ≈ 0.7071
192
            // In F26Dot6: period * 46 / 64 (approximation)
193
            self.super_round_period = F26Dot6(
194
                (self.super_round_period.0 as i64 * 46 / 64) as i32,
195
            );
196
        }
197

            
198
        // Phase (bits 5-4): derived from the actual period (period*{0,1/4,1/2,3/4}),
199
        // NOT hardcoded 16/32/48 which is only correct when period == 64.
200
        let period = self.super_round_period.0;
201
        let phase_bits = (n >> 4) & 0x03;
202
        self.super_round_phase = match phase_bits {
203
            0 => F26Dot6::ZERO,
204
            1 => F26Dot6(period / 4),
205
            2 => F26Dot6(period / 2),
206
            3 => F26Dot6(period * 3 / 4),
207
            _ => unreachable!(),
208
        };
209

            
210
        // Threshold (bits 3-0)
211
        let threshold_bits = n & 0x0F;
212
        self.super_round_threshold = if threshold_bits == 0 {
213
            F26Dot6(self.super_round_period.0 - 1)
214
        } else {
215
            F26Dot6((threshold_bits as i32 - 4) * self.super_round_period.0 / 8)
216
        };
217
    }
218
}