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 = Params::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.inversed().to_mat3x4(); |
49 | |
50 | let scissor_scale = [ |
51 | (scissor.transform[0] * scissor.transform[0] + scissor.transform[2] * scissor.transform[2]).sqrt() |
52 | / fringe_width, |
53 | (scissor.transform[1] * scissor.transform[1] + scissor.transform[3] * scissor.transform[3]).sqrt() |
54 | / fringe_width, |
55 | ]; |
56 | |
57 | (ext, scissor_scale) |
58 | } |
59 | } else { |
60 | ([1.0, 1.0], [1.0, 1.0]) |
61 | }; |
62 | |
63 | params.scissor_ext = scissor_ext; |
64 | params.scissor_scale = scissor_scale; |
65 | |
66 | params.stroke_mult = (stroke_width * 0.5 + fringe_width * 0.5) / fringe_width; |
67 | params.stroke_thr = stroke_thr; |
68 | |
69 | params.glyph_texture_type = match glyph_texture { |
70 | GlyphTexture::None => 0, |
71 | GlyphTexture::AlphaMask(_) => 1, |
72 | GlyphTexture::ColorTexture(_) => 2, |
73 | }; |
74 | |
75 | let inv_transform; |
76 | |
77 | match &paint_flavor { |
78 | PaintFlavor::Color(color) => { |
79 | let color = color.premultiplied().to_array(); |
80 | params.inner_col = color; |
81 | params.outer_col = color; |
82 | params.shader_type = ShaderType::FillColor; |
83 | inv_transform = global_transform.inversed(); |
84 | } |
85 | &PaintFlavor::Image { |
86 | id, |
87 | center: Position { x: cx, y: cy }, |
88 | width, |
89 | height, |
90 | angle, |
91 | tint, |
92 | } => { |
93 | let image_info = match images.info(*id) { |
94 | Some(info) => info, |
95 | None => return params, |
96 | }; |
97 | |
98 | params.extent[0] = *width; |
99 | params.extent[1] = *height; |
100 | |
101 | let color = tint; |
102 | |
103 | params.inner_col = color.premultiplied().to_array(); |
104 | params.outer_col = color.premultiplied().to_array(); |
105 | |
106 | let mut transform = Transform2D::identity(); |
107 | transform.rotate(*angle); |
108 | transform.translate(*cx, *cy); |
109 | transform.multiply(global_transform); |
110 | |
111 | if image_info.flags().contains(ImageFlags::FLIP_Y) { |
112 | let mut m1 = Transform2D::identity(); |
113 | m1.translate(0.0, height * 0.5); |
114 | m1.multiply(&transform); |
115 | |
116 | let mut m2 = Transform2D::identity(); |
117 | m2.scale(1.0, -1.0); |
118 | m2.multiply(&m1); |
119 | |
120 | let mut m1 = Transform2D::identity(); |
121 | m1.translate(0.0, -height * 0.5); |
122 | m1.multiply(&m2); |
123 | |
124 | inv_transform = m1.inversed(); |
125 | } else { |
126 | inv_transform = transform.inversed(); |
127 | } |
128 | |
129 | params.shader_type = ShaderType::FillImage; |
130 | |
131 | params.tex_type = match image_info.format() { |
132 | PixelFormat::Rgba8 => { |
133 | if image_info.flags().contains(ImageFlags::PREMULTIPLIED) { |
134 | 0.0 |
135 | } else { |
136 | 1.0 |
137 | } |
138 | } |
139 | PixelFormat::Gray8 => 2.0, |
140 | _ => 0.0, |
141 | }; |
142 | } |
143 | PaintFlavor::LinearGradient { |
144 | start: Position { x: start_x, y: start_y }, |
145 | end: Position { x: end_x, y: end_y }, |
146 | colors, |
147 | } => { |
148 | let large = 1e5f32; |
149 | let mut dx = end_x - start_x; |
150 | let mut dy = end_y - start_y; |
151 | let d = (dx * dx + dy * dy).sqrt(); |
152 | |
153 | if d > 0.0001 { |
154 | dx /= d; |
155 | dy /= d; |
156 | } else { |
157 | dx = 0.0; |
158 | dy = 1.0; |
159 | } |
160 | |
161 | let mut transform = Transform2D([dy, -dx, dx, dy, start_x - dx * large, start_y - dy * large]); |
162 | |
163 | transform.multiply(global_transform); |
164 | |
165 | inv_transform = transform.inversed(); |
166 | |
167 | params.extent[0] = large; |
168 | params.extent[1] = large + d * 0.5; |
169 | params.feather = 1.0f32.max(d); |
170 | |
171 | match colors { |
172 | GradientColors::TwoStop { start_color, end_color } => { |
173 | params.inner_col = start_color.premultiplied().to_array(); |
174 | params.outer_col = end_color.premultiplied().to_array(); |
175 | params.shader_type = ShaderType::FillGradient; |
176 | } |
177 | GradientColors::MultiStop { .. } => { |
178 | params.shader_type = ShaderType::FillImageGradient; |
179 | } |
180 | } |
181 | } |
182 | &PaintFlavor::BoxGradient { |
183 | pos: Position { x, y }, |
184 | width, |
185 | height, |
186 | radius, |
187 | feather, |
188 | colors, |
189 | } => { |
190 | let mut transform = Transform2D::new_translation(x + width * 0.5, y + height * 0.5); |
191 | transform.multiply(global_transform); |
192 | inv_transform = transform.inversed(); |
193 | |
194 | params.extent[0] = width * 0.5; |
195 | params.extent[1] = height * 0.5; |
196 | params.radius = *radius; |
197 | params.feather = *feather; |
198 | match colors { |
199 | GradientColors::TwoStop { start_color, end_color } => { |
200 | params.inner_col = start_color.premultiplied().to_array(); |
201 | params.outer_col = end_color.premultiplied().to_array(); |
202 | params.shader_type = ShaderType::FillGradient; |
203 | } |
204 | GradientColors::MultiStop { .. } => { |
205 | params.shader_type = ShaderType::FillImageGradient; |
206 | } |
207 | } |
208 | } |
209 | &PaintFlavor::RadialGradient { |
210 | center: Position { x: cx, y: cy }, |
211 | in_radius, |
212 | out_radius, |
213 | colors, |
214 | } => { |
215 | let r = (in_radius + out_radius) * 0.5; |
216 | let f = out_radius - in_radius; |
217 | |
218 | let mut transform = Transform2D::new_translation(*cx, *cy); |
219 | transform.multiply(global_transform); |
220 | inv_transform = transform.inversed(); |
221 | |
222 | params.extent[0] = r; |
223 | params.extent[1] = r; |
224 | params.radius = r; |
225 | params.feather = 1.0f32.max(f); |
226 | match colors { |
227 | GradientColors::TwoStop { start_color, end_color } => { |
228 | params.inner_col = start_color.premultiplied().to_array(); |
229 | params.outer_col = end_color.premultiplied().to_array(); |
230 | params.shader_type = ShaderType::FillGradient; |
231 | } |
232 | GradientColors::MultiStop { .. } => { |
233 | params.shader_type = ShaderType::FillImageGradient; |
234 | } |
235 | } |
236 | } |
237 | } |
238 | |
239 | params.paint_mat = inv_transform.to_mat3x4(); |
240 | |
241 | params |
242 | } |
243 | |
244 | pub(crate) fn uses_glyph_texture(self) -> bool { |
245 | self.glyph_texture_type != 0 |
246 | } |
247 | } |
248 | |