1 | use crate::{prelude::*, scalar, Color, Color4f, ColorSpace, Matrix, Point, Shader, TileMode}; |
2 | use sb::SkGradientShader_Interpolation; |
3 | use skia_bindings as sb; |
4 | |
5 | #[derive (Copy, Clone, PartialEq, Eq, Hash, Debug)] |
6 | #[repr (C)] |
7 | pub struct Interpolation { |
8 | pub in_premul: interpolation::InPremul, |
9 | pub color_space: interpolation::ColorSpace, |
10 | pub hue_method: interpolation::HueMethod, |
11 | } |
12 | |
13 | native_transmutable!( |
14 | SkGradientShader_Interpolation, |
15 | Interpolation, |
16 | interpolation_layout |
17 | ); |
18 | |
19 | pub mod interpolation { |
20 | pub type InPremul = skia_bindings::SkGradientShader_Interpolation_InPremul; |
21 | variant_name!(InPremul::Yes); |
22 | |
23 | pub type ColorSpace = skia_bindings::SkGradientShader_Interpolation_ColorSpace; |
24 | variant_name!(ColorSpace::HSL); |
25 | |
26 | pub type HueMethod = skia_bindings::SkGradientShader_Interpolation_HueMethod; |
27 | variant_name!(HueMethod::Shorter); |
28 | } |
29 | |
30 | impl From<Flags> for Interpolation { |
31 | fn from(flags: Flags) -> Self { |
32 | let in_premul: SkGradientShader_Interpolation_InPremul = if flags.contains(Flags::INTERPOLATE_COLORS_IN_PREMUL) { |
33 | interpolation::InPremul::Yes |
34 | } else { |
35 | interpolation::InPremul::No |
36 | }; |
37 | Self { |
38 | in_premul, |
39 | color_space: interpolation::ColorSpace::Destination, |
40 | hue_method: interpolation::HueMethod::Shorter, |
41 | } |
42 | } |
43 | } |
44 | |
45 | impl Shader { |
46 | pub fn linear_gradient<'a>( |
47 | points: (impl Into<Point>, impl Into<Point>), |
48 | colors: impl Into<GradientShaderColors<'a>>, |
49 | pos: impl Into<Option<&'a [scalar]>>, |
50 | mode: TileMode, |
51 | flags: impl Into<Option<Flags>>, |
52 | local_matrix: impl Into<Option<&'a Matrix>>, |
53 | ) -> Option<Self> { |
54 | linear(points, colors, pos, mode, flags, local_matrix) |
55 | } |
56 | |
57 | pub fn linear_gradient_with_interpolation<'a>( |
58 | points: (impl Into<Point>, impl Into<Point>), |
59 | colors: (&[Color4f], impl Into<Option<ColorSpace>>), |
60 | pos: impl Into<Option<&'a [scalar]>>, |
61 | mode: TileMode, |
62 | interpolation: impl Into<Interpolation>, |
63 | local_matrix: impl Into<Option<&'a Matrix>>, |
64 | ) -> Option<Self> { |
65 | linear_with_interpolation(points, colors, pos, mode, interpolation, local_matrix) |
66 | } |
67 | |
68 | pub fn radial_gradient<'a>( |
69 | center: impl Into<Point>, |
70 | radius: scalar, |
71 | colors: impl Into<GradientShaderColors<'a>>, |
72 | pos: impl Into<Option<&'a [scalar]>>, |
73 | mode: TileMode, |
74 | flags: impl Into<Option<self::Flags>>, |
75 | local_matrix: impl Into<Option<&'a Matrix>>, |
76 | ) -> Option<Self> { |
77 | radial(center, radius, colors, pos, mode, flags, local_matrix) |
78 | } |
79 | |
80 | #[allow (clippy::too_many_arguments)] |
81 | pub fn radial_gradient_with_interpolation<'a>( |
82 | center_and_radius: (impl Into<Point>, scalar), |
83 | colors: (&[Color4f], impl Into<Option<ColorSpace>>), |
84 | pos: impl Into<Option<&'a [scalar]>>, |
85 | mode: TileMode, |
86 | interpolation: impl Into<Interpolation>, |
87 | local_matrix: impl Into<Option<&'a Matrix>>, |
88 | ) -> Option<Shader> { |
89 | radial_with_interpolation( |
90 | center_and_radius, |
91 | colors, |
92 | pos, |
93 | mode, |
94 | interpolation, |
95 | local_matrix, |
96 | ) |
97 | } |
98 | |
99 | #[allow (clippy::too_many_arguments)] |
100 | pub fn two_point_conical_gradient<'a>( |
101 | start: impl Into<Point>, |
102 | start_radius: scalar, |
103 | end: impl Into<Point>, |
104 | end_radius: scalar, |
105 | colors: impl Into<GradientShaderColors<'a>>, |
106 | pos: impl Into<Option<&'a [scalar]>>, |
107 | mode: TileMode, |
108 | flags: impl Into<Option<self::Flags>>, |
109 | local_matrix: impl Into<Option<&'a Matrix>>, |
110 | ) -> Option<Self> { |
111 | two_point_conical( |
112 | start, |
113 | start_radius, |
114 | end, |
115 | end_radius, |
116 | colors, |
117 | pos, |
118 | mode, |
119 | flags, |
120 | local_matrix, |
121 | ) |
122 | } |
123 | |
124 | pub fn two_point_conical_gradient_with_interpolation<'a>( |
125 | start_and_radius: (impl Into<Point>, scalar), |
126 | end_and_radius: (impl Into<Point>, scalar), |
127 | colors: (&[Color4f], impl Into<Option<ColorSpace>>), |
128 | pos: impl Into<Option<&'a [scalar]>>, |
129 | mode: TileMode, |
130 | interpolation: impl Into<Interpolation>, |
131 | local_matrix: impl Into<Option<&'a Matrix>>, |
132 | ) -> Option<Shader> { |
133 | two_point_conical_with_interpolation( |
134 | start_and_radius, |
135 | end_and_radius, |
136 | colors, |
137 | pos, |
138 | mode, |
139 | interpolation, |
140 | local_matrix, |
141 | ) |
142 | } |
143 | |
144 | pub fn sweep_gradient<'a>( |
145 | center: impl Into<Point>, |
146 | colors: impl Into<GradientShaderColors<'a>>, |
147 | pos: impl Into<Option<&'a [scalar]>>, |
148 | mode: TileMode, |
149 | angles: impl Into<Option<(scalar, scalar)>>, |
150 | flags: impl Into<Option<self::Flags>>, |
151 | local_matrix: impl Into<Option<&'a Matrix>>, |
152 | ) -> Option<Self> { |
153 | sweep(center, colors, pos, mode, angles, flags, local_matrix) |
154 | } |
155 | |
156 | pub fn sweep_gradient_with_interpolation<'a>( |
157 | center: impl Into<Point>, |
158 | colors: (&[Color4f], impl Into<Option<ColorSpace>>), |
159 | pos: impl Into<Option<&'a [scalar]>>, |
160 | mode: TileMode, |
161 | angles: impl Into<Option<(scalar, scalar)>>, |
162 | interpolation: impl Into<Interpolation>, |
163 | local_matrix: impl Into<Option<&'a Matrix>>, |
164 | ) -> Option<Shader> { |
165 | sweep_with_interpolation( |
166 | center, |
167 | colors, |
168 | pos, |
169 | mode, |
170 | angles, |
171 | interpolation, |
172 | local_matrix, |
173 | ) |
174 | } |
175 | } |
176 | |
177 | bitflags! { |
178 | #[derive (Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] |
179 | pub struct Flags: u32 { |
180 | const INTERPOLATE_COLORS_IN_PREMUL = sb::SkGradientShader_Flags_kInterpolateColorsInPremul_Flag as _; |
181 | } |
182 | } |
183 | |
184 | impl Default for self::Flags { |
185 | fn default() -> Self { |
186 | Self::empty() |
187 | } |
188 | } |
189 | |
190 | pub fn linear<'a>( |
191 | points: (impl Into<Point>, impl Into<Point>), |
192 | colors: impl Into<GradientShaderColors<'a>>, |
193 | pos: impl Into<Option<&'a [scalar]>>, |
194 | mode: TileMode, |
195 | flags: impl Into<Option<Flags>>, |
196 | local_matrix: impl Into<Option<&'a Matrix>>, |
197 | ) -> Option<Shader> { |
198 | let colors = colors.into(); |
199 | let pos = pos.into(); |
200 | assert!(pos.is_none() || (pos.unwrap().len() == colors.len())); |
201 | let flags = flags.into().unwrap_or_default(); |
202 | let local_matrix = local_matrix.into(); |
203 | |
204 | match colors { |
205 | GradientShaderColors::Colors(colors) => Shader::from_ptr(unsafe { |
206 | let points = [points.0.into(), points.1.into()]; |
207 | sb::C_SkGradientShader_MakeLinear( |
208 | points.native().as_ptr(), |
209 | colors.native().as_ptr(), |
210 | pos.as_ptr_or_null(), |
211 | colors.len().try_into().unwrap(), |
212 | mode, |
213 | flags.bits(), |
214 | local_matrix.native_ptr_or_null(), |
215 | ) |
216 | }), |
217 | GradientShaderColors::ColorsInSpace(colors, color_space) => linear_with_interpolation( |
218 | points, |
219 | (colors, color_space), |
220 | pos, |
221 | mode, |
222 | flags, |
223 | local_matrix, |
224 | ), |
225 | } |
226 | } |
227 | |
228 | pub fn linear_with_interpolation<'a>( |
229 | points: (impl Into<Point>, impl Into<Point>), |
230 | (colors: &[Color4f], color_space: impl Into): (&[Color4f], impl Into<Option<ColorSpace>>), |
231 | pos: impl Into<Option<&'a [scalar]>>, |
232 | mode: TileMode, |
233 | interpolation: impl Into<Interpolation>, |
234 | local_matrix: impl Into<Option<&'a Matrix>>, |
235 | ) -> Option<Shader> { |
236 | let points: [{unknown}; 2] = [points.0.into(), points.1.into()]; |
237 | let color_space = color_space.into(); |
238 | let pos = pos.into(); |
239 | assert!(pos.is_none() || (pos.unwrap().len() == colors.len())); |
240 | let interpolation = interpolation.into(); |
241 | let local_matrix = local_matrix.into(); |
242 | Shader::from_ptr(unsafe { |
243 | sb::C_SkGradientShader_MakeLinearWithInterpolation( |
244 | pts:points.native().as_ptr(), |
245 | colors:colors.native().as_ptr(), |
246 | colorSpace:color_space.into_ptr_or_null(), |
247 | pos:pos.as_ptr_or_null(), |
248 | count:colors.len().try_into().unwrap(), |
249 | mode, |
250 | interpolation:interpolation.native(), |
251 | localMatrix:local_matrix.native_ptr_or_null(), |
252 | ) |
253 | }) |
254 | } |
255 | |
256 | pub fn radial<'a>( |
257 | center: impl Into<Point>, |
258 | radius: scalar, |
259 | colors: impl Into<GradientShaderColors<'a>>, |
260 | pos: impl Into<Option<&'a [scalar]>>, |
261 | mode: TileMode, |
262 | flags: impl Into<Option<self::Flags>>, |
263 | local_matrix: impl Into<Option<&'a Matrix>>, |
264 | ) -> Option<Shader> { |
265 | let colors = colors.into(); |
266 | let center = center.into(); |
267 | let pos = pos.into(); |
268 | assert!(pos.is_none() || (pos.unwrap().len() == colors.len())); |
269 | let flags = flags.into().unwrap_or_default(); |
270 | let local_matrix = local_matrix.into(); |
271 | |
272 | match colors { |
273 | GradientShaderColors::Colors(colors) => Shader::from_ptr(unsafe { |
274 | sb::C_SkGradientShader_MakeRadial( |
275 | center.native(), |
276 | radius, |
277 | colors.native().as_ptr(), |
278 | pos.as_ptr_or_null(), |
279 | colors.len().try_into().unwrap(), |
280 | mode, |
281 | flags.bits(), |
282 | local_matrix.native_ptr_or_null(), |
283 | ) |
284 | }), |
285 | |
286 | GradientShaderColors::ColorsInSpace(colors, color_space) => radial_with_interpolation( |
287 | (center, radius), |
288 | (colors, color_space), |
289 | pos, |
290 | mode, |
291 | flags, |
292 | local_matrix, |
293 | ), |
294 | } |
295 | } |
296 | |
297 | pub fn radial_with_interpolation<'a>( |
298 | (center: impl Into, radius: f32): (impl Into<Point>, scalar), |
299 | (colors: &[Color4f], color_space: impl Into): (&[Color4f], impl Into<Option<ColorSpace>>), |
300 | pos: impl Into<Option<&'a [scalar]>>, |
301 | mode: TileMode, |
302 | interpolation: impl Into<Interpolation>, |
303 | local_matrix: impl Into<Option<&'a Matrix>>, |
304 | ) -> Option<Shader> { |
305 | let color_space = color_space.into(); |
306 | let center = center.into(); |
307 | let pos = pos.into(); |
308 | assert!(pos.is_none() || (pos.unwrap().len() == colors.len())); |
309 | let interpolation = interpolation.into(); |
310 | let local_matrix = local_matrix.into(); |
311 | |
312 | Shader::from_ptr(unsafe { |
313 | sb::C_SkGradientShader_MakeRadialWithInterpolation( |
314 | center:center.native(), |
315 | radius, |
316 | colors:colors.native().as_ptr(), |
317 | colorSpace:color_space.into_ptr_or_null(), |
318 | pos:pos.as_ptr_or_null(), |
319 | count:colors.len().try_into().unwrap(), |
320 | mode, |
321 | interpolation:interpolation.native(), |
322 | localMatrix:local_matrix.native_ptr_or_null(), |
323 | ) |
324 | }) |
325 | } |
326 | |
327 | #[allow (clippy::too_many_arguments)] |
328 | pub fn two_point_conical<'a>( |
329 | start: impl Into<Point>, |
330 | start_radius: scalar, |
331 | end: impl Into<Point>, |
332 | end_radius: scalar, |
333 | colors: impl Into<GradientShaderColors<'a>>, |
334 | pos: impl Into<Option<&'a [scalar]>>, |
335 | mode: TileMode, |
336 | flags: impl Into<Option<self::Flags>>, |
337 | local_matrix: impl Into<Option<&'a Matrix>>, |
338 | ) -> Option<Shader> { |
339 | let colors = colors.into(); |
340 | let start = start.into(); |
341 | let end = end.into(); |
342 | let pos = pos.into(); |
343 | assert!(pos.is_none() || (pos.unwrap().len() == colors.len())); |
344 | let flags = flags.into().unwrap_or_default(); |
345 | let local_matrix = local_matrix.into(); |
346 | |
347 | match colors { |
348 | GradientShaderColors::Colors(colors) => Shader::from_ptr(unsafe { |
349 | sb::C_SkGradientShader_MakeTwoPointConical( |
350 | start.native(), |
351 | start_radius, |
352 | end.native(), |
353 | end_radius, |
354 | colors.native().as_ptr(), |
355 | pos.as_ptr_or_null(), |
356 | colors.len().try_into().unwrap(), |
357 | mode, |
358 | flags.bits(), |
359 | local_matrix.native_ptr_or_null(), |
360 | ) |
361 | }), |
362 | |
363 | GradientShaderColors::ColorsInSpace(colors, color_space) => { |
364 | two_point_conical_with_interpolation( |
365 | (start, start_radius), |
366 | (end, end_radius), |
367 | (colors, color_space), |
368 | pos, |
369 | mode, |
370 | flags, |
371 | local_matrix, |
372 | ) |
373 | } |
374 | } |
375 | } |
376 | |
377 | pub fn two_point_conical_with_interpolation<'a>( |
378 | (start: impl Into, start_radius: f32): (impl Into<Point>, scalar), |
379 | (end: impl Into, end_radius: f32): (impl Into<Point>, scalar), |
380 | (colors: &[Color4f], color_space: impl Into): (&[Color4f], impl Into<Option<ColorSpace>>), |
381 | pos: impl Into<Option<&'a [scalar]>>, |
382 | mode: TileMode, |
383 | interpolation: impl Into<Interpolation>, |
384 | local_matrix: impl Into<Option<&'a Matrix>>, |
385 | ) -> Option<Shader> { |
386 | let start = start.into(); |
387 | let end = end.into(); |
388 | let pos = pos.into(); |
389 | assert!(pos.is_none() || (pos.unwrap().len() == colors.len())); |
390 | let local_matrix = local_matrix.into(); |
391 | |
392 | Shader::from_ptr(unsafe { |
393 | sb::C_SkGradientShader_MakeTwoPointConicalWithInterpolation( |
394 | start:start.native(), |
395 | startRadius:start_radius, |
396 | end:end.native(), |
397 | endRadius:end_radius, |
398 | colors:colors.native().as_ptr(), |
399 | colorSpace:color_space.into().into_ptr_or_null(), |
400 | pos:pos.as_ptr_or_null(), |
401 | count:colors.len().try_into().unwrap(), |
402 | mode, |
403 | interpolation:interpolation.into().native(), |
404 | localMatrix:local_matrix.native_ptr_or_null(), |
405 | ) |
406 | }) |
407 | } |
408 | |
409 | pub fn sweep<'a>( |
410 | center: impl Into<Point>, |
411 | colors: impl Into<GradientShaderColors<'a>>, |
412 | pos: impl Into<Option<&'a [scalar]>>, |
413 | mode: TileMode, |
414 | angles: impl Into<Option<(scalar, scalar)>>, |
415 | flags: impl Into<Option<self::Flags>>, |
416 | local_matrix: impl Into<Option<&'a Matrix>>, |
417 | ) -> Option<Shader> { |
418 | let center = center.into(); |
419 | let colors = colors.into(); |
420 | let pos = pos.into(); |
421 | assert!(pos.is_none() || (pos.unwrap().len() == colors.len())); |
422 | let angles = angles.into(); |
423 | let flags = flags.into().unwrap_or_default(); |
424 | let local_matrix = local_matrix.into(); |
425 | |
426 | let (start_angle, end_angle) = ( |
427 | angles.map(|a| a.0).unwrap_or(0.0), |
428 | angles.map(|a| a.1).unwrap_or(360.0), |
429 | ); |
430 | |
431 | match colors { |
432 | GradientShaderColors::Colors(colors) => Shader::from_ptr(unsafe { |
433 | sb::C_SkGradientShader_MakeSweep( |
434 | center.x, |
435 | center.y, |
436 | colors.native().as_ptr(), |
437 | pos.as_ptr_or_null(), |
438 | colors.len().try_into().unwrap(), |
439 | mode, |
440 | start_angle, |
441 | end_angle, |
442 | flags.bits(), |
443 | local_matrix.native_ptr_or_null(), |
444 | ) |
445 | }), |
446 | GradientShaderColors::ColorsInSpace(colors, color_space) => sweep_with_interpolation( |
447 | center, |
448 | (colors, color_space), |
449 | pos, |
450 | mode, |
451 | angles, |
452 | flags, |
453 | local_matrix, |
454 | ), |
455 | } |
456 | } |
457 | |
458 | pub fn sweep_with_interpolation<'a>( |
459 | center: impl Into<Point>, |
460 | (colors: &[Color4f], color_space: impl Into): (&[Color4f], impl Into<Option<ColorSpace>>), |
461 | pos: impl Into<Option<&'a [scalar]>>, |
462 | mode: TileMode, |
463 | angles: impl Into<Option<(scalar, scalar)>>, |
464 | interpolation: impl Into<Interpolation>, |
465 | local_matrix: impl Into<Option<&'a Matrix>>, |
466 | ) -> Option<Shader> { |
467 | let center = center.into(); |
468 | let pos = pos.into(); |
469 | assert!(pos.is_none() || (pos.unwrap().len() == colors.len())); |
470 | let angles = angles.into(); |
471 | let local_matrix = local_matrix.into(); |
472 | |
473 | let (start_angle, end_angle) = ( |
474 | angles.map(|a| a.0).unwrap_or(0.0), |
475 | angles.map(|a| a.1).unwrap_or(360.0), |
476 | ); |
477 | |
478 | Shader::from_ptr(unsafe { |
479 | sb::C_SkGradientShader_MakeSweepWithInterpolation( |
480 | center.x, |
481 | center.y, |
482 | colors.native().as_ptr(), |
483 | color_space.into().into_ptr_or_null(), |
484 | pos.as_ptr_or_null(), |
485 | colors.len().try_into().unwrap(), |
486 | mode, |
487 | start_angle, |
488 | end_angle, |
489 | interpolation.into().native(), |
490 | local_matrix.native_ptr_or_null(), |
491 | ) |
492 | }) |
493 | } |
494 | |
495 | /// Type that represents either a slice of [`Color`], or a slice of [`Color4f`] and a color space. |
496 | /// Whenever this type is expected, it's either possible to directly pass a `&[Color]` , or |
497 | /// a tuple of type `(&[Color4f], &ColorSpace)`. |
498 | #[derive (Debug)] |
499 | pub enum GradientShaderColors<'a> { |
500 | Colors(&'a [Color]), |
501 | ColorsInSpace(&'a [Color4f], Option<ColorSpace>), |
502 | } |
503 | |
504 | impl GradientShaderColors<'_> { |
505 | pub fn len(&self) -> usize { |
506 | match self { |
507 | GradientShaderColors::Colors(colors: &&[Color]) => colors.len(), |
508 | GradientShaderColors::ColorsInSpace(colors: &&[Color4f], _) => colors.len(), |
509 | } |
510 | } |
511 | |
512 | // to keep clippy happy. |
513 | pub fn is_empty(&self) -> bool { |
514 | self.len() == 0 |
515 | } |
516 | } |
517 | |
518 | impl<'a> From<&'a [Color]> for GradientShaderColors<'a> { |
519 | fn from(colors: &'a [Color]) -> Self { |
520 | GradientShaderColors::<'a>::Colors(colors) |
521 | } |
522 | } |
523 | |
524 | impl<'a> From<(&'a [Color4f], ColorSpace)> for GradientShaderColors<'a> { |
525 | fn from(c: (&'a [Color4f], ColorSpace)) -> Self { |
526 | GradientShaderColors::<'a>::ColorsInSpace(c.0, Some(c.1)) |
527 | } |
528 | } |
529 | |
530 | impl<'a> From<&'a [Color4f]> for GradientShaderColors<'a> { |
531 | fn from(c: &'a [Color4f]) -> Self { |
532 | GradientShaderColors::<'a>::ColorsInSpace(c, None) |
533 | } |
534 | } |
535 | |