From d4cc8c775d4c579aeac69ca2d212a604c67043b0 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Sun, 10 Oct 2021 20:26:58 +0200 Subject: Move paint and colors into `geom` --- src/color.rs | 152 ---------------------------------------------- src/eval/state.rs | 2 - src/eval/value.rs | 3 +- src/export/pdf.rs | 5 +- src/geom/mod.rs | 2 + src/geom/paint.rs | 157 ++++++++++++++++++++++++++++++++++++++++++++++++ src/layout/frame.rs | 12 +--- src/lib.rs | 1 - src/library/elements.rs | 4 +- src/library/mod.rs | 1 - src/library/text.rs | 2 +- src/library/utility.rs | 1 - 12 files changed, 166 insertions(+), 176 deletions(-) delete mode 100644 src/color.rs create mode 100644 src/geom/paint.rs (limited to 'src') diff --git a/src/color.rs b/src/color.rs deleted file mode 100644 index 966ab3d6..00000000 --- a/src/color.rs +++ /dev/null @@ -1,152 +0,0 @@ -//! Color handling. - -use std::fmt::{self, Debug, Display, Formatter}; -use std::str::FromStr; - -use serde::{Deserialize, Serialize}; - -/// A color in a dynamic format. -#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] -pub enum Color { - /// An 8-bit RGBA color. - Rgba(RgbaColor), -} - -impl Debug for Color { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - match self { - Self::Rgba(c) => Debug::fmt(c, f), - } - } -} - -/// An 8-bit RGBA color. -#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] -pub struct RgbaColor { - /// Red channel. - pub r: u8, - /// Green channel. - pub g: u8, - /// Blue channel. - pub b: u8, - /// Alpha channel. - pub a: u8, -} - -impl RgbaColor { - /// Black color. - pub const BLACK: Self = Self { r: 0, g: 0, b: 0, a: 255 }; - - /// White color. - pub const WHITE: Self = Self { r: 255, g: 255, b: 255, a: 255 }; - - /// Constructs a new RGBA color. - pub fn new(r: u8, g: u8, b: u8, a: u8) -> Self { - Self { r, g, b, a } - } -} - -impl FromStr for RgbaColor { - type Err = ParseRgbaError; - - /// Constructs a new color from hex strings like the following: - /// - `#aef` (shorthand, with leading hashtag), - /// - `7a03c2` (without alpha), - /// - `abcdefff` (with alpha). - /// - /// Both lower and upper case is fine. - fn from_str(hex_str: &str) -> Result { - let hex_str = hex_str.strip_prefix('#').unwrap_or(hex_str); - if !hex_str.is_ascii() { - return Err(ParseRgbaError); - } - - let len = hex_str.len(); - let long = len == 6 || len == 8; - let short = len == 3 || len == 4; - let alpha = len == 4 || len == 8; - - if !long && !short { - return Err(ParseRgbaError); - } - - let mut values: [u8; 4] = [255; 4]; - - for elem in if alpha { 0 .. 4 } else { 0 .. 3 } { - let item_len = if long { 2 } else { 1 }; - let pos = elem * item_len; - - let item = &hex_str[pos .. (pos + item_len)]; - values[elem] = u8::from_str_radix(item, 16).map_err(|_| ParseRgbaError)?; - - if short { - // Duplicate number for shorthand notation, i.e. `a` -> `aa` - values[elem] += values[elem] * 16; - } - } - - Ok(Self::new(values[0], values[1], values[2], values[3])) - } -} - -impl Debug for RgbaColor { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - if f.alternate() { - write!( - f, - "rgba({:02}, {:02}, {:02}, {:02})", - self.r, self.g, self.b, self.a, - )?; - } else { - write!(f, "#{:02x}{:02x}{:02x}", self.r, self.g, self.b)?; - if self.a != 255 { - write!(f, "{:02x}", self.a)?; - } - } - Ok(()) - } -} - -/// The error when parsing an [`RgbaColor`] from a string fails. -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub struct ParseRgbaError; - -impl Display for ParseRgbaError { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - f.pad("invalid color") - } -} - -impl std::error::Error for ParseRgbaError {} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_parse_color_strings() { - #[track_caller] - fn test(hex: &str, r: u8, g: u8, b: u8, a: u8) { - assert_eq!(RgbaColor::from_str(hex), Ok(RgbaColor::new(r, g, b, a))); - } - - test("f61243ff", 0xf6, 0x12, 0x43, 0xff); - test("b3d8b3", 0xb3, 0xd8, 0xb3, 0xff); - test("fCd2a9AD", 0xfc, 0xd2, 0xa9, 0xad); - test("233", 0x22, 0x33, 0x33, 0xff); - test("111b", 0x11, 0x11, 0x11, 0xbb); - } - - #[test] - fn test_parse_invalid_colors() { - #[track_caller] - fn test(hex: &str) { - assert_eq!(RgbaColor::from_str(hex), Err(ParseRgbaError)); - } - - test("12345"); - test("a5"); - test("14B2AH"); - test("f075ff011"); - } -} diff --git a/src/eval/state.rs b/src/eval/state.rs index a9195199..5d8d3172 100644 --- a/src/eval/state.rs +++ b/src/eval/state.rs @@ -1,11 +1,9 @@ use std::rc::Rc; -use crate::color::{Color, RgbaColor}; use crate::font::{ FontFamily, FontStretch, FontStyle, FontVariant, FontWeight, VerticalFontMetric, }; use crate::geom::*; -use crate::layout::Paint; use crate::paper::{PaperClass, ISO_A4}; /// Defines an set of properties a template can be instantiated with. diff --git a/src/eval/value.rs b/src/eval/value.rs index a1d65a18..2c6167a4 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -4,9 +4,8 @@ use std::fmt::{self, Debug, Formatter}; use std::rc::Rc; use super::{ops, Array, Dict, Function, Str, Template}; -use crate::color::{Color, RgbaColor}; use crate::diag::StrResult; -use crate::geom::{Angle, Fractional, Length, Linear, Relative}; +use crate::geom::{Angle, Color, Fractional, Length, Linear, Relative, RgbaColor}; use crate::syntax::Spanned; use crate::util::EcoString; diff --git a/src/export/pdf.rs b/src/export/pdf.rs index 3abda916..1ac8149d 100644 --- a/src/export/pdf.rs +++ b/src/export/pdf.rs @@ -13,11 +13,10 @@ use pdf_writer::{Content, Filter, Finish, Name, PdfWriter, Rect, Ref, Str, Unico use ttf_parser::{name_id, GlyphId, Tag}; use super::subset; -use crate::color::Color; use crate::font::{find_name, FaceId, FontStore}; -use crate::geom::{self, Em, Length, Size}; +use crate::geom::{self, Color, Em, Length, Paint, Size}; use crate::image::{Image, ImageId, ImageStore}; -use crate::layout::{Element, Frame, Geometry, Paint}; +use crate::layout::{Element, Frame, Geometry}; use crate::Context; /// Export a collection of frames into a PDF document. diff --git a/src/geom/mod.rs b/src/geom/mod.rs index 936f1868..ad92813e 100644 --- a/src/geom/mod.rs +++ b/src/geom/mod.rs @@ -10,6 +10,7 @@ mod fr; mod gen; mod length; mod linear; +mod paint; mod path; mod point; mod relative; @@ -25,6 +26,7 @@ pub use fr::*; pub use gen::*; pub use length::*; pub use linear::*; +pub use paint::*; pub use path::*; pub use point::*; pub use relative::*; diff --git a/src/geom/paint.rs b/src/geom/paint.rs new file mode 100644 index 00000000..2ac11b56 --- /dev/null +++ b/src/geom/paint.rs @@ -0,0 +1,157 @@ +use std::fmt::Display; +use std::str::FromStr; + +use super::*; + +/// How a fill or stroke should be painted. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] +pub enum Paint { + /// A solid color. + Color(Color), +} + +/// A color in a dynamic format. +#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] +pub enum Color { + /// An 8-bit RGBA color. + Rgba(RgbaColor), +} + +impl Debug for Color { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + Self::Rgba(c) => Debug::fmt(c, f), + } + } +} + +/// An 8-bit RGBA color. +#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] +pub struct RgbaColor { + /// Red channel. + pub r: u8, + /// Green channel. + pub g: u8, + /// Blue channel. + pub b: u8, + /// Alpha channel. + pub a: u8, +} + +impl RgbaColor { + /// Black color. + pub const BLACK: Self = Self { r: 0, g: 0, b: 0, a: 255 }; + + /// White color. + pub const WHITE: Self = Self { r: 255, g: 255, b: 255, a: 255 }; + + /// Constructs a new RGBA color. + pub fn new(r: u8, g: u8, b: u8, a: u8) -> Self { + Self { r, g, b, a } + } +} + +impl FromStr for RgbaColor { + type Err = ParseRgbaError; + + /// Constructs a new color from hex strings like the following: + /// - `#aef` (shorthand, with leading hashtag), + /// - `7a03c2` (without alpha), + /// - `abcdefff` (with alpha). + /// + /// Both lower and upper case is fine. + fn from_str(hex_str: &str) -> Result { + let hex_str = hex_str.strip_prefix('#').unwrap_or(hex_str); + if !hex_str.is_ascii() { + return Err(ParseRgbaError); + } + + let len = hex_str.len(); + let long = len == 6 || len == 8; + let short = len == 3 || len == 4; + let alpha = len == 4 || len == 8; + + if !long && !short { + return Err(ParseRgbaError); + } + + let mut values: [u8; 4] = [255; 4]; + + for elem in if alpha { 0 .. 4 } else { 0 .. 3 } { + let item_len = if long { 2 } else { 1 }; + let pos = elem * item_len; + + let item = &hex_str[pos .. (pos + item_len)]; + values[elem] = u8::from_str_radix(item, 16).map_err(|_| ParseRgbaError)?; + + if short { + // Duplicate number for shorthand notation, i.e. `a` -> `aa` + values[elem] += values[elem] * 16; + } + } + + Ok(Self::new(values[0], values[1], values[2], values[3])) + } +} + +impl Debug for RgbaColor { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + if f.alternate() { + write!( + f, + "rgba({:02}, {:02}, {:02}, {:02})", + self.r, self.g, self.b, self.a, + )?; + } else { + write!(f, "#{:02x}{:02x}{:02x}", self.r, self.g, self.b)?; + if self.a != 255 { + write!(f, "{:02x}", self.a)?; + } + } + Ok(()) + } +} + +/// The error when parsing an [`RgbaColor`] from a string fails. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct ParseRgbaError; + +impl Display for ParseRgbaError { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + f.pad("invalid color") + } +} + +impl std::error::Error for ParseRgbaError {} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_parse_color_strings() { + #[track_caller] + fn test(hex: &str, r: u8, g: u8, b: u8, a: u8) { + assert_eq!(RgbaColor::from_str(hex), Ok(RgbaColor::new(r, g, b, a))); + } + + test("f61243ff", 0xf6, 0x12, 0x43, 0xff); + test("b3d8b3", 0xb3, 0xd8, 0xb3, 0xff); + test("fCd2a9AD", 0xfc, 0xd2, 0xa9, 0xad); + test("233", 0x22, 0x33, 0x33, 0xff); + test("111b", 0x11, 0x11, 0x11, 0xbb); + } + + #[test] + fn test_parse_invalid_colors() { + #[track_caller] + fn test(hex: &str) { + assert_eq!(RgbaColor::from_str(hex), Err(ParseRgbaError)); + } + + test("12345"); + test("a5"); + test("14B2AH"); + test("f075ff011"); + } +} diff --git a/src/layout/frame.rs b/src/layout/frame.rs index c74f2abd..0e986fe4 100644 --- a/src/layout/frame.rs +++ b/src/layout/frame.rs @@ -4,9 +4,8 @@ use std::rc::Rc; use serde::{Deserialize, Serialize}; use super::{Constrained, Constraints}; -use crate::color::Color; use crate::font::FaceId; -use crate::geom::{Em, Length, Path, Point, Size}; +use crate::geom::{Em, Length, Paint, Path, Point, Size}; use crate::image::ImageId; /// A finished layout with elements at fixed positions. @@ -36,7 +35,7 @@ pub enum Element { /// Shaped text. Text(Text), /// A geometric shape and the paint which with it should be filled or - /// stroked. + /// stroked (which one depends on the kind of geometry). Geometry(Geometry, Paint), /// A raster image. Image(ImageId, Size), @@ -83,13 +82,6 @@ pub enum Geometry { Path(Path), } -/// How a fill or stroke should be painted. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] -pub enum Paint { - /// A solid color. - Color(Color), -} - impl Frame { /// Create a new, empty frame. #[track_caller] diff --git a/src/lib.rs b/src/lib.rs index fa988207..4ac40d96 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,7 +33,6 @@ pub mod diag; #[macro_use] pub mod eval; -pub mod color; pub mod export; pub mod font; pub mod geom; diff --git a/src/library/elements.rs b/src/library/elements.rs index d1c52ab7..9680dfee 100644 --- a/src/library/elements.rs +++ b/src/library/elements.rs @@ -5,9 +5,7 @@ use decorum::N64; use super::*; use crate::diag::Error; -use crate::layout::{ - BackgroundNode, BackgroundShape, FixedNode, ImageNode, PadNode, Paint, -}; +use crate::layout::{BackgroundNode, BackgroundShape, FixedNode, ImageNode, PadNode}; /// `image`: An image. pub fn image(ctx: &mut EvalContext, args: &mut Args) -> TypResult { diff --git a/src/library/mod.rs b/src/library/mod.rs index 411755bb..d99eb24d 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -16,7 +16,6 @@ pub use utility::*; use std::convert::TryFrom; use std::rc::Rc; -use crate::color::{Color, RgbaColor}; use crate::diag::{At, TypResult}; use crate::eval::{Args, Array, EvalContext, Scope, State, Str, Template, Value}; use crate::font::{FontFamily, FontStretch, FontStyle, FontWeight, VerticalFontMetric}; diff --git a/src/library/text.rs b/src/library/text.rs index 2816ea81..b532b20b 100644 --- a/src/library/text.rs +++ b/src/library/text.rs @@ -1,5 +1,5 @@ use super::*; -use crate::layout::{Decoration, LineDecoration, LineKind, Paint}; +use crate::layout::{Decoration, LineDecoration, LineKind}; /// `font`: Configure the font. pub fn font(ctx: &mut EvalContext, args: &mut Args) -> TypResult { diff --git a/src/library/utility.rs b/src/library/utility.rs index ef7adff0..16a6f5db 100644 --- a/src/library/utility.rs +++ b/src/library/utility.rs @@ -2,7 +2,6 @@ use std::cmp::Ordering; use std::str::FromStr; use super::*; -use crate::color::{Color, RgbaColor}; /// `assert`: Ensure that a condition is fulfilled. pub fn assert(_: &mut EvalContext, args: &mut Args) -> TypResult { -- cgit v1.2.3