1 | use crate::{ |
2 | geometry::Position, |
3 | paint::{GlyphTexture, GradientColors}, |
4 | ImageFlags, ImageStore, PaintFlavor, PixelFormat, Scissor, Transform2D, |
5 | }; |
6 | |
7 | use super::ShaderType; |
8 | |
9 | #[derive (Copy, Clone, Debug, Default)] |
10 | pub struct Params { |
11 | pub(crate) scissor_mat: [f32; 12], |
12 | pub(crate) paint_mat: [f32; 12], |
13 | pub(crate) inner_col: [f32; 4], |
14 | pub(crate) outer_col: [f32; 4], |
15 | pub(crate) scissor_ext: [f32; 2], |
16 | pub(crate) scissor_scale: [f32; 2], |
17 | pub(crate) extent: [f32; 2], |
18 | pub(crate) radius: f32, |
19 | pub(crate) feather: f32, |
20 | pub(crate) stroke_mult: f32, |
21 | pub(crate) stroke_thr: f32, |
22 | pub(crate) tex_type: f32, |
23 | pub(crate) shader_type: ShaderType, |
24 | pub(crate) glyph_texture_type: u8, // 0 -> no glyph rendering, 1 -> alpha mask, 2 -> color texture |
25 | pub(crate) image_blur_filter_direction: [f32; 2], |
26 | pub(crate) image_blur_filter_sigma: f32, |
27 | pub(crate) image_blur_filter_coeff: [f32; 3], |
28 | } |
29 | |
30 | impl Params { |
31 | pub(crate) fn new<T>( |
32 | images: &ImageStore<T>, |
33 | global_transform: &Transform2D, |
34 | paint_flavor: &PaintFlavor, |
35 | glyph_texture: &GlyphTexture, |
36 | scissor: &Scissor, |
37 | stroke_width: f32, |
38 | fringe_width: f32, |
39 | stroke_thr: f32, |
40 | ) -> Self { |
41 | let mut params = Self::default(); |
42 | |
43 | // Scissor |
44 | let (scissor_ext, scissor_scale) = if let Some(ext) = scissor.extent { |
45 | if ext[0] < -0.5 || ext[1] < -0.5 { |
46 | ([1.0, 1.0], [1.0, 1.0]) |
47 | } else { |
48 | params.scissor_mat = scissor.transform.inverse().to_mat3x4(); |
49 | |
50 | let Transform2D([a, b, c, d, ..]) = scissor.transform; |
51 | let scissor_scale = [a.hypot(c) / fringe_width, b.hypot(d) / fringe_width]; |
52 | |
53 | (ext, scissor_scale) |
54 | } |
55 | } else { |
56 | ([1.0, 1.0], [1.0, 1.0]) |
57 | }; |
58 | |
59 | params.scissor_ext = scissor_ext; |
60 | params.scissor_scale = scissor_scale; |
61 | |
62 | params.stroke_mult = (stroke_width * 0.5 + fringe_width * 0.5) / fringe_width; |
63 | params.stroke_thr = stroke_thr; |
64 | |
65 | params.glyph_texture_type = match glyph_texture { |
66 | GlyphTexture::None => 0, |
67 | GlyphTexture::AlphaMask(_) => 1, |
68 | GlyphTexture::ColorTexture(_) => 2, |
69 | }; |
70 | |
71 | let inv_transform; |
72 | |
73 | match &paint_flavor { |
74 | PaintFlavor::Color(color) => { |
75 | let color = color.premultiplied().to_array(); |
76 | params.inner_col = color; |
77 | params.outer_col = color; |
78 | params.shader_type = ShaderType::FillColor; |
79 | inv_transform = global_transform.inverse(); |
80 | } |
81 | &PaintFlavor::Image { |
82 | id, |
83 | center: Position { x: cx, y: cy }, |
84 | width, |
85 | height, |
86 | angle, |
87 | tint, |
88 | } => { |
89 | let Some(image_info) = images.info(*id) else { |
90 | return params; |
91 | }; |
92 | |
93 | params.extent[0] = *width; |
94 | params.extent[1] = *height; |
95 | |
96 | let color = tint; |
97 | |
98 | params.inner_col = color.premultiplied().to_array(); |
99 | params.outer_col = color.premultiplied().to_array(); |
100 | |
101 | let mut transform = Transform2D::rotation(*angle); |
102 | transform.translate(*cx, *cy); |
103 | transform *= *global_transform; |
104 | |
105 | if image_info.flags().contains(ImageFlags::FLIP_Y) { |
106 | let mut m1 = Transform2D::translation(0.0, height * 0.5); |
107 | m1 *= transform; |
108 | |
109 | let mut m2 = Transform2D::scaling(1.0, -1.0); |
110 | m2 *= m1; |
111 | |
112 | let mut m1 = Transform2D::translation(0.0, -height * 0.5); |
113 | m1 *= m2; |
114 | |
115 | inv_transform = m1.inverse(); |
116 | } else { |
117 | inv_transform = transform.inverse(); |
118 | } |
119 | |
120 | params.shader_type = ShaderType::FillImage; |
121 | |
122 | params.tex_type = match image_info.format() { |
123 | PixelFormat::Rgba8 => { |
124 | if image_info.flags().contains(ImageFlags::PREMULTIPLIED) { |
125 | 0.0 |
126 | } else { |
127 | 1.0 |
128 | } |
129 | } |
130 | PixelFormat::Gray8 => 2.0, |
131 | PixelFormat::Rgb8 => 0.0, |
132 | }; |
133 | } |
134 | PaintFlavor::LinearGradient { |
135 | start: Position { x: start_x, y: start_y }, |
136 | end: Position { x: end_x, y: end_y }, |
137 | colors, |
138 | } => { |
139 | let large = 1e5f32; |
140 | let mut dx = end_x - start_x; |
141 | let mut dy = end_y - start_y; |
142 | let d = dx.hypot(dy); |
143 | |
144 | if d > 0.0001 { |
145 | dx /= d; |
146 | dy /= d; |
147 | } else { |
148 | dx = 0.0; |
149 | dy = 1.0; |
150 | } |
151 | |
152 | let mut transform = Transform2D([dy, -dx, dx, dy, start_x - dx * large, start_y - dy * large]); |
153 | |
154 | transform *= *global_transform; |
155 | |
156 | inv_transform = transform.inverse(); |
157 | |
158 | params.extent[0] = large; |
159 | params.extent[1] = large + d * 0.5; |
160 | params.feather = 1.0f32.max(d); |
161 | |
162 | match colors { |
163 | GradientColors::TwoStop { start_color, end_color } => { |
164 | params.inner_col = start_color.premultiplied().to_array(); |
165 | params.outer_col = end_color.premultiplied().to_array(); |
166 | params.shader_type = ShaderType::FillGradient; |
167 | } |
168 | GradientColors::MultiStop { .. } => { |
169 | params.shader_type = ShaderType::FillImageGradient; |
170 | } |
171 | } |
172 | } |
173 | &PaintFlavor::BoxGradient { |
174 | pos: Position { x, y }, |
175 | width, |
176 | height, |
177 | radius, |
178 | feather, |
179 | colors, |
180 | } => { |
181 | let mut transform = Transform2D::translation(x + width * 0.5, y + height * 0.5); |
182 | transform *= *global_transform; |
183 | inv_transform = transform.inverse(); |
184 | |
185 | params.extent[0] = width * 0.5; |
186 | params.extent[1] = height * 0.5; |
187 | params.radius = *radius; |
188 | params.feather = *feather; |
189 | match colors { |
190 | GradientColors::TwoStop { start_color, end_color } => { |
191 | params.inner_col = start_color.premultiplied().to_array(); |
192 | params.outer_col = end_color.premultiplied().to_array(); |
193 | params.shader_type = ShaderType::FillGradient; |
194 | } |
195 | GradientColors::MultiStop { .. } => { |
196 | params.shader_type = ShaderType::FillImageGradient; |
197 | } |
198 | } |
199 | } |
200 | &PaintFlavor::RadialGradient { |
201 | center: Position { x: cx, y: cy }, |
202 | in_radius, |
203 | out_radius, |
204 | colors, |
205 | } => { |
206 | let r = (in_radius + out_radius) * 0.5; |
207 | let f = out_radius - in_radius; |
208 | |
209 | let mut transform = Transform2D::translation(*cx, *cy); |
210 | transform *= *global_transform; |
211 | inv_transform = transform.inverse(); |
212 | |
213 | params.extent[0] = r; |
214 | params.extent[1] = r; |
215 | params.radius = r; |
216 | params.feather = 1.0f32.max(f); |
217 | match colors { |
218 | GradientColors::TwoStop { start_color, end_color } => { |
219 | params.inner_col = start_color.premultiplied().to_array(); |
220 | params.outer_col = end_color.premultiplied().to_array(); |
221 | params.shader_type = ShaderType::FillGradient; |
222 | } |
223 | GradientColors::MultiStop { .. } => { |
224 | params.shader_type = ShaderType::FillImageGradient; |
225 | } |
226 | } |
227 | } |
228 | } |
229 | |
230 | params.paint_mat = inv_transform.to_mat3x4(); |
231 | |
232 | params |
233 | } |
234 | |
235 | pub(crate) fn uses_glyph_texture(self) -> bool { |
236 | self.glyph_texture_type != 0 |
237 | } |
238 | } |
239 | |