diff --git a/sixtyfps_runtime/corelib/graphics.rs b/sixtyfps_runtime/corelib/graphics.rs index f3ef47d8e..15e51bc40 100644 --- a/sixtyfps_runtime/corelib/graphics.rs +++ b/sixtyfps_runtime/corelib/graphics.rs @@ -37,6 +37,9 @@ pub use color::*; mod path; pub use path::*; +mod brush; +pub use brush::*; + /// A resource is a reference to binary data, for example images. They can be accessible on the file /// system or embedded in the resulting binary. Or they might be URLs to a web server and a downloaded /// is necessary before they can be used. diff --git a/sixtyfps_runtime/corelib/graphics/brush.rs b/sixtyfps_runtime/corelib/graphics/brush.rs new file mode 100644 index 000000000..2af074b2b --- /dev/null +++ b/sixtyfps_runtime/corelib/graphics/brush.rs @@ -0,0 +1,82 @@ +/* LICENSE BEGIN + This file is part of the SixtyFPS Project -- https://sixtyfps.io + Copyright (c) 2020 Olivier Goffart + Copyright (c) 2020 Simon Hausmann + + SPDX-License-Identifier: GPL-3.0-only + This file is also available under commercial licensing terms. + Please contact info@sixtyfps.io for more information. +LICENSE END */ +/*! +This module contains brush related types for the run-time library. +*/ + +use super::Color; +use crate::SharedVector; + +/// A brush is an opaque data structure that is used to describe how +/// a shape, such as a rectangle, path or even text, shall be filled. +/// A brush can also be applied to the outline of a shape, that means +/// the fill of the outline itself. +#[repr(transparent)] +pub struct Brush(BrushInner); + +/// BrushInner is the variant for the `Brush` type that can be either +/// a color or a linear gradient. +#[repr(C)] +pub enum BrushInner { + /// The color variant of brush is a plain color that is to be used for the fill. + Color(Color), + /// The linear gradient variant of a brush describes the gradient stops for a fill + /// where all color stops are along a line that's rotated by the specified angle. + LinearGradient(LinearGradient), +} + +/// The LinearGradient describes a way of filling a shape with different colors, which +/// are interpolated between different stops. The colors are aligned with a line that's rotated +/// by the LinearGradient's angle. +#[repr(transparent)] +pub struct LinearGradient(SharedVector); + +impl LinearGradient { + /// Creates a new linear gradient, described by the specified angle and the provided color stops. + pub fn new(angle: f32, stops: impl IntoIterator) -> Self { + let stop_iter = stops.into_iter(); + let mut encoded_angle_and_stops = SharedVector::with_capacity(stop_iter.size_hint().0 + 1); + encoded_angle_and_stops.push(GradientStop { color: Default::default(), position: angle }); + encoded_angle_and_stops.extend(stop_iter); + Self(encoded_angle_and_stops) + } + /// Returns the angle of the linear gradient in degrees. + pub fn angle(&self) -> f32 { + self.0[0].position + } + /// Returns the color stops of the linear gradient. + pub fn stops<'a>(&'a self) -> impl Iterator + 'a { + self.0.iter().skip(1) + } +} + +/// GradientStop describes a single color stop in a gradient. The colors between multiple +/// stops are interpolated. +#[repr(C)] +#[derive(Clone, Debug, PartialEq)] +pub struct GradientStop { + /// The color to draw at this stop. + color: Color, + /// The position of this stop on the entire shape, as a normalized value between 0 and 1. + position: f32, +} + +#[test] +fn test_linear_gradient_encoding() { + let stops: SharedVector = [ + GradientStop { position: 0.0, color: Color::from_argb_u8(255, 255, 0, 0) }, + GradientStop { position: 0.5, color: Color::from_argb_u8(255, 0, 255, 0) }, + GradientStop { position: 1.0, color: Color::from_argb_u8(255, 0, 0, 255) }, + ] + .into(); + let grad = LinearGradient::new(256., stops.clone()); + assert_eq!(grad.angle(), 256.); + assert!(grad.stops().eq(stops.iter())); +} diff --git a/sixtyfps_runtime/corelib/sharedvector.rs b/sixtyfps_runtime/corelib/sharedvector.rs index a7f2c922b..553754bed 100644 --- a/sixtyfps_runtime/corelib/sharedvector.rs +++ b/sixtyfps_runtime/corelib/sharedvector.rs @@ -314,6 +314,14 @@ impl FromIterator for SharedVector { } } +impl Extend for SharedVector { + fn extend>(&mut self, iter: X) { + for item in iter { + self.push(item); + } + } +} + static SHARED_NULL: SharedVectorHeader = SharedVectorHeader { refcount: std::sync::atomic::AtomicIsize::new(-1), size: 0, capacity: 0 }; diff --git a/tests/driver_lib/cbindgen.rs b/tests/driver_lib/cbindgen.rs index 815f475e4..d22c087a7 100644 --- a/tests/driver_lib/cbindgen.rs +++ b/tests/driver_lib/cbindgen.rs @@ -198,6 +198,7 @@ fn gen_corelib(include_dir: &Path) -> anyhow::Result<()> { .with_src(crate_dir.join("graphics.rs")) .with_src(crate_dir.join("graphics/color.rs")) .with_src(crate_dir.join("graphics/path.rs")) + .with_src(crate_dir.join("graphics/brush.rs")) .with_src(crate_dir.join("animations.rs")) // .with_src(crate_dir.join("input.rs")) .with_src(crate_dir.join("item_rendering.rs"))