| 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 | |