Skip to main content

typf_core/
linra.rs

1//! Linra rendering: shape and render in a single pass
2//!
3//! For maximum performance, some platform APIs can shape AND render text in a
4//! single operation. This module provides the trait and types for such linra
5//! backends.
6//!
7//! ## Why Linra Rendering?
8//!
9//! Traditional pipeline: Shape → Extract Glyphs → Render Each Glyph
10//! Linra pipeline: Shape + Render in One Call
11//!
12//! On macOS, CoreText's CTLineDraw shapes and renders atomically.
13//! On Windows, DirectWrite's DrawTextLayout does the same.
14//!
15//! The linra approach:
16//! - Eliminates glyph extraction overhead
17//! - Allows the OS to optimize internally
18//! - Can leverage hardware acceleration
19
20use std::sync::Arc;
21
22use crate::error::Result;
23use crate::traits::FontRef;
24use crate::types::RenderOutput;
25use crate::Color;
26
27/// Combined parameters for linra shape+render operations
28///
29/// This replaces separate ShapingParams and RenderParams when using
30/// a linra renderer that handles both steps internally.
31#[derive(Debug, Clone)]
32pub struct LinraRenderParams {
33    /// Font size in points
34    pub size: f32,
35    /// Text direction
36    pub direction: crate::types::Direction,
37    /// Text color
38    pub foreground: Color,
39    /// Background color (None = transparent)
40    pub background: Option<Color>,
41    /// Padding around the rendered text
42    pub padding: u32,
43    /// Variable font axis values like [("wght", 700.0), ("wdth", 100.0)]
44    pub variations: Vec<(String, f32)>,
45    /// OpenType feature settings like [("liga", 1), ("kern", 1)]
46    pub features: Vec<(String, u32)>,
47    /// Language code for shaping (e.g., "en", "ar", "zh")
48    pub language: Option<String>,
49    /// Script tag for shaping (e.g., "latn", "arab")
50    pub script: Option<String>,
51    /// Enable antialiasing
52    pub antialias: bool,
53    /// Extra spacing between characters (in points, can be negative)
54    pub letter_spacing: f32,
55    /// CPAL color palette index for COLR color glyphs (0 = default palette)
56    pub color_palette: u16,
57}
58
59impl Default for LinraRenderParams {
60    fn default() -> Self {
61        Self {
62            size: 16.0,
63            direction: crate::types::Direction::LeftToRight,
64            foreground: Color::black(),
65            background: None,
66            padding: 0,
67            variations: Vec::new(),
68            features: Vec::new(),
69            language: None,
70            script: None,
71            antialias: true,
72            letter_spacing: 0.0,
73            color_palette: 0,
74        }
75    }
76}
77
78impl LinraRenderParams {
79    /// Create params with a specific font size
80    pub fn with_size(size: f32) -> Self {
81        Self {
82            size,
83            ..Default::default()
84        }
85    }
86
87    /// Convert to separate ShapingParams for compatibility
88    pub fn to_shaping_params(&self) -> crate::ShapingParams {
89        crate::ShapingParams {
90            size: self.size,
91            direction: self.direction,
92            language: self.language.clone(),
93            script: self.script.clone(),
94            features: self.features.clone(),
95            variations: self.variations.clone(),
96            letter_spacing: self.letter_spacing,
97        }
98    }
99
100    /// Convert to separate RenderParams for compatibility
101    pub fn to_render_params(&self) -> crate::RenderParams {
102        crate::RenderParams {
103            foreground: self.foreground,
104            background: self.background,
105            padding: self.padding,
106            antialias: self.antialias,
107            variations: self.variations.clone(),
108            color_palette: self.color_palette,
109            glyph_sources: crate::GlyphSourcePreference::default(),
110            output: crate::RenderMode::Bitmap,
111        }
112    }
113}
114
115/// Linra text renderer: shapes AND renders in a single operation
116///
117/// Implementations of this trait bypass the separate shaper/renderer pipeline
118/// to achieve maximum performance through platform-native APIs.
119///
120/// ## Platform Implementations
121///
122/// - **macOS**: `CoreTextLinraRenderer` uses CTLineDraw
123/// - **Windows**: `DirectWriteLinraRenderer` uses DrawTextLayout
124///
125/// ## Usage
126///
127/// ```rust,no_run
128/// use typf_core::linra::{LinraRenderer, LinraRenderParams};
129/// use typf_core::traits::FontRef;
130/// use std::sync::Arc;
131///
132/// fn render_text<R: LinraRenderer>(
133///     renderer: &R,
134///     text: &str,
135///     font: Arc<dyn FontRef>,
136/// ) -> typf_core::Result<typf_core::types::RenderOutput> {
137///     let params = LinraRenderParams::with_size(24.0);
138///     renderer.render_text(text, font, &params)
139/// }
140/// ```
141pub trait LinraRenderer: Send + Sync {
142    /// The renderer's name (e.g., "coretext-linra", "directwrite-linra")
143    fn name(&self) -> &'static str;
144
145    /// Shape and render text in a single operation
146    ///
147    /// This method performs both text shaping (character→glyph mapping,
148    /// positioning, feature application) and rendering (rasterization)
149    /// in a single pass through the platform's native text API.
150    ///
151    /// # Arguments
152    ///
153    /// * `text` - The text string to render
154    /// * `font` - Font to use for rendering
155    /// * `params` - Combined shaping and rendering parameters
156    ///
157    /// # Returns
158    ///
159    /// Rendered output as a bitmap or vector format
160    fn render_text(
161        &self,
162        text: &str,
163        font: Arc<dyn FontRef>,
164        params: &LinraRenderParams,
165    ) -> Result<RenderOutput>;
166
167    /// Clear any internal caches
168    fn clear_cache(&self) {}
169
170    /// Check if this renderer supports a given output format
171    fn supports_format(&self, format: &str) -> bool {
172        matches!(format, "bitmap" | "rgba")
173    }
174}
175
176#[cfg(test)]
177mod tests {
178    use super::*;
179
180    #[test]
181    fn test_linra_params_default() {
182        let params = LinraRenderParams::default();
183        assert_eq!(params.size, 16.0);
184        assert!(params.antialias);
185        assert!(params.background.is_none());
186    }
187
188    #[test]
189    fn test_linra_params_with_size() {
190        let params = LinraRenderParams::with_size(24.0);
191        assert_eq!(params.size, 24.0);
192    }
193
194    #[test]
195    fn test_params_conversion() {
196        let linra = LinraRenderParams {
197            size: 32.0,
198            variations: vec![("wght".to_string(), 700.0)],
199            features: vec![("liga".to_string(), 1)],
200            language: Some("en".to_string()),
201            color_palette: 2,
202            ..Default::default()
203        };
204
205        let shaping = linra.to_shaping_params();
206        assert_eq!(shaping.size, 32.0);
207        assert_eq!(shaping.variations.len(), 1);
208        assert_eq!(shaping.features.len(), 1);
209
210        let render = linra.to_render_params();
211        assert_eq!(render.variations.len(), 1);
212        assert_eq!(render.color_palette, 2);
213    }
214}