1 | // Copyright © SixtyFPS GmbH <info@slint.dev> |
2 | // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 |
3 | |
4 | /*! |
5 | This module contains color related types for the run-time library. |
6 | */ |
7 | |
8 | use crate::properties::InterpolatedPropertyValue; |
9 | |
10 | #[cfg (not(feature = "std" ))] |
11 | use num_traits::float::Float; |
12 | |
13 | /// RgbaColor stores the red, green, blue and alpha components of a color |
14 | /// with the precision of the generic parameter T. For example if T is f32, |
15 | /// the values are normalized between 0 and 1. If T is u8, they values range |
16 | /// is 0 to 255. |
17 | /// This is merely a helper class for use with [`Color`]. |
18 | #[derive (Copy, Clone, PartialEq, Debug, Default)] |
19 | pub struct RgbaColor<T> { |
20 | /// The alpha component. |
21 | pub alpha: T, |
22 | /// The red channel. |
23 | pub red: T, |
24 | /// The green channel. |
25 | pub green: T, |
26 | /// The blue channel. |
27 | pub blue: T, |
28 | } |
29 | |
30 | /// Color represents a color in the Slint run-time, represented using 8-bit channels for |
31 | /// red, green, blue and the alpha (opacity). |
32 | /// It can be conveniently converted using the `to_` and `from_` (a)rgb helper functions: |
33 | /// ``` |
34 | /// # fn do_something_with_red_and_green(_:f32, _:f32) {} |
35 | /// # fn do_something_with_red(_:u8) {} |
36 | /// # use i_slint_core::graphics::{Color, RgbaColor}; |
37 | /// # let some_color = Color::from_rgb_u8(0, 0, 0); |
38 | /// let col = some_color.to_argb_f32(); |
39 | /// do_something_with_red_and_green(col.red, col.green); |
40 | /// |
41 | /// let RgbaColor { red, blue, green, .. } = some_color.to_argb_u8(); |
42 | /// do_something_with_red(red); |
43 | /// |
44 | /// let new_col = Color::from(RgbaColor{ red: 0.5, green: 0.65, blue: 0.32, alpha: 1.}); |
45 | /// ``` |
46 | #[derive (Copy, Clone, PartialEq, PartialOrd, Debug, Default)] |
47 | #[cfg_attr (feature = "serde" , derive(serde::Serialize, serde::Deserialize))] |
48 | #[repr (C)] |
49 | pub struct Color { |
50 | red: u8, |
51 | green: u8, |
52 | blue: u8, |
53 | alpha: u8, |
54 | } |
55 | |
56 | impl From<RgbaColor<u8>> for Color { |
57 | fn from(col: RgbaColor<u8>) -> Self { |
58 | Self { red: col.red, green: col.green, blue: col.blue, alpha: col.alpha } |
59 | } |
60 | } |
61 | |
62 | impl From<Color> for RgbaColor<u8> { |
63 | fn from(col: Color) -> Self { |
64 | RgbaColor { red: col.red, green: col.green, blue: col.blue, alpha: col.alpha } |
65 | } |
66 | } |
67 | |
68 | impl From<RgbaColor<u8>> for RgbaColor<f32> { |
69 | fn from(col: RgbaColor<u8>) -> Self { |
70 | Self { |
71 | red: (col.red as f32) / 255.0, |
72 | green: (col.green as f32) / 255.0, |
73 | blue: (col.blue as f32) / 255.0, |
74 | alpha: (col.alpha as f32) / 255.0, |
75 | } |
76 | } |
77 | } |
78 | |
79 | impl From<Color> for RgbaColor<f32> { |
80 | fn from(col: Color) -> Self { |
81 | let u8col: RgbaColor<u8> = col.into(); |
82 | u8col.into() |
83 | } |
84 | } |
85 | |
86 | impl From<RgbaColor<f32>> for Color { |
87 | fn from(col: RgbaColor<f32>) -> Self { |
88 | Self { |
89 | red: (col.red * 255.).round() as u8, |
90 | green: (col.green * 255.).round() as u8, |
91 | blue: (col.blue * 255.).round() as u8, |
92 | alpha: (col.alpha * 255.).round() as u8, |
93 | } |
94 | } |
95 | } |
96 | |
97 | impl Color { |
98 | /// Construct a color from an integer encoded as `0xAARRGGBB` |
99 | pub const fn from_argb_encoded(encoded: u32) -> Color { |
100 | Self { |
101 | red: (encoded >> 16) as u8, |
102 | green: (encoded >> 8) as u8, |
103 | blue: encoded as u8, |
104 | alpha: (encoded >> 24) as u8, |
105 | } |
106 | } |
107 | |
108 | /// Returns `(alpha, red, green, blue)` encoded as u32 |
109 | pub fn as_argb_encoded(&self) -> u32 { |
110 | ((self.red as u32) << 16) |
111 | | ((self.green as u32) << 8) |
112 | | (self.blue as u32) |
113 | | ((self.alpha as u32) << 24) |
114 | } |
115 | |
116 | /// Construct a color from the alpha, red, green and blue color channel parameters. |
117 | pub const fn from_argb_u8(alpha: u8, red: u8, green: u8, blue: u8) -> Self { |
118 | Self { red, green, blue, alpha } |
119 | } |
120 | |
121 | /// Construct a color from the red, green and blue color channel parameters. The alpha |
122 | /// channel will have the value 255. |
123 | pub const fn from_rgb_u8(red: u8, green: u8, blue: u8) -> Self { |
124 | Self::from_argb_u8(255, red, green, blue) |
125 | } |
126 | |
127 | /// Construct a color from the alpha, red, green and blue color channel parameters. |
128 | pub fn from_argb_f32(alpha: f32, red: f32, green: f32, blue: f32) -> Self { |
129 | RgbaColor { alpha, red, green, blue }.into() |
130 | } |
131 | |
132 | /// Construct a color from the red, green and blue color channel parameters. The alpha |
133 | /// channel will have the value 255. |
134 | pub fn from_rgb_f32(red: f32, green: f32, blue: f32) -> Self { |
135 | Self::from_argb_f32(1.0, red, green, blue) |
136 | } |
137 | |
138 | /// Converts this color to an RgbaColor struct for easy destructuring. |
139 | pub fn to_argb_u8(&self) -> RgbaColor<u8> { |
140 | RgbaColor::from(*self) |
141 | } |
142 | |
143 | /// Converts this color to an RgbaColor struct for easy destructuring. |
144 | pub fn to_argb_f32(&self) -> RgbaColor<f32> { |
145 | RgbaColor::from(*self) |
146 | } |
147 | |
148 | /// Converts this color to the HSV color space. |
149 | pub fn to_hsva(&self) -> HsvaColor { |
150 | let rgba: RgbaColor<f32> = (*self).into(); |
151 | rgba.into() |
152 | } |
153 | |
154 | /// Construct a color from the hue, saturation, and value HSV color space parameters. |
155 | /// |
156 | /// Hue is between 0 and 360, the others parameters between 0 and 1. |
157 | pub fn from_hsva(hue: f32, saturation: f32, value: f32, alpha: f32) -> Self { |
158 | let hsva = HsvaColor { hue, saturation, value, alpha }; |
159 | <RgbaColor<f32>>::from(hsva).into() |
160 | } |
161 | |
162 | /// Returns the red channel of the color as u8 in the range 0..255. |
163 | #[inline (always)] |
164 | pub fn red(self) -> u8 { |
165 | self.red |
166 | } |
167 | |
168 | /// Returns the green channel of the color as u8 in the range 0..255. |
169 | #[inline (always)] |
170 | pub fn green(self) -> u8 { |
171 | self.green |
172 | } |
173 | |
174 | /// Returns the blue channel of the color as u8 in the range 0..255. |
175 | #[inline (always)] |
176 | pub fn blue(self) -> u8 { |
177 | self.blue |
178 | } |
179 | |
180 | /// Returns the alpha channel of the color as u8 in the range 0..255. |
181 | #[inline (always)] |
182 | pub fn alpha(self) -> u8 { |
183 | self.alpha |
184 | } |
185 | |
186 | /// Returns a new version of this color that has the brightness increased |
187 | /// by the specified factor. This is done by converting the color to the HSV |
188 | /// color space and multiplying the brightness (value) with (1 + factor). |
189 | /// The result is converted back to RGB and the alpha channel is unchanged. |
190 | /// So for example `brighter(0.2)` will increase the brightness by 20%, and |
191 | /// calling `brighter(-0.5)` will return a color that's 50% darker. |
192 | #[must_use ] |
193 | pub fn brighter(&self, factor: f32) -> Self { |
194 | let rgba: RgbaColor<f32> = (*self).into(); |
195 | let mut hsva: HsvaColor = rgba.into(); |
196 | hsva.value *= 1. + factor; |
197 | let rgba: RgbaColor<f32> = hsva.into(); |
198 | rgba.into() |
199 | } |
200 | |
201 | /// Returns a new version of this color that has the brightness decreased |
202 | /// by the specified factor. This is done by converting the color to the HSV |
203 | /// color space and dividing the brightness (value) by (1 + factor). The |
204 | /// result is converted back to RGB and the alpha channel is unchanged. |
205 | /// So for example `darker(0.3)` will decrease the brightness by 30%. |
206 | #[must_use ] |
207 | pub fn darker(&self, factor: f32) -> Self { |
208 | let rgba: RgbaColor<f32> = (*self).into(); |
209 | let mut hsva: HsvaColor = rgba.into(); |
210 | hsva.value /= 1. + factor; |
211 | let rgba: RgbaColor<f32> = hsva.into(); |
212 | rgba.into() |
213 | } |
214 | |
215 | /// Returns a new version of this color with the opacity decreased by `factor`. |
216 | /// |
217 | /// The transparency is obtained by multiplying the alpha channel by `(1 - factor)`. |
218 | /// |
219 | /// # Examples |
220 | /// Decreasing the opacity of a red color by half: |
221 | /// ``` |
222 | /// # use i_slint_core::graphics::Color; |
223 | /// let red = Color::from_argb_u8(255, 255, 0, 0); |
224 | /// assert_eq!(red.transparentize(0.5), Color::from_argb_u8(128, 255, 0, 0)); |
225 | /// ``` |
226 | /// |
227 | /// Decreasing the opacity of a blue color by 20%: |
228 | /// ``` |
229 | /// # use i_slint_core::graphics::Color; |
230 | /// let blue = Color::from_argb_u8(200, 0, 0, 255); |
231 | /// assert_eq!(blue.transparentize(0.2), Color::from_argb_u8(160, 0, 0, 255)); |
232 | /// ``` |
233 | /// |
234 | /// Negative values increase the opacity |
235 | /// |
236 | /// ``` |
237 | /// # use i_slint_core::graphics::Color; |
238 | /// let blue = Color::from_argb_u8(200, 0, 0, 255); |
239 | /// assert_eq!(blue.transparentize(-0.1), Color::from_argb_u8(220, 0, 0, 255)); |
240 | /// ``` |
241 | #[must_use ] |
242 | pub fn transparentize(&self, factor: f32) -> Self { |
243 | let mut color = *self; |
244 | color.alpha = ((self.alpha as f32) * (1.0 - factor)) |
245 | .round() |
246 | .clamp(u8::MIN as f32, u8::MAX as f32) as u8; |
247 | color |
248 | } |
249 | |
250 | /// Returns a new color that is a mix of this color and `other`. The specified factor is |
251 | /// clamped to be between `0.0` and `1.0` and then applied to this color, while `1.0 - factor` |
252 | /// is applied to `other`. |
253 | /// |
254 | /// # Examples |
255 | /// Mix red with black half-and-half: |
256 | /// ``` |
257 | /// # use i_slint_core::graphics::Color; |
258 | /// let red = Color::from_rgb_u8(255, 0, 0); |
259 | /// let black = Color::from_rgb_u8(0, 0, 0); |
260 | /// assert_eq!(red.mix(&black, 0.5), Color::from_rgb_u8(128, 0, 0)); |
261 | /// ``` |
262 | /// |
263 | /// Mix Purple with OrangeRed, with `75%` purpe and `25%` orange red ratio: |
264 | /// ``` |
265 | /// # use i_slint_core::graphics::Color; |
266 | /// let purple = Color::from_rgb_u8(128, 0, 128); |
267 | /// let orange_red = Color::from_rgb_u8(255, 69, 0); |
268 | /// assert_eq!(purple.mix(&orange_red, 0.75), Color::from_rgb_u8(160, 17, 96)); |
269 | /// ``` |
270 | #[must_use ] |
271 | pub fn mix(&self, other: &Self, factor: f32) -> Self { |
272 | // * NOTE: The opacity (`alpha` as a "percentage") of each color involved |
273 | // * must be taken into account when mixing them. Because of this, |
274 | // * we cannot just interpolate between them. |
275 | // * NOTE: Considering the spec (textual): |
276 | // * <https://github.com/sass/sass/blob/47d30713765b975c86fa32ec359ed16e83ad1ecc/spec/built-in-modules/color.md#mix> |
277 | |
278 | fn lerp(v1: u8, v2: u8, f: f32) -> u8 { |
279 | (v1 as f32 * f + v2 as f32 * (1.0 - f)).clamp(u8::MIN as f32, u8::MAX as f32).round() |
280 | as u8 |
281 | } |
282 | |
283 | let original_factor = factor.clamp(0.0, 1.0); |
284 | |
285 | let self_opacity = RgbaColor::<f32>::from(*self).alpha; |
286 | let other_opacity = RgbaColor::<f32>::from(*other).alpha; |
287 | |
288 | let normal_weight = 2.0 * original_factor - 1.0; |
289 | let alpha_distance = self_opacity - other_opacity; |
290 | let weight_by_distance = normal_weight * alpha_distance; |
291 | |
292 | // As to not divide by 0.0 |
293 | let combined_weight = if weight_by_distance == -1.0 { |
294 | normal_weight |
295 | } else { |
296 | (normal_weight + alpha_distance) / (1.0 + weight_by_distance) |
297 | }; |
298 | |
299 | let channels_factor = (combined_weight + 1.0) / 2.0; |
300 | |
301 | let red = lerp(self.red, other.red, channels_factor); |
302 | let green = lerp(self.green, other.green, channels_factor); |
303 | let blue = lerp(self.blue, other.blue, channels_factor); |
304 | |
305 | let alpha = lerp(self.alpha, other.alpha, original_factor); |
306 | |
307 | Self { red, green, blue, alpha } |
308 | } |
309 | |
310 | /// Returns a new version of this color with the opacity set to `alpha`. |
311 | #[must_use ] |
312 | pub fn with_alpha(&self, alpha: f32) -> Self { |
313 | let mut rgba: RgbaColor<f32> = (*self).into(); |
314 | rgba.alpha = alpha.clamp(0.0, 1.0); |
315 | rgba.into() |
316 | } |
317 | } |
318 | |
319 | impl InterpolatedPropertyValue for Color { |
320 | fn interpolate(&self, target_value: &Self, t: f32) -> Self { |
321 | target_value.mix(self, factor:t) |
322 | } |
323 | } |
324 | |
325 | impl core::fmt::Display for Color { |
326 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
327 | write!(f, "argb( {}, {}, {}, {})" , self.alpha, self.red, self.green, self.blue) |
328 | } |
329 | } |
330 | |
331 | /// HsvaColor stores the hue, saturation, value and alpha components of a color |
332 | /// in the HSV color space as `f32 ` fields. |
333 | /// This is merely a helper struct for use with [`Color`]. |
334 | #[derive (Copy, Clone, PartialOrd, Debug, Default)] |
335 | #[cfg_attr (feature = "serde" , derive(serde::Serialize, serde::Deserialize))] |
336 | pub struct HsvaColor { |
337 | /// The hue component in degrees between 0 and 360. |
338 | pub hue: f32, |
339 | /// The saturation component, between 0 and 1. |
340 | pub saturation: f32, |
341 | /// The value component, between 0 and 1. |
342 | pub value: f32, |
343 | /// The alpha component, between 0 and 1. |
344 | pub alpha: f32, |
345 | } |
346 | |
347 | impl PartialEq for HsvaColor { |
348 | fn eq(&self, other: &Self) -> bool { |
349 | (self.hue - other.hue).abs() < 0.00001 |
350 | && (self.saturation - other.saturation).abs() < 0.00001 |
351 | && (self.value - other.value).abs() < 0.00001 |
352 | && (self.alpha - other.alpha).abs() < 0.00001 |
353 | } |
354 | } |
355 | |
356 | impl From<RgbaColor<f32>> for HsvaColor { |
357 | fn from(col: RgbaColor<f32>) -> Self { |
358 | // RGB to HSL conversion from https://en.wikipedia.org/wiki/HSL_and_HSV#Color_conversion_formulae |
359 | |
360 | let red = col.red; |
361 | let green = col.green; |
362 | let blue = col.blue; |
363 | |
364 | let min = red.min(green).min(blue); |
365 | let max = red.max(green).max(blue); |
366 | let chroma = max - min; |
367 | |
368 | #[allow (clippy::float_cmp)] // `max` is either `red`, `green` or `blue` |
369 | let hue = num_traits::Euclid::rem_euclid( |
370 | &(60. |
371 | * if chroma == 0.0 { |
372 | 0.0 |
373 | } else if max == red { |
374 | ((green - blue) / chroma) % 6.0 |
375 | } else if max == green { |
376 | 2. + (blue - red) / chroma |
377 | } else { |
378 | 4. + (red - green) / chroma |
379 | }), |
380 | &360.0, |
381 | ); |
382 | let saturation = if max == 0. { 0. } else { chroma / max }; |
383 | |
384 | Self { hue, saturation, value: max, alpha: col.alpha } |
385 | } |
386 | } |
387 | |
388 | impl From<HsvaColor> for RgbaColor<f32> { |
389 | fn from(col: HsvaColor) -> Self { |
390 | // RGB to HSL conversion from https://en.wikipedia.org/wiki/HSL_and_HSV#Color_conversion_formulae |
391 | |
392 | let chroma = col.saturation * col.value; |
393 | |
394 | let hue = num_traits::Euclid::rem_euclid(&col.hue, &360.0); |
395 | |
396 | let x = chroma * (1. - ((hue / 60.) % 2. - 1.).abs()); |
397 | |
398 | let (red, green, blue) = match (hue / 60.0) as usize { |
399 | 0 => (chroma, x, 0.), |
400 | 1 => (x, chroma, 0.), |
401 | 2 => (0., chroma, x), |
402 | 3 => (0., x, chroma), |
403 | 4 => (x, 0., chroma), |
404 | 5 => (chroma, 0., x), |
405 | _ => (0., 0., 0.), |
406 | }; |
407 | |
408 | let m = col.value - chroma; |
409 | |
410 | Self { red: red + m, green: green + m, blue: blue + m, alpha: col.alpha } |
411 | } |
412 | } |
413 | |
414 | impl From<HsvaColor> for Color { |
415 | fn from(value: HsvaColor) -> Self { |
416 | RgbaColor::from(value).into() |
417 | } |
418 | } |
419 | |
420 | impl From<Color> for HsvaColor { |
421 | fn from(value: Color) -> Self { |
422 | value.to_hsva() |
423 | } |
424 | } |
425 | |
426 | #[test ] |
427 | fn test_rgb_to_hsv() { |
428 | // White |
429 | assert_eq!( |
430 | HsvaColor::from(RgbaColor::<f32> { red: 1., green: 1., blue: 1., alpha: 0.5 }), |
431 | HsvaColor { hue: 0., saturation: 0., value: 1., alpha: 0.5 } |
432 | ); |
433 | assert_eq!( |
434 | RgbaColor::<f32>::from(HsvaColor { hue: 0., saturation: 0., value: 1., alpha: 0.3 }), |
435 | RgbaColor::<f32> { red: 1., green: 1., blue: 1., alpha: 0.3 } |
436 | ); |
437 | |
438 | // #8a0c77ff ensure the hue ends up positive |
439 | assert_eq!( |
440 | HsvaColor::from(Color::from_argb_u8(0xff, 0x8a, 0xc, 0x77,).to_argb_f32()), |
441 | HsvaColor { hue: 309.0476, saturation: 0.9130435, value: 0.5411765, alpha: 1.0 } |
442 | ); |
443 | |
444 | let received = RgbaColor::<f32>::from(HsvaColor { |
445 | hue: 309.0476, |
446 | saturation: 0.9130435, |
447 | value: 0.5411765, |
448 | alpha: 1.0, |
449 | }); |
450 | let expected = Color::from_argb_u8(0xff, 0x8a, 0xc, 0x77).to_argb_f32(); |
451 | |
452 | assert!( |
453 | (received.alpha - expected.alpha).abs() < 0.00001 |
454 | && (received.red - expected.red).abs() < 0.00001 |
455 | && (received.green - expected.green).abs() < 0.00001 |
456 | && (received.blue - expected.blue).abs() < 0.00001 |
457 | ); |
458 | |
459 | // Bright greenish, verified via colorizer.org |
460 | assert_eq!( |
461 | HsvaColor::from(RgbaColor::<f32> { red: 0., green: 0.9, blue: 0., alpha: 1.0 }), |
462 | HsvaColor { hue: 120., saturation: 1., value: 0.9, alpha: 1.0 } |
463 | ); |
464 | assert_eq!( |
465 | RgbaColor::<f32>::from(HsvaColor { hue: 120., saturation: 1., value: 0.9, alpha: 1.0 }), |
466 | RgbaColor::<f32> { red: 0., green: 0.9, blue: 0., alpha: 1.0 } |
467 | ); |
468 | |
469 | // Hue should wrap around 360deg i.e. 480 == 120 && -240 == 240 |
470 | assert_eq!( |
471 | RgbaColor::<f32> { red: 0., green: 0.9, blue: 0., alpha: 1.0 }, |
472 | RgbaColor::<f32>::from(HsvaColor { hue: 480., saturation: 1., value: 0.9, alpha: 1.0 }), |
473 | ); |
474 | assert_eq!( |
475 | RgbaColor::<f32> { red: 0., green: 0.9, blue: 0., alpha: 1.0 }, |
476 | RgbaColor::<f32>::from(HsvaColor { hue: -240., saturation: 1., value: 0.9, alpha: 1.0 }), |
477 | ); |
478 | } |
479 | |
480 | #[test ] |
481 | fn test_brighter_darker() { |
482 | let blue = Color::from_rgb_u8(0, 0, 128); |
483 | assert_eq!(blue.brighter(0.5), Color::from_rgb_u8(0, 0, 192)); |
484 | assert_eq!(blue.darker(0.5), Color::from_rgb_u8(0, 0, 85)); |
485 | } |
486 | |
487 | #[test ] |
488 | fn test_transparent_transition() { |
489 | let color = Color::from_argb_u8(0, 0, 0, 0); |
490 | let interpolated = color.interpolate(&Color::from_rgb_u8(211, 211, 211), 0.25); |
491 | assert_eq!(interpolated, Color::from_argb_u8(64, 211, 211, 211)); |
492 | let interpolated = color.interpolate(&Color::from_rgb_u8(211, 211, 211), 0.5); |
493 | assert_eq!(interpolated, Color::from_argb_u8(128, 211, 211, 211)); |
494 | let interpolated = color.interpolate(&Color::from_rgb_u8(211, 211, 211), 0.75); |
495 | assert_eq!(interpolated, Color::from_argb_u8(191, 211, 211, 211)); |
496 | } |
497 | |
498 | #[cfg (feature = "ffi" )] |
499 | pub(crate) mod ffi { |
500 | #![allow (unsafe_code)] |
501 | use super::*; |
502 | |
503 | #[no_mangle ] |
504 | pub unsafe extern "C" fn slint_color_brighter(col: &Color, factor: f32, out: *mut Color) { |
505 | core::ptr::write(out, col.brighter(factor)) |
506 | } |
507 | |
508 | #[no_mangle ] |
509 | pub unsafe extern "C" fn slint_color_darker(col: &Color, factor: f32, out: *mut Color) { |
510 | core::ptr::write(out, col.darker(factor)) |
511 | } |
512 | |
513 | #[no_mangle ] |
514 | pub unsafe extern "C" fn slint_color_transparentize(col: &Color, factor: f32, out: *mut Color) { |
515 | core::ptr::write(out, col.transparentize(factor)) |
516 | } |
517 | |
518 | #[no_mangle ] |
519 | pub unsafe extern "C" fn slint_color_mix( |
520 | col1: &Color, |
521 | col2: &Color, |
522 | factor: f32, |
523 | out: *mut Color, |
524 | ) { |
525 | core::ptr::write(out, col1.mix(col2, factor)) |
526 | } |
527 | |
528 | #[no_mangle ] |
529 | pub unsafe extern "C" fn slint_color_with_alpha(col: &Color, alpha: f32, out: *mut Color) { |
530 | core::ptr::write(out, col.with_alpha(alpha)) |
531 | } |
532 | |
533 | #[no_mangle ] |
534 | pub extern "C" fn slint_color_to_hsva( |
535 | col: &Color, |
536 | h: &mut f32, |
537 | s: &mut f32, |
538 | v: &mut f32, |
539 | a: &mut f32, |
540 | ) { |
541 | let hsv = col.to_hsva(); |
542 | *h = hsv.hue; |
543 | *s = hsv.saturation; |
544 | *v = hsv.value; |
545 | *a = hsv.alpha; |
546 | } |
547 | |
548 | #[no_mangle ] |
549 | pub extern "C" fn slint_color_from_hsva(h: f32, s: f32, v: f32, a: f32) -> Color { |
550 | Color::from_hsva(h, s, v, a) |
551 | } |
552 | } |
553 | |