diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-19 09:26:03 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-19 09:26:03 +0000 |
commit | 9918693037dce8aa4bb6f08741b6812923486c18 (patch) | |
tree | 21d2b40bec7e6a7ea664acee056eb3d08e15a1cf /vendor/plotters/src/style | |
parent | Releasing progress-linux version 1.75.0+dfsg1-5~progress7.99u1. (diff) | |
download | rustc-9918693037dce8aa4bb6f08741b6812923486c18.tar.xz rustc-9918693037dce8aa4bb6f08741b6812923486c18.zip |
Merging upstream version 1.76.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/plotters/src/style')
-rw-r--r-- | vendor/plotters/src/style/colors/colormaps.rs | 311 | ||||
-rw-r--r-- | vendor/plotters/src/style/colors/mod.rs | 8 | ||||
-rw-r--r-- | vendor/plotters/src/style/font/ab_glyph.rs | 160 | ||||
-rw-r--r-- | vendor/plotters/src/style/font/mod.rs | 26 | ||||
-rw-r--r-- | vendor/plotters/src/style/font/ttf.rs | 4 | ||||
-rw-r--r-- | vendor/plotters/src/style/mod.rs | 3 |
6 files changed, 506 insertions, 6 deletions
diff --git a/vendor/plotters/src/style/colors/colormaps.rs b/vendor/plotters/src/style/colors/colormaps.rs new file mode 100644 index 000000000..50430db0a --- /dev/null +++ b/vendor/plotters/src/style/colors/colormaps.rs @@ -0,0 +1,311 @@ +use crate::style::{HSLColor, RGBAColor, RGBColor}; + +use num_traits::{Float, FromPrimitive, ToPrimitive}; + +/// Converts scalar values to colors. +pub trait ColorMap<ColorType: crate::prelude::Color, FloatType = f32> +where + FloatType: Float, +{ + /// Takes a scalar value 0.0 <= h <= 1.0 and returns the corresponding color. + /// Typically color-scales are named according to which color-type they return. + /// To use upper and lower bounds with ths function see [get_color_normalized](ColorMap::get_color_normalized). + fn get_color(&self, h: FloatType) -> ColorType { + self.get_color_normalized(h, FloatType::zero(), FloatType::one()) + } + + /// A slight abstraction over [get_color](ColorMap::get_color) function where lower and upper bound can be specified. + fn get_color_normalized(&self, h: FloatType, min: FloatType, max: FloatType) -> ColorType; +} + +/// This struct is used to dynamically construct colormaps by giving it a slice of colors. +/// It can then be used when being intantiated, but not with associated functions. +/// ``` +/// use plotters::prelude::{BLACK,BLUE,WHITE,DerivedColorMap,ColorMap}; +/// +/// let derived_colormap = DerivedColorMap::new( +/// &[BLACK, +/// BLUE, +/// WHITE] +/// ); +/// +/// assert_eq!(derived_colormap.get_color(0.0), BLACK); +/// assert_eq!(derived_colormap.get_color(0.5), BLUE); +/// assert_eq!(derived_colormap.get_color(1.0), WHITE); +/// ``` +pub struct DerivedColorMap<ColorType> { + colors: Vec<ColorType>, +} + +impl<ColorType: crate::style::Color + Clone> DerivedColorMap<ColorType> { + /// This function lets the user define a new colormap by simply specifying colors in the correct order. + /// For calculation of the color values, the colors will be spaced evenly apart. + pub fn new(colors: &[ColorType]) -> Self { + DerivedColorMap { + colors: colors.to_vec(), + } + } +} + +macro_rules! calculate_new_color_value( + ($relative_difference:expr, $colors:expr, $index_upper:expr, $index_lower:expr, RGBColor) => { + RGBColor( + // These equations are a very complicated way of writing a simple linear extrapolation with lots of casting between numerical values + // In principle every cast should be safe which is why we choose to unwrap + // (1.0 - r) * color_value_1 + r * color_value_2 + ((FloatType::one() - $relative_difference) * FloatType::from_u8($colors[$index_upper].0).unwrap() + $relative_difference * FloatType::from_u8($colors[$index_lower].0).unwrap()).round().to_u8().unwrap(), + ((FloatType::one() - $relative_difference) * FloatType::from_u8($colors[$index_upper].1).unwrap() + $relative_difference * FloatType::from_u8($colors[$index_lower].1).unwrap()).round().to_u8().unwrap(), + ((FloatType::one() - $relative_difference) * FloatType::from_u8($colors[$index_upper].2).unwrap() + $relative_difference * FloatType::from_u8($colors[$index_lower].2).unwrap()).round().to_u8().unwrap() + ) + }; + ($relative_difference:expr, $colors:expr, $index_upper:expr, $index_lower:expr, RGBAColor) => { + RGBAColor( + // These equations are a very complicated way of writing a simple linear extrapolation with lots of casting between numerical values + // In principle every cast should be safe which is why we choose to unwrap + // (1.0 - r) * color_value_1 + r * color_value_2 + ((FloatType::one() - $relative_difference) * FloatType::from_u8($colors[$index_upper].0).unwrap() + $relative_difference * FloatType::from_u8($colors[$index_lower].0).unwrap()).round().to_u8().unwrap(), + ((FloatType::one() - $relative_difference) * FloatType::from_u8($colors[$index_upper].1).unwrap() + $relative_difference * FloatType::from_u8($colors[$index_lower].1).unwrap()).round().to_u8().unwrap(), + ((FloatType::one() - $relative_difference) * FloatType::from_u8($colors[$index_upper].2).unwrap() + $relative_difference * FloatType::from_u8($colors[$index_lower].2).unwrap()).round().to_u8().unwrap(), + ((FloatType::one() - $relative_difference) * FloatType::from_f64($colors[$index_upper].3).unwrap() + $relative_difference * FloatType::from_f64($colors[$index_lower].3).unwrap()).to_f64().unwrap() + ) + }; + ($relative_difference:expr, $colors:expr, $index_upper:expr, $index_lower:expr, HSLColor) => { + HSLColor( + // These equations are a very complicated way of writing a simple linear extrapolation with lots of casting between numerical values + // In principle every cast should be safe which is why we choose to unwrap + // (1.0 - r) * color_value_1 + r * color_value_2 + ((FloatType::one() - $relative_difference) * FloatType::from_f64($colors[$index_upper].0).unwrap() + $relative_difference * FloatType::from_f64($colors[$index_lower].0).unwrap()).to_f64().unwrap(), + ((FloatType::one() - $relative_difference) * FloatType::from_f64($colors[$index_upper].1).unwrap() + $relative_difference * FloatType::from_f64($colors[$index_lower].1).unwrap()).to_f64().unwrap(), + ((FloatType::one() - $relative_difference) * FloatType::from_f64($colors[$index_upper].2).unwrap() + $relative_difference * FloatType::from_f64($colors[$index_lower].2).unwrap()).to_f64().unwrap(), + ) + }; +); + +fn calculate_relative_difference_index_lower_upper< + FloatType: Float + FromPrimitive + ToPrimitive, +>( + h: FloatType, + min: FloatType, + max: FloatType, + n_colors: usize, +) -> (FloatType, usize, usize) { + // Ensure that we do have a value in bounds + let h = num_traits::clamp(h, min, max); + // Next calculate a normalized value between 0.0 and 1.0 + let t = (h - min) / (max - min); + let approximate_index = + t * (FloatType::from_usize(n_colors).unwrap() - FloatType::one()).max(FloatType::zero()); + // Calculate which index are the two most nearest of the supplied value + let index_lower = approximate_index.floor().to_usize().unwrap(); + let index_upper = approximate_index.ceil().to_usize().unwrap(); + // Calculate the relative difference, ie. is the actual value more towards the color of index_upper or index_lower? + let relative_difference = approximate_index.ceil() - approximate_index; + (relative_difference, index_lower, index_upper) +} + +macro_rules! implement_color_scale_for_derived_color_map{ + ($($color_type:ident),+) => { + $( + impl<FloatType: Float + FromPrimitive + ToPrimitive> ColorMap<$color_type, FloatType> for DerivedColorMap<$color_type> { + fn get_color_normalized(&self, h: FloatType, min: FloatType, max: FloatType) -> $color_type { + let ( + relative_difference, + index_lower, + index_upper + ) = calculate_relative_difference_index_lower_upper( + h, + min, + max, + self.colors.len() + ); + // Interpolate the final color linearly + calculate_new_color_value!( + relative_difference, + self.colors, + index_upper, + index_lower, + $color_type + ) + } + } + )+ + } +} + +implement_color_scale_for_derived_color_map! {RGBAColor, RGBColor, HSLColor} + +macro_rules! count { + () => (0usize); + ($x:tt $($xs:tt)* ) => (1usize + count!($($xs)*)); +} + +macro_rules! define_colors_from_list_of_values_or_directly{ + ($color_type:ident, $(($($color_value:expr),+)),+) => { + [$($color_type($($color_value),+)),+] + }; + ($($color_complete:tt),+) => { + [$($color_complete),+] + }; +} + +macro_rules! implement_linear_interpolation_color_map { + ($color_scale_name:ident, $color_type:ident) => { + impl<FloatType: std::fmt::Debug + Float + FromPrimitive + ToPrimitive> + ColorMap<$color_type, FloatType> for $color_scale_name + { + fn get_color_normalized( + &self, + h: FloatType, + min: FloatType, + max: FloatType, + ) -> $color_type { + let ( + relative_difference, + index_lower, + index_upper + ) = calculate_relative_difference_index_lower_upper( + h, + min, + max, + Self::COLORS.len() + ); + // Interpolate the final color linearly + calculate_new_color_value!( + relative_difference, + Self::COLORS, + index_upper, + index_lower, + $color_type + ) + } + } + + impl $color_scale_name { + #[doc = "Get color value from `"] + #[doc = stringify!($color_scale_name)] + #[doc = "` by supplying a parameter 0.0 <= h <= 1.0"] + pub fn get_color<FloatType: std::fmt::Debug + Float + FromPrimitive + ToPrimitive>( + h: FloatType, + ) -> $color_type { + let color_scale = $color_scale_name {}; + color_scale.get_color(h) + } + + #[doc = "Get color value from `"] + #[doc = stringify!($color_scale_name)] + #[doc = "` by supplying lower and upper bounds min, max and a parameter h where min <= h <= max"] + pub fn get_color_normalized< + FloatType: std::fmt::Debug + Float + FromPrimitive + ToPrimitive, + >( + h: FloatType, + min: FloatType, + max: FloatType, + ) -> $color_type { + let color_scale = $color_scale_name {}; + color_scale.get_color_normalized(h, min, max) + } + } + }; +} + +#[macro_export] +/// Macro to create a new colormap with evenly spaced colors at compile-time. +macro_rules! define_linear_interpolation_color_map{ + ($color_scale_name:ident, $color_type:ident, $doc:expr, $(($($color_value:expr),+)),*) => { + #[doc = $doc] + pub struct $color_scale_name {} + + impl $color_scale_name { + // const COLORS: [$color_type; $number_colors] = [$($color_type($($color_value),+)),+]; + // const COLORS: [$color_type; count!($(($($color_value:expr),+))*)] = [$($color_type($($color_value),+)),+]; + const COLORS: [$color_type; count!($(($($color_value:expr),+))*)] = define_colors_from_list_of_values_or_directly!{$color_type, $(($($color_value),+)),*}; + } + + implement_linear_interpolation_color_map!{$color_scale_name, $color_type} + }; + ($color_scale_name:ident, $color_type:ident, $doc:expr, $($color_complete:tt),+) => { + #[doc = $doc] + pub struct $color_scale_name {} + + impl $color_scale_name { + const COLORS: [$color_type; count!($($color_complete)*)] = define_colors_from_list_of_values_or_directly!{$($color_complete),+}; + } + + implement_linear_interpolation_color_map!{$color_scale_name, $color_type} + } +} + +define_linear_interpolation_color_map! { + ViridisRGBA, + RGBAColor, + "A colormap optimized for visually impaired people (RGBA format). + It is currently the default colormap also used by [matplotlib](https://matplotlib.org/stable/tutorials/colors/colormaps.html). + Read more in this [paper](https://doi.org/10.1371/journal.pone.0199239)", + ( 68, 1, 84, 1.0), + ( 70, 50, 127, 1.0), + ( 54, 92, 141, 1.0), + ( 39, 127, 143, 1.0), + ( 31, 162, 136, 1.0), + ( 74, 194, 110, 1.0), + (160, 219, 57, 1.0), + (254, 232, 37, 1.0) +} + +define_linear_interpolation_color_map! { + ViridisRGB, + RGBColor, + "A colormap optimized for visually impaired people (RGB Format). + It is currently the default colormap also used by [matplotlib](https://matplotlib.org/stable/tutorials/colors/colormaps.html). + Read more in this [paper](https://doi.org/10.1371/journal.pone.0199239)", + ( 68, 1, 84), + ( 70, 50, 127), + ( 54, 92, 141), + ( 39, 127, 143), + ( 31, 162, 136), + ( 74, 194, 110), + (160, 219, 57), + (254, 232, 37) +} + +define_linear_interpolation_color_map! { + BlackWhite, + RGBColor, + "Simple chromatic colormap from black to white.", + ( 0, 0, 0), + (255, 255, 255) +} + +define_linear_interpolation_color_map! { + MandelbrotHSL, + HSLColor, + "Colormap created to replace the one used in the mandelbrot example.", + (0.0, 1.0, 0.5), + (1.0, 1.0, 0.5) +} + +define_linear_interpolation_color_map! { + VulcanoHSL, + HSLColor, + "A vulcanic colormap that display red/orange and black colors", + (2.0/3.0, 1.0, 0.7), + ( 0.0, 1.0, 0.7) +} + +use super::full_palette::*; +define_linear_interpolation_color_map! { + Bone, + RGBColor, + "Dark colormap going from black over blue to white.", + BLACK, + BLUE, + WHITE +} + +define_linear_interpolation_color_map! { + Copper, + RGBColor, + "Friendly black to brown colormap.", + BLACK, + BROWN, + ORANGE +} diff --git a/vendor/plotters/src/style/colors/mod.rs b/vendor/plotters/src/style/colors/mod.rs index 34448ba0b..8aa09a7e1 100644 --- a/vendor/plotters/src/style/colors/mod.rs +++ b/vendor/plotters/src/style/colors/mod.rs @@ -1,8 +1,9 @@ //! Basic predefined colors. use super::{RGBAColor, RGBColor}; -// Macro for allowing dynamic creation of doc attributes. // Taken from https://stackoverflow.com/questions/60905060/prevent-line-break-in-doc-test +/// Macro for allowing dynamic creation of doc attributes. +#[macro_export] macro_rules! doc { { $(#[$m:meta])* @@ -55,5 +56,10 @@ define_color!(CYAN, 0, 255, 255, "Cyan"); define_color!(MAGENTA, 255, 0, 255, "Magenta"); define_color!(TRANSPARENT, 0, 0, 0, 0.0, "Transparent"); +#[cfg(feature = "colormaps")] +/// Colormaps can be used to simply go from a scalar value to a color value which will be more/less +/// intense corresponding to the value of the supplied scalar. +/// These colormaps can also be defined by the user and be used with lower and upper bounds. +pub mod colormaps; #[cfg(feature = "full_palette")] pub mod full_palette; diff --git a/vendor/plotters/src/style/font/ab_glyph.rs b/vendor/plotters/src/style/font/ab_glyph.rs new file mode 100644 index 000000000..42b433444 --- /dev/null +++ b/vendor/plotters/src/style/font/ab_glyph.rs @@ -0,0 +1,160 @@ +use super::{FontData, FontFamily, FontStyle, LayoutBox}; +use ab_glyph::{Font, FontRef, ScaleFont}; +use core::fmt::{self, Display}; +use once_cell::sync::Lazy; +use std::collections::HashMap; +use std::error::Error; +use std::sync::RwLock; + +struct FontMap { + map: HashMap<String, FontRef<'static>>, +} +impl FontMap { + fn new() -> Self { + Self { + map: HashMap::with_capacity(4), + } + } + fn insert(&mut self, style: FontStyle, font: FontRef<'static>) -> Option<FontRef<'static>> { + self.map.insert(style.as_str().to_string(), font) + } + // fn get(&self, style: FontStyle) -> Option<&FontRef<'static>> { + // self.map.get(style.as_str()) + // } + fn get_fallback(&self, style: FontStyle) -> Option<&FontRef<'static>> { + self.map + .get(style.as_str()) + .or_else(|| self.map.get(FontStyle::Normal.as_str())) + } +} + +static FONTS: Lazy<RwLock<HashMap<String, FontMap>>> = Lazy::new(|| RwLock::new(HashMap::new())); +pub struct InvalidFont { + _priv: (), +} + +// Note for future contributors: There is nothing fundamental about the static reference requirement here. +// It would be reasonably easy to add a function which accepts an owned buffer, +// or even a reference counted buffer, instead. +/// Register a font in the fonts table. +/// +/// The `name` parameter gives the name this font shall be referred to +/// in the other APIs, like `"sans-serif"`. +/// +/// Unprovided font styles for a given name will fallback to `FontStyle::Normal` +/// if that is available for that name, when other functions lookup fonts which +/// are registered with this function. +/// +/// The `bytes` parameter should be the complete contents +/// of an OpenType font file, like: +/// ```ignore +/// include_bytes!("FiraGO-Regular.otf") +/// ``` +pub fn register_font( + name: &str, + style: FontStyle, + bytes: &'static [u8], +) -> Result<(), InvalidFont> { + let font = FontRef::try_from_slice(bytes).map_err(|_| InvalidFont { _priv: () })?; + let mut lock = FONTS.write().unwrap(); + lock.entry(name.to_string()) + .or_insert_with(FontMap::new) + .insert(style, font); + Ok(()) +} + +#[derive(Clone)] +pub struct FontDataInternal { + font_ref: FontRef<'static>, +} + +#[derive(Debug, Clone)] +pub enum FontError { + /// No idea what the problem is + Unknown, + /// No font data available for the requested family and style. + FontUnavailable, +} +impl Display for FontError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Since it makes literally no difference to how we'd format + // this, just delegate to the derived Debug formatter. + write!(f, "{:?}", self) + } +} +impl Error for FontError {} + +impl FontData for FontDataInternal { + // TODO: can we rename this to `Error`? + type ErrorType = FontError; + fn new(family: FontFamily<'_>, style: FontStyle) -> Result<Self, Self::ErrorType> { + Ok(Self { + font_ref: FONTS + .read() + .unwrap() + .get(family.as_str()) + .and_then(|fam| fam.get_fallback(style)) + .ok_or(FontError::FontUnavailable)? + .clone(), + }) + } + // TODO: ngl, it makes no sense that this uses the same error type as `new` + fn estimate_layout(&self, size: f64, text: &str) -> Result<LayoutBox, Self::ErrorType> { + let pixel_per_em = size / 1.24; + // let units_per_em = self.font_ref.units_per_em().unwrap(); + let font = self.font_ref.as_scaled(size as f32); + + let mut x_pixels = 0f32; + + let mut prev = None; + for c in text.chars() { + let glyph_id = font.glyph_id(c); + let size = font.h_advance(glyph_id); + x_pixels += size; + if let Some(pc) = prev { + x_pixels += font.kern(pc, glyph_id); + } + prev = Some(glyph_id); + } + + Ok(((0, 0), (x_pixels as i32, pixel_per_em as i32))) + } + fn draw<E, DrawFunc: FnMut(i32, i32, f32) -> Result<(), E>>( + &self, + pos: (i32, i32), + size: f64, + text: &str, + mut draw: DrawFunc, + ) -> Result<Result<(), E>, Self::ErrorType> { + let font = self.font_ref.as_scaled(size as f32); + let mut draw = |x: i32, y: i32, c| { + let (base_x, base_y) = pos; + draw(base_x + x, base_y + y, c) + }; + let mut x_shift = 0f32; + let mut prev = None; + for c in text.chars() { + if let Some(pc) = prev { + x_shift += font.kern(font.glyph_id(pc), font.glyph_id(c)); + } + prev = Some(c); + let glyph = font.scaled_glyph(c); + if let Some(q) = font.outline_glyph(glyph) { + let rect = q.px_bounds(); + let y_shift = ((size as f32) / 2.0 + rect.min.y) as i32; + let x_shift = x_shift as i32; + let mut buf = vec![]; + q.draw(|x, y, c| buf.push((x, y, c))); + for (x, y, c) in buf { + draw(x as i32 + x_shift, y as i32 + y_shift, c).map_err(|_e| { + // Note: If ever `plotters` adds a tracing or logging crate, + // this would be a good place to use it. + FontError::Unknown + })?; + } + } + x_shift += font.h_advance(font.glyph_id(c)); + } + Ok(Ok(())) + } +} diff --git a/vendor/plotters/src/style/font/mod.rs b/vendor/plotters/src/style/font/mod.rs index 305978fd0..d80ee289d 100644 --- a/vendor/plotters/src/style/font/mod.rs +++ b/vendor/plotters/src/style/font/mod.rs @@ -18,13 +18,35 @@ mod ttf; use ttf::FontDataInternal; #[cfg(all( - not(all(target_arch = "wasm32", not(target_os = "wasi"))), + not(target_arch = "wasm32"), + not(target_os = "wasi"), + feature = "ab_glyph" +))] +mod ab_glyph; +#[cfg(all( + not(target_arch = "wasm32"), + not(target_os = "wasi"), + feature = "ab_glyph" +))] +pub use self::ab_glyph::register_font; +#[cfg(all( + not(target_arch = "wasm32"), + not(target_os = "wasi"), + feature = "ab_glyph", not(feature = "ttf") ))] +use self::ab_glyph::FontDataInternal; + +#[cfg(all( + not(all(target_arch = "wasm32", not(target_os = "wasi"))), + not(feature = "ttf"), + not(feature = "ab_glyph") +))] mod naive; #[cfg(all( not(all(target_arch = "wasm32", not(target_os = "wasi"))), - not(feature = "ttf") + not(feature = "ttf"), + not(feature = "ab_glyph") ))] use naive::FontDataInternal; diff --git a/vendor/plotters/src/style/font/ttf.rs b/vendor/plotters/src/style/font/ttf.rs index e6feadc82..345aae4b9 100644 --- a/vendor/plotters/src/style/font/ttf.rs +++ b/vendor/plotters/src/style/font/ttf.rs @@ -82,9 +82,7 @@ impl FontExt { _ => unreachable!(), }; let face = unsafe { - std::mem::transmute::<_, Option<Face<'static>>>( - ttf_parser::Face::from_slice(data, idx).ok(), - ) + std::mem::transmute::<_, Option<Face<'static>>>(ttf_parser::Face::parse(data, idx).ok()) }; Self { inner: font, face } } diff --git a/vendor/plotters/src/style/mod.rs b/vendor/plotters/src/style/mod.rs index 7d7c9ac35..4daa0a85b 100644 --- a/vendor/plotters/src/style/mod.rs +++ b/vendor/plotters/src/style/mod.rs @@ -17,9 +17,12 @@ pub use colors::{BLACK, BLUE, CYAN, GREEN, MAGENTA, RED, TRANSPARENT, WHITE, YEL #[cfg(feature = "full_palette")] pub use colors::full_palette; +#[cfg(all(not(target_arch = "wasm32"), feature = "ab_glyph"))] +pub use font::register_font; pub use font::{ FontDesc, FontError, FontFamily, FontResult, FontStyle, FontTransform, IntoFont, LayoutBox, }; + pub use shape::ShapeStyle; pub use size::{AsRelative, RelativeSize, SizeDesc}; pub use text::text_anchor; |