1use crate::{
2 geometry::Position,
3 paint::{GlyphTexture, GradientColors},
4 ImageFlags, ImageStore, PaintFlavor, PixelFormat, Scissor, Transform2D,
5};
6
7use super::ShaderType;
8
9#[derive(Copy, Clone, Debug, Default)]
10pub 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
30impl 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