1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
// Copyright (c) 2023 Xu Shaohua <shaohua@biofan.org>. All rights reserved.
// Use of this source is governed by Lesser General Public License that can be found
// in the LICENSE file.
//! Paint controls options applied when drawing.
//!
//! Paint collects all options outside of the Canvas clip and Canvas matrix.
//! Various options apply to strokes and fills, and images.
//!
//! Paint collects effects and filters that describe single-pass and multiple-pass
//! algorithms that alter the drawing geometry, color, and transparency.
//! For instance, Paint does not directly implement dashing or blur, but contains
//! the objects that do so.
use crate::core::color::{colors::BLACK, Color, Color4f};
use crate::core::color_space::ColorSpace;
use crate::core::font_types::FontHinting;
use crate::core::paint_types::{PaintStyle, StrokeCap, StrokeJoin};
use crate::core::scalar::Scalar;
pub const DEFAULT_TEXT_SIZE: Scalar = 12.0;
pub const DEFAULT_FONT_HINTING: FontHinting = FontHinting::Normal;
pub const DEFAULT_MITER_LIMIT: Scalar = 4.0;
#[derive(Debug, Clone, PartialEq)]
pub struct Paint {
color_space: Option<ColorSpace>,
color4f: Color4f,
stroke_width: Scalar,
miter_limit: Scalar,
anti_alias: bool,
dither: bool,
style: PaintStyle,
cap: StrokeCap,
join: StrokeJoin,
}
impl Paint {
/// Constructs Paint with default values.
#[must_use]
pub fn new() -> Self {
Self {
color_space: None,
color4f: BLACK,
stroke_width: 0.0,
miter_limit: DEFAULT_MITER_LIMIT,
anti_alias: false,
dither: false,
style: PaintStyle::Fill,
cap: StrokeCap::default(),
join: StrokeJoin::default(),
}
}
/// Constructs Paint with default values and the given color.
#[must_use]
pub fn from_color(color: &Color4f) -> Self {
let mut p = Self::new();
p.color4f = color.clone();
p
}
/// Constructs Paint with default values and the given color.
///
/// Sets alpha and RGB used when stroking and filling.
/// The color is four floating point values, unpremultiplied.
/// The color values are interpreted as being in the `color_space`.
/// If `color_space` is None, then color is assumed to be in the
/// `sRGB` color space.
///
/// # Parameters
/// - `color` - unpremultiplied RGBA
/// - `color_space` - `ColorSpace` describing the encoding of color
#[must_use]
pub fn from_color_space(color: &Color4f, color_space: &Option<ColorSpace>) -> Self {
let mut p = Self::new();
p.color4f = color.clone();
p.color_space = color_space.clone();
p
}
/// Sets all Paint contents to their initial values.
///
/// This is equivalent to replacing Paint with the result of `Paint::default()`.
pub fn reset(&mut self) {
*self = Self::new();
}
/// Returns the thickness of the pen used by Paint to outline the shape.
///
/// Returns zero for hairline, greater than zero for pen thickness
#[must_use]
pub const fn get_stroke_width(&self) -> Scalar {
self.stroke_width
}
/// Sets the thickness of the pen used by the paint to outline the shape.
///
/// A stroke-width of zero is treated as "hairline" width. Hairlines are always exactly one
/// pixel wide in device space (their thickness does not change as the canvas is scaled).
/// Negative stroke-widths are invalid; setting a negative width will have no effect.
///
/// # Parameters
/// - `width` - zero thickness for hairline; greater than zero for pen thickness
pub fn set_stroke_width(&mut self, width: Scalar) {
debug_assert!(width >= 0.0);
if width >= 0.0 {
self.stroke_width = width;
}
}
/// Returns the limit at which a sharp corner is drawn beveled.
///
/// Returns zero and greater miter limit
#[must_use]
pub const fn get_stroke_miter(&self) -> Scalar {
self.miter_limit
}
/// Sets the limit at which a sharp corner is drawn beveled.
///
/// Valid values are zero and greater.
/// Has no effect if miter is less than zero.
///
/// # Parameters
/// - `miter` - zero and greater miter limit
pub fn set_stroke_miter(&mut self, miter: Scalar) {
debug_assert!(miter >= 0.0);
if miter >= 0.0 {
self.miter_limit = miter;
}
}
/// Returns true if pixels on the active edges of Path may be drawn with partial transparency.
#[must_use]
pub const fn is_anti_alias(&self) -> bool {
self.anti_alias
}
/// Requests, but does not require, that edge pixels draw opaque or with partial transparency.
pub fn set_anti_alias(&mut self, aa: bool) {
self.anti_alias = aa;
}
/// Returns true if color error may be distributed to smooth color transition.
#[must_use]
pub const fn is_dither(&self) -> bool {
self.dither
}
/// Requests, but does not require, to distribute color error.
pub fn set_dither(&mut self, dither: bool) {
self.dither = dither;
}
/// Returns whether the geometry is filled, stroked, or filled and stroked.
#[must_use]
pub const fn get_style(&self) -> PaintStyle {
self.style
}
/// Sets whether the geometry is filled, stroked, or filled and stroked.
///
/// Has no effect if style is not a legal `PaintStyle` value.
pub fn set_style(&mut self, style: PaintStyle) {
self.style = style;
}
/// Set paint's style to `PaintStyle::Stroke` if true, or `PaintStyle::Fill` if false.
pub fn set_stroke(&mut self, is_stroke: bool) {
self.style = if is_stroke {
PaintStyle::Stroke
} else {
PaintStyle::Fill
};
}
/// Returns the geometry drawn at the beginning and end of strokes.
#[must_use]
pub const fn get_stroke_cap(&self) -> StrokeCap {
self.cap
}
/// Sets the geometry drawn at the beginning and end of strokes.
pub fn set_stroke_cap(&mut self, cap: StrokeCap) {
self.cap = cap;
}
/// Returns the geometry drawn at the corners of strokes.
#[must_use]
pub const fn get_stroke_join(&self) -> StrokeJoin {
self.join
}
/// Sets the geometry drawn at the corners of strokes.
pub fn set_stroke_join(&mut self, join: StrokeJoin) {
self.join = join;
}
/// Retrieves alpha and RGB, unpremultiplied, packed into 32 bits.
///
/// Use helpers `get_alpha()`, `get_red()`, `get_green()`, and `get_blue()`
/// to extract a color component.
#[must_use]
pub fn get_color(&self) -> Color {
(&self.color4f).into()
}
/// Retrieves alpha and RGB, unpremultiplied, as four floating point values.
///
/// RGB are extended `sRGB` values (`sRGB` gamut, and encoded with the `sRGB` transfer function).
#[must_use]
pub const fn get_color4f(&self) -> &Color4f {
&self.color4f
}
/// Sets alpha and RGB used when stroking and filling.
///
/// The color is a 32-bit value, unpremultiplied, packing 8-bit components
/// for alpha, red, blue, and green.
///
/// # Parameters
/// - `color` - unpremultiplied ARGB
pub fn set_color(&mut self, color: Color) {
self.color4f = color.into();
}
/// Sets alpha and RGB used when stroking and filling.
///
/// The color is four floating point values, unpremultiplied.
/// The color values are interpreted as being in the `color_space`.
/// If `color_space` is None , then color is assumed to be in the `sRGB` color space.
///
/// # Parameters
/// - `color` - unpremultiplied RGBA
/// - `color_space` - `ColorSpace` describing the encoding of color
pub fn set_color_space(&mut self, color: &Color4f, color_space: &Option<ColorSpace>) {
self.color4f = color.clone();
self.color_space = color_space.clone();
}
/// Retrieves alpha from the color used when stroking and filling.
///
/// Returns alpha ranging from zero, fully transparent, to one, fully opaque
#[must_use]
pub const fn get_alphaf(&self) -> Scalar {
self.color4f.alpha()
}
/// Helper that scales the alpha by 255.
#[must_use]
#[allow(clippy::cast_sign_loss)]
#[allow(clippy::cast_possible_truncation)]
pub fn get_alpha(&self) -> u8 {
(self.color4f.alpha() * 255.0) as u8
}
/// Replaces alpha, leaving RGB unchanged.
///
/// alpha is a value from 0.0 to 1.0.
/// alpha set to zero makes color fully transparent; a set to 1.0 makes color
/// fully opaque.
pub fn set_alphaf(&mut self, alpha: f32) {
self.color4f.set_alpha(alpha);
}
/// Helper that accepts an int between 0 and 255, and divides it by 255.0
#[allow(clippy::cast_lossless)]
pub fn set_alpha(&mut self, alpha: u8) {
self.color4f.set_alpha(alpha as f32 / 255.0);
}
/// Sets color used when drawing solid fills.
///
/// The color components range from 0 to 255. The color is unpremultiplied;
/// alpha sets the transparency independent of RGB.
///
/// # Parameters
/// - `alpha` - amount of alpha, from fully transparent (0) to fully opaque (255)
/// - `red` - amount of red, from no red (0) to full red (255)
/// - `green` - amount of green, from no green (0) to full green (255)
/// - `blue` - amount of blue, from no blue (0) to full blue (255)
pub fn set_argb(&mut self, alpha: u8, red: u8, green: u8, blue: u8) {
self.set_color(Color::from_argb(alpha, red, green, blue));
}
}
impl Default for Paint {
fn default() -> Self {
Self::new()
}
}
// TODO(Shaohua): Impl PartialEq