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, ¶ms)
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}