//! A [Color Table]( //! https://docs.microsoft.com/en-us/typography/opentype/spec/colr) implementation. // NOTE: Parts of the implementation have been inspired by // [skrifa](https://github.com/googlefonts/fontations/tree/main/skrifa). #[cfg(feature = "variable-fonts")] use crate::delta_set::DeltaSetIndexMap; use crate::parser::{FromData, LazyArray16, Offset, Offset24, Offset32, Stream, F2DOT14}; #[cfg(feature = "variable-fonts")] use crate::var_store::ItemVariationStore; #[cfg(feature = "variable-fonts")] use crate::NormalizedCoordinate; use crate::{cpal, Fixed, LazyArray32, RectF, Transform}; use crate::{GlyphId, RgbaColor}; /// A [base glyph]( /// https://learn.microsoft.com/en-us/typography/opentype/spec/colr#baseglyph-and-layer-records). #[derive(Clone, Copy, Debug)] struct BaseGlyphRecord { glyph_id: GlyphId, first_layer_index: u16, num_layers: u16, } /// A [ClipBox](https://learn.microsoft.com/en-us/typography/opentype/spec/colr#baseglyphlist-layerlist-and-cliplist). pub type ClipBox = RectF; /// A paint. #[derive(Clone, Debug)] pub enum Paint<'a> { /// A paint with a solid color. Solid(RgbaColor), /// A paint with a linear gradient. LinearGradient(LinearGradient<'a>), /// A paint with a radial gradient. RadialGradient(RadialGradient<'a>), /// A paint with a sweep gradient. SweepGradient(SweepGradient<'a>), } /// A [clip record]( /// https://learn.microsoft.com/en-us/typography/opentype/spec/colr#baseglyphlist-layerlist-and-cliplist). #[derive(Clone, Copy, Debug)] struct ClipRecord { /// The first glyph ID for the range covered by this record. pub start_glyph_id: GlyphId, /// The last glyph ID, *inclusive*, for the range covered by this record. pub end_glyph_id: GlyphId, /// The offset to the clip box. pub clip_box_offset: Offset24, } impl FromData for ClipRecord { const SIZE: usize = 7; fn parse(data: &[u8]) -> Option { let mut s = Stream::new(data); Some(ClipRecord { start_glyph_id: s.read::()?, end_glyph_id: s.read::()?, clip_box_offset: s.read::()?, }) } } impl ClipRecord { /// Returns the glyphs range. pub fn glyphs_range(&self) -> core::ops::RangeInclusive { self.start_glyph_id..=self.end_glyph_id } } /// A [clip list]( /// https://learn.microsoft.com/en-us/typography/opentype/spec/colr#baseglyphlist-layerlist-and-cliplist). #[derive(Clone, Copy, Debug, Default)] struct ClipList<'a> { data: &'a [u8], records: LazyArray32<'a, ClipRecord>, } impl<'a> ClipList<'a> { pub fn get( &self, index: u32, #[cfg(feature = "variable-fonts")] variation_data: &VariationData, #[cfg(feature = "variable-fonts")] coords: &[NormalizedCoordinate], ) -> Option { let record = self.records.get(index)?; let offset = record.clip_box_offset.to_usize(); self.data.get(offset..).and_then(|data| { let mut s = Stream::new(data); let format = s.read::()?; #[cfg(not(feature = "variable-fonts"))] let deltas = [0.0, 0.0, 0.0, 0.0]; #[cfg(feature = "variable-fonts")] let deltas = if format == 2 { let mut var_s = s.clone(); var_s.advance(8); let var_index_base = var_s.read::()?; variation_data.read_deltas::<4>(var_index_base, coords) } else { [0.0, 0.0, 0.0, 0.0] }; Some(ClipBox { x_min: s.read::()? as f32 + deltas[0], y_min: s.read::()? as f32 + deltas[1], x_max: s.read::()? as f32 + deltas[2], y_max: s.read::()? as f32 + deltas[3], }) }) } /// Returns a ClipBox by glyph ID. #[inline] pub fn find( &self, glyph_id: GlyphId, #[cfg(feature = "variable-fonts")] variation_data: &VariationData, #[cfg(feature = "variable-fonts")] coords: &[NormalizedCoordinate], ) -> Option { let index = self .records .into_iter() .position(|v| v.glyphs_range().contains(&glyph_id))?; self.get( index as u32, #[cfg(feature = "variable-fonts")] variation_data, #[cfg(feature = "variable-fonts")] coords, ) } } impl FromData for BaseGlyphRecord { const SIZE: usize = 6; fn parse(data: &[u8]) -> Option { let mut s = Stream::new(data); Some(Self { glyph_id: s.read::()?, first_layer_index: s.read::()?, num_layers: s.read::()?, }) } } /// A [layer]( /// https://learn.microsoft.com/en-us/typography/opentype/spec/colr#baseglyph-and-layer-records). #[derive(Clone, Copy, Debug)] struct LayerRecord { glyph_id: GlyphId, palette_index: u16, } impl FromData for LayerRecord { const SIZE: usize = 4; fn parse(data: &[u8]) -> Option { let mut s = Stream::new(data); Some(Self { glyph_id: s.read::()?, palette_index: s.read::()?, }) } } /// A [BaseGlyphPaintRecord]( /// https://learn.microsoft.com/en-us/typography/opentype/spec/colr#baseglyphlist-layerlist-and-cliplist). #[derive(Clone, Copy, Debug)] struct BaseGlyphPaintRecord { glyph_id: GlyphId, paint_table_offset: Offset32, } impl FromData for BaseGlyphPaintRecord { const SIZE: usize = 6; fn parse(data: &[u8]) -> Option { let mut s = Stream::new(data); Some(Self { glyph_id: s.read::()?, paint_table_offset: s.read::()?, }) } } /// A [gradient extend]( /// https://learn.microsoft.com/en-us/typography/opentype/spec/colr#baseglyphlist-layerlist-and-cliplist). #[derive(Clone, Copy, Debug, PartialEq)] pub enum GradientExtend { /// The `Pad` gradient extend mode. Pad, /// The `Repeat` gradient extend mode. Repeat, /// The `Reflect` gradient extend mode. Reflect, } impl FromData for GradientExtend { const SIZE: usize = 1; fn parse(data: &[u8]) -> Option { match data[0] { 0 => Some(Self::Pad), 1 => Some(Self::Repeat), 2 => Some(Self::Reflect), _ => None, } } } /// A [color stop]( /// https://learn.microsoft.com/en-us/typography/opentype/spec/colr#color-references-colorstop-and-colorline). #[derive(Clone, Copy, Debug)] struct ColorStopRaw { stop_offset: F2DOT14, palette_index: u16, alpha: F2DOT14, } impl FromData for ColorStopRaw { const SIZE: usize = 6; fn parse(data: &[u8]) -> Option { let mut s = Stream::new(data); Some(Self { stop_offset: s.read::()?, palette_index: s.read::()?, alpha: s.read::()?, }) } } /// A [var color stop]( /// https://learn.microsoft.com/en-us/typography/opentype/spec/colr#color-references-colorstop-and-colorline). #[cfg(feature = "variable-fonts")] #[derive(Clone, Copy, Debug)] struct VarColorStopRaw { stop_offset: F2DOT14, palette_index: u16, alpha: F2DOT14, var_index_base: u32, } #[cfg(feature = "variable-fonts")] impl FromData for VarColorStopRaw { const SIZE: usize = 10; fn parse(data: &[u8]) -> Option { let mut s = Stream::new(data); Some(Self { stop_offset: s.read::()?, palette_index: s.read::()?, alpha: s.read::()?, var_index_base: s.read::()?, }) } } #[derive(Clone)] struct NonVarColorLine<'a> { extend: GradientExtend, colors: LazyArray16<'a, ColorStopRaw>, palettes: cpal::Table<'a>, foreground_color: RgbaColor, } impl NonVarColorLine<'_> { // TODO: Color stops should be sorted, but hard to do without allocations fn get(&self, palette: u16, index: u16) -> Option { let info = self.colors.get(index)?; let mut color = if info.palette_index == u16::MAX { self.foreground_color } else { self.palettes.get(palette, info.palette_index)? }; color.apply_alpha(info.alpha.to_f32()); Some(ColorStop { stop_offset: info.stop_offset.to_f32(), color, }) } } #[cfg(feature = "variable-fonts")] impl VarColorLine<'_> { // TODO: Color stops should be sorted, but hard to do without allocations fn get( &self, palette: u16, index: u16, #[cfg(feature = "variable-fonts")] variation_data: VariationData, #[cfg(feature = "variable-fonts")] coordinates: &[NormalizedCoordinate], ) -> Option { let info = self.colors.get(index)?; let mut color = if info.palette_index == u16::MAX { self.foreground_color } else { self.palettes.get(palette, info.palette_index)? }; let deltas = variation_data.read_deltas::<2>(info.var_index_base, coordinates); let stop_offset = info.stop_offset.apply_float_delta(deltas[0]); color.apply_alpha(info.alpha.apply_float_delta(deltas[1])); Some(ColorStop { stop_offset, color }) } } #[cfg(feature = "variable-fonts")] #[derive(Clone)] struct VarColorLine<'a> { extend: GradientExtend, colors: LazyArray16<'a, VarColorStopRaw>, palettes: cpal::Table<'a>, foreground_color: RgbaColor, } #[derive(Clone)] enum ColorLine<'a> { #[cfg(feature = "variable-fonts")] VarColorLine(VarColorLine<'a>), NonVarColorLine(NonVarColorLine<'a>), } /// A [gradient extend]( /// https://learn.microsoft.com/en-us/typography/opentype/spec/colr#baseglyphlist-layerlist-and-cliplist). #[derive(Clone, Copy, Debug)] pub struct ColorStop { /// The offset of the color stop. pub stop_offset: f32, /// The color of the color stop. pub color: RgbaColor, } /// A [linear gradient](https://learn.microsoft.com/en-us/typography/opentype/spec/colr#formats-4-and-5-paintlineargradient-paintvarlineargradient) #[derive(Clone)] pub struct LinearGradient<'a> { /// The `x0` value. pub x0: f32, /// The `y0` value. pub y0: f32, /// The `x1` value. pub x1: f32, /// The `y1` value. pub y1: f32, /// The `x2` value. pub x2: f32, /// The `y2` value. pub y2: f32, /// The extend. pub extend: GradientExtend, #[cfg(feature = "variable-fonts")] variation_data: VariationData<'a>, color_line: ColorLine<'a>, } impl<'a> core::fmt::Debug for LinearGradient<'a> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("LinearGradient") .field("x0", &self.x0) .field("y0", &self.y0) .field("x1", &self.x1) .field("y1", &self.y1) .field("x2", &self.x2) .field("y2", &self.y2) .field("extend", &self.extend) .field( "stops", &self.stops( 0, #[cfg(feature = "variable-fonts")] &[], ), ) .finish() } } impl<'a> LinearGradient<'a> { /// Returns an iterator over the stops of the linear gradient. Stops need to be sorted /// manually by the caller. pub fn stops<'b>( &'b self, palette: u16, #[cfg(feature = "variable-fonts")] coords: &'b [NormalizedCoordinate], ) -> GradientStopsIter<'a, 'b> { GradientStopsIter { color_line: &self.color_line, palette, index: 0, #[cfg(feature = "variable-fonts")] variation_data: self.variation_data, #[cfg(feature = "variable-fonts")] coords, } } } /// A [radial gradient](https://learn.microsoft.com/en-us/typography/opentype/spec/colr#formats-6-and-7-paintradialgradient-paintvarradialgradient) #[derive(Clone)] pub struct RadialGradient<'a> { /// The `x0` value. pub x0: f32, /// The `y0` value. pub y0: f32, /// The `r0` value. pub r0: f32, /// The `r1` value. pub r1: f32, /// The `x1` value. pub x1: f32, /// The `y1` value. pub y1: f32, /// The extend. pub extend: GradientExtend, #[cfg(feature = "variable-fonts")] variation_data: VariationData<'a>, color_line: ColorLine<'a>, } impl<'a> core::fmt::Debug for RadialGradient<'a> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("RadialGradient") .field("x0", &self.x0) .field("y0", &self.y0) .field("r0", &self.r0) .field("r1", &self.r1) .field("x1", &self.x1) .field("y1", &self.y1) .field("extend", &self.extend) .field( "stops", &self.stops( 0, #[cfg(feature = "variable-fonts")] &[], ), ) .finish() } } impl<'a> RadialGradient<'a> { /// Returns an iterator over the stops of the radial gradient. Stops need to be sorted /// manually by the caller. pub fn stops<'b>( &'b self, palette: u16, #[cfg(feature = "variable-fonts")] coords: &'a [NormalizedCoordinate], ) -> GradientStopsIter<'a, 'b> { GradientStopsIter { color_line: &self.color_line, palette, index: 0, #[cfg(feature = "variable-fonts")] variation_data: self.variation_data, #[cfg(feature = "variable-fonts")] coords, } } } /// A [sweep gradient](https://learn.microsoft.com/en-us/typography/opentype/spec/colr#formats-8-and-9-paintsweepgradient-paintvarsweepgradient) #[derive(Clone)] pub struct SweepGradient<'a> { /// The x of the center. pub center_x: f32, /// The y of the center. pub center_y: f32, /// The start angle. pub start_angle: f32, /// The end angle. pub end_angle: f32, /// The extend. pub extend: GradientExtend, #[cfg(feature = "variable-fonts")] variation_data: VariationData<'a>, color_line: ColorLine<'a>, } impl<'a> core::fmt::Debug for SweepGradient<'a> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("SweepGradient") .field("center_x", &self.center_x) .field("center_y", &self.center_y) .field("start_angle", &self.start_angle) .field("end_angle", &self.end_angle) .field("extend", &self.extend) .field( "stops", &self.stops( 0, #[cfg(feature = "variable-fonts")] &[], ), ) .finish() } } impl<'a> SweepGradient<'a> { // TODO: Make API nicer so that variable coordinates don't // need to be passed by the caller (same for radial and linear gradient) /// Returns an iterator over the stops of the sweep gradient. Stops need to be sorted /// manually by the caller. pub fn stops<'b>( &'b self, palette: u16, #[cfg(feature = "variable-fonts")] coords: &'a [NormalizedCoordinate], ) -> GradientStopsIter<'a, 'b> { GradientStopsIter { color_line: &self.color_line, palette, index: 0, #[cfg(feature = "variable-fonts")] variation_data: self.variation_data, #[cfg(feature = "variable-fonts")] coords, } } } /// An iterator over stops of a gradient. #[derive(Clone, Copy)] pub struct GradientStopsIter<'a, 'b> { color_line: &'b ColorLine<'a>, palette: u16, index: u16, #[cfg(feature = "variable-fonts")] variation_data: VariationData<'a>, #[cfg(feature = "variable-fonts")] coords: &'b [NormalizedCoordinate], } impl Iterator for GradientStopsIter<'_, '_> { type Item = ColorStop; fn next(&mut self) -> Option { let len = match self.color_line { #[cfg(feature = "variable-fonts")] ColorLine::VarColorLine(vcl) => vcl.colors.len(), ColorLine::NonVarColorLine(nvcl) => nvcl.colors.len(), }; if self.index == len { return None; } let index = self.index; self.index = self.index.checked_add(1)?; match self.color_line { #[cfg(feature = "variable-fonts")] ColorLine::VarColorLine(vcl) => { vcl.get(self.palette, index, self.variation_data, self.coords) } ColorLine::NonVarColorLine(nvcl) => nvcl.get(self.palette, index), } } } impl core::fmt::Debug for GradientStopsIter<'_, '_> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_list().entries(*self).finish() } } /// A [composite mode](https://learn.microsoft.com/en-us/typography/opentype/spec/colr#format-32-paintcomposite) #[derive(Clone, Copy, PartialEq, Debug)] pub enum CompositeMode { /// The composite mode 'Clear'. Clear, /// The composite mode 'Source'. Source, /// The composite mode 'Destination'. Destination, /// The composite mode 'SourceOver'. SourceOver, /// The composite mode 'DestinationOver'. DestinationOver, /// The composite mode 'SourceIn'. SourceIn, /// The composite mode 'DestinationIn'. DestinationIn, /// The composite mode 'SourceOut'. SourceOut, /// The composite mode 'DestinationOut'. DestinationOut, /// The composite mode 'SourceAtop'. SourceAtop, /// The composite mode 'DestinationAtop'. DestinationAtop, /// The composite mode 'Xor'. Xor, /// The composite mode 'Plus'. Plus, /// The composite mode 'Screen'. Screen, /// The composite mode 'Overlay'. Overlay, /// The composite mode 'Darken'. Darken, /// The composite mode 'Lighten'. Lighten, /// The composite mode 'ColorDodge'. ColorDodge, /// The composite mode 'ColorBurn'. ColorBurn, /// The composite mode 'HardLight'. HardLight, /// The composite mode 'SoftLight'. SoftLight, /// The composite mode 'Difference'. Difference, /// The composite mode 'Exclusion'. Exclusion, /// The composite mode 'Multiply'. Multiply, /// The composite mode 'Hue'. Hue, /// The composite mode 'Saturation'. Saturation, /// The composite mode 'Color'. Color, /// The composite mode 'Luminosity'. Luminosity, } impl FromData for CompositeMode { const SIZE: usize = 1; fn parse(data: &[u8]) -> Option { match data[0] { 0 => Some(Self::Clear), 1 => Some(Self::Source), 2 => Some(Self::Destination), 3 => Some(Self::SourceOver), 4 => Some(Self::DestinationOver), 5 => Some(Self::SourceIn), 6 => Some(Self::DestinationIn), 7 => Some(Self::SourceOut), 8 => Some(Self::DestinationOut), 9 => Some(Self::SourceAtop), 10 => Some(Self::DestinationAtop), 11 => Some(Self::Xor), 12 => Some(Self::Plus), 13 => Some(Self::Screen), 14 => Some(Self::Overlay), 15 => Some(Self::Darken), 16 => Some(Self::Lighten), 17 => Some(Self::ColorDodge), 18 => Some(Self::ColorBurn), 19 => Some(Self::HardLight), 20 => Some(Self::SoftLight), 21 => Some(Self::Difference), 22 => Some(Self::Exclusion), 23 => Some(Self::Multiply), 24 => Some(Self::Hue), 25 => Some(Self::Saturation), 26 => Some(Self::Color), 27 => Some(Self::Luminosity), _ => None, } } } /// A trait for color glyph painting. /// /// See [COLR](https://learn.microsoft.com/en-us/typography/opentype/spec/colr) for details. pub trait Painter<'a> { /// Outline a glyph and store it. fn outline_glyph(&mut self, glyph_id: GlyphId); /// Paint the stored outline using the provided color. fn paint(&mut self, paint: Paint<'a>); /// Push a new clip path using the currently stored outline. fn push_clip(&mut self); /// Push a new clip path using the clip box. fn push_clip_box(&mut self, clipbox: ClipBox); /// Pop the last clip path. fn pop_clip(&mut self); /// Push a new layer with the given composite mode. fn push_layer(&mut self, mode: CompositeMode); /// Pop the last layer. fn pop_layer(&mut self); // TODO: Unify transforms into one callback. /// Push a translation transform. fn push_translate(&mut self, tx: f32, ty: f32); /// Push a scaling transform. fn push_scale(&mut self, sx: f32, sy: f32); /// Push a rotation transform. fn push_rotate(&mut self, angle: f32); /// Push a skewing transform. fn push_skew(&mut self, skew_x: f32, skew_y: f32); /// Push a transform. fn push_transform(&mut self, transform: Transform); /// Pop the last transform. fn pop_transform(&mut self); } /// A [Color Table]( /// https://docs.microsoft.com/en-us/typography/opentype/spec/colr). /// /// Currently, only version 0 is supported. #[derive(Clone, Copy, Debug)] pub struct Table<'a> { pub(crate) palettes: cpal::Table<'a>, data: &'a [u8], version: u8, // v0 base_glyphs: LazyArray16<'a, BaseGlyphRecord>, layers: LazyArray16<'a, LayerRecord>, // v1 base_glyph_paints_offset: Offset32, base_glyph_paints: LazyArray32<'a, BaseGlyphPaintRecord>, layer_paint_offsets_offset: Offset32, layer_paint_offsets: LazyArray32<'a, Offset32>, clip_list_offsets_offset: Offset32, clip_list: ClipList<'a>, #[cfg(feature = "variable-fonts")] var_index_map: Option>, #[cfg(feature = "variable-fonts")] item_variation_store: Option>, } impl<'a> Table<'a> { /// Parses a table from raw data. pub fn parse(palettes: cpal::Table<'a>, data: &'a [u8]) -> Option { let mut s = Stream::new(data); let version = s.read::()?; if version > 1 { return None; } let num_base_glyphs = s.read::()?; let base_glyphs_offset = s.read::()?; let layers_offset = s.read::()?; let num_layers = s.read::()?; let base_glyphs = Stream::new_at(data, base_glyphs_offset.to_usize())? .read_array16::(num_base_glyphs)?; let layers = Stream::new_at(data, layers_offset.to_usize())? .read_array16::(num_layers)?; let mut table = Self { version: version as u8, data, palettes, base_glyphs, layers, base_glyph_paints_offset: Offset32(0), // the actual value doesn't matter base_glyph_paints: LazyArray32::default(), layer_paint_offsets_offset: Offset32(0), layer_paint_offsets: LazyArray32::default(), clip_list_offsets_offset: Offset32(0), clip_list: ClipList::default(), #[cfg(feature = "variable-fonts")] item_variation_store: None, #[cfg(feature = "variable-fonts")] var_index_map: None, }; if version == 0 { return Some(table); } table.base_glyph_paints_offset = s.read::()?; let layer_list_offset = s.read::>()?; let clip_list_offset = s.read::>()?; #[cfg(feature = "variable-fonts")] let var_index_map_offset = s.read::>()?; #[cfg(feature = "variable-fonts")] let item_variation_offset = s.read::>()?; { let mut s = Stream::new_at(data, table.base_glyph_paints_offset.to_usize())?; let count = s.read::()?; table.base_glyph_paints = s.read_array32::(count)?; } if let Some(offset) = layer_list_offset { table.layer_paint_offsets_offset = offset; let mut s = Stream::new_at(data, offset.to_usize())?; let count = s.read::()?; table.layer_paint_offsets = s.read_array32::(count)?; } if let Some(offset) = clip_list_offset { table.clip_list_offsets_offset = offset; let clip_data = data.get(offset.to_usize()..)?; let mut s = Stream::new(clip_data); s.skip::(); // Format let count = s.read::()?; table.clip_list = ClipList { data: clip_data, records: s.read_array32::(count)?, }; } #[cfg(feature = "variable-fonts")] { if let Some(offset) = item_variation_offset { let item_var_data = data.get(offset.to_usize()..)?; let s = Stream::new(item_var_data); let var_store = ItemVariationStore::parse(s)?; table.item_variation_store = Some(var_store); } } #[cfg(feature = "variable-fonts")] { if let Some(offset) = var_index_map_offset { let var_index_map_data = data.get(offset.to_usize()..)?; let var_index_map = DeltaSetIndexMap::new(var_index_map_data); table.var_index_map = Some(var_index_map); } } Some(table) } /// Returns `true` if the current table has version 0. /// /// A simple table can only emit `outline_glyph` and `paint` /// [`Painter`] methods. pub fn is_simple(&self) -> bool { self.version == 0 } fn get_v0(&self, glyph_id: GlyphId) -> Option { self.base_glyphs .binary_search_by(|base| base.glyph_id.cmp(&glyph_id)) .map(|v| v.1) } fn get_v1(&self, glyph_id: GlyphId) -> Option { self.base_glyph_paints .binary_search_by(|base| base.glyph_id.cmp(&glyph_id)) .map(|v| v.1) } #[cfg(feature = "variable-fonts")] fn variation_data(&self) -> VariationData<'a> { VariationData { variation_store: self.item_variation_store, delta_map: self.var_index_map, } } /// Whether the table contains a definition for the given glyph. pub fn contains(&self, glyph_id: GlyphId) -> bool { self.get_v1(glyph_id).is_some() || self.get_v0(glyph_id).is_some() } // This method should only be called from outside, not from within `colr.rs`. // From inside, you always should call paint_impl, so that the recursion stack can // be passed on and any kind of recursion can be prevented. /// Paints the color glyph. pub fn paint( &self, glyph_id: GlyphId, palette: u16, painter: &mut dyn Painter<'a>, #[cfg(feature = "variable-fonts")] coords: &[NormalizedCoordinate], foreground_color: RgbaColor, ) -> Option<()> { let mut recursion_stack = RecursionStack { stack: [0; 64], len: 0, }; self.paint_impl( glyph_id, palette, painter, &mut recursion_stack, #[cfg(feature = "variable-fonts")] coords, foreground_color, ) } fn paint_impl( &self, glyph_id: GlyphId, palette: u16, painter: &mut dyn Painter<'a>, recusion_stack: &mut RecursionStack, #[cfg(feature = "variable-fonts")] coords: &[NormalizedCoordinate], foreground_color: RgbaColor, ) -> Option<()> { if let Some(base) = self.get_v1(glyph_id) { self.paint_v1( base, palette, painter, recusion_stack, #[cfg(feature = "variable-fonts")] coords, foreground_color, ) } else if let Some(base) = self.get_v0(glyph_id) { self.paint_v0(base, palette, painter, foreground_color) } else { None } } fn paint_v0( &self, base: BaseGlyphRecord, palette: u16, painter: &mut dyn Painter, foreground_color: RgbaColor, ) -> Option<()> { let start = base.first_layer_index; let end = start.checked_add(base.num_layers)?; let layers = self.layers.slice(start..end)?; for layer in layers { if layer.palette_index == 0xFFFF { // A special case. painter.outline_glyph(layer.glyph_id); painter.paint(Paint::Solid(foreground_color)); } else { let color = self.palettes.get(palette, layer.palette_index)?; painter.outline_glyph(layer.glyph_id); painter.paint(Paint::Solid(color)); } } Some(()) } fn paint_v1( &self, base: BaseGlyphPaintRecord, palette: u16, painter: &mut dyn Painter<'a>, recursion_stack: &mut RecursionStack, #[cfg(feature = "variable-fonts")] coords: &[NormalizedCoordinate], foreground_color: RgbaColor, ) -> Option<()> { let clip_box = self.clip_list.find( base.glyph_id, #[cfg(feature = "variable-fonts")] &self.variation_data(), #[cfg(feature = "variable-fonts")] coords, ); if let Some(clip_box) = clip_box { painter.push_clip_box(clip_box); } self.parse_paint( self.base_glyph_paints_offset.to_usize() + base.paint_table_offset.to_usize(), palette, painter, recursion_stack, #[cfg(feature = "variable-fonts")] coords, foreground_color, ); if clip_box.is_some() { painter.pop_clip(); } Some(()) } fn parse_paint( &self, offset: usize, palette: u16, painter: &mut dyn Painter<'a>, recursion_stack: &mut RecursionStack, #[cfg(feature = "variable-fonts")] coords: &[NormalizedCoordinate], foreground_color: RgbaColor, ) -> Option<()> { let mut s = Stream::new_at(self.data, offset)?; let format = s.read::()?; // Cycle detected if recursion_stack.contains(offset) { return None; } recursion_stack.push(offset).ok()?; let result = self.parse_paint_impl( offset, palette, painter, recursion_stack, &mut s, format, #[cfg(feature = "variable-fonts")] coords, foreground_color, ); recursion_stack.pop(); result } fn parse_paint_impl( &self, offset: usize, palette: u16, painter: &mut dyn Painter<'a>, recursion_stack: &mut RecursionStack, s: &mut Stream, format: u8, #[cfg(feature = "variable-fonts")] coords: &[NormalizedCoordinate], foreground_color: RgbaColor, ) -> Option<()> { match format { 1 => { // PaintColrLayers let layers_count = s.read::()?; let first_layer_index = s.read::()?; for i in 0..layers_count { let index = first_layer_index.checked_add(u32::from(i))?; let paint_offset = self.layer_paint_offsets.get(index)?; self.parse_paint( self.layer_paint_offsets_offset.to_usize() + paint_offset.to_usize(), palette, painter, recursion_stack, #[cfg(feature = "variable-fonts")] coords, foreground_color, ); } } 2 => { // PaintSolid let palette_index = s.read::()?; let alpha = s.read::()?; let mut color = if palette_index == u16::MAX { foreground_color } else { self.palettes.get(palette, palette_index)? }; color.apply_alpha(alpha.to_f32()); painter.paint(Paint::Solid(color)); } #[cfg(feature = "variable-fonts")] 3 => { // PaintVarSolid let palette_index = s.read::()?; let alpha = s.read::()?; let var_index_base = s.read::()?; let deltas = self .variation_data() .read_deltas::<1>(var_index_base, coords); let mut color = if palette_index == u16::MAX { foreground_color } else { self.palettes.get(palette, palette_index)? }; color.apply_alpha(alpha.apply_float_delta(deltas[0])); painter.paint(Paint::Solid(color)); } 4 => { // PaintLinearGradient let color_line_offset = s.read::()?; let color_line = self.parse_color_line(offset + color_line_offset.to_usize(), foreground_color)?; painter.paint(Paint::LinearGradient(LinearGradient { x0: s.read::()? as f32, y0: s.read::()? as f32, x1: s.read::()? as f32, y1: s.read::()? as f32, x2: s.read::()? as f32, y2: s.read::()? as f32, extend: color_line.extend, #[cfg(feature = "variable-fonts")] variation_data: self.variation_data(), color_line: ColorLine::NonVarColorLine(color_line), })) } #[cfg(feature = "variable-fonts")] 5 => { // PaintVarLinearGradient let var_color_line_offset = s.read::()?; let color_line = self.parse_var_color_line( offset + var_color_line_offset.to_usize(), foreground_color, )?; let mut var_s = s.clone(); var_s.advance(12); let var_index_base = var_s.read::()?; let deltas = self .variation_data() .read_deltas::<6>(var_index_base, coords); painter.paint(Paint::LinearGradient(LinearGradient { x0: s.read::()? as f32 + deltas[0], y0: s.read::()? as f32 + deltas[1], x1: s.read::()? as f32 + deltas[2], y1: s.read::()? as f32 + deltas[3], x2: s.read::()? as f32 + deltas[4], y2: s.read::()? as f32 + deltas[5], extend: color_line.extend, variation_data: self.variation_data(), color_line: ColorLine::VarColorLine(color_line), })) } 6 => { // PaintRadialGradient let color_line_offset = s.read::()?; let color_line = self.parse_color_line(offset + color_line_offset.to_usize(), foreground_color)?; painter.paint(Paint::RadialGradient(RadialGradient { x0: s.read::()? as f32, y0: s.read::()? as f32, r0: s.read::()? as f32, x1: s.read::()? as f32, y1: s.read::()? as f32, r1: s.read::()? as f32, extend: color_line.extend, #[cfg(feature = "variable-fonts")] variation_data: self.variation_data(), color_line: ColorLine::NonVarColorLine(color_line), })) } #[cfg(feature = "variable-fonts")] 7 => { // PaintVarRadialGradient let color_line_offset = s.read::()?; let color_line = self.parse_var_color_line( offset + color_line_offset.to_usize(), foreground_color, )?; let mut var_s = s.clone(); var_s.advance(12); let var_index_base = var_s.read::()?; let deltas = self .variation_data() .read_deltas::<6>(var_index_base, coords); painter.paint(Paint::RadialGradient(RadialGradient { x0: s.read::()? as f32 + deltas[0], y0: s.read::()? as f32 + deltas[1], r0: s.read::()? as f32 + deltas[2], x1: s.read::()? as f32 + deltas[3], y1: s.read::()? as f32 + deltas[4], r1: s.read::()? as f32 + deltas[5], extend: color_line.extend, variation_data: self.variation_data(), color_line: ColorLine::VarColorLine(color_line), })) } 8 => { // PaintSweepGradient let color_line_offset = s.read::()?; let color_line = self.parse_color_line(offset + color_line_offset.to_usize(), foreground_color)?; painter.paint(Paint::SweepGradient(SweepGradient { center_x: s.read::()? as f32, center_y: s.read::()? as f32, start_angle: s.read::()?.to_f32(), end_angle: s.read::()?.to_f32(), extend: color_line.extend, color_line: ColorLine::NonVarColorLine(color_line), #[cfg(feature = "variable-fonts")] variation_data: self.variation_data(), })) } #[cfg(feature = "variable-fonts")] 9 => { // PaintVarSweepGradient let color_line_offset = s.read::()?; let color_line = self.parse_var_color_line( offset + color_line_offset.to_usize(), foreground_color, )?; let mut var_s = s.clone(); var_s.advance(8); let var_index_base = var_s.read::()?; let deltas = self .variation_data() .read_deltas::<4>(var_index_base, coords); painter.paint(Paint::SweepGradient(SweepGradient { center_x: s.read::()? as f32 + deltas[0], center_y: s.read::()? as f32 + deltas[1], start_angle: s.read::()?.apply_float_delta(deltas[2]), end_angle: s.read::()?.apply_float_delta(deltas[3]), extend: color_line.extend, color_line: ColorLine::VarColorLine(color_line), variation_data: self.variation_data(), })) } 10 => { // PaintGlyph let paint_offset = s.read::()?; let glyph_id = s.read::()?; painter.outline_glyph(glyph_id); painter.push_clip(); self.parse_paint( offset + paint_offset.to_usize(), palette, painter, recursion_stack, #[cfg(feature = "variable-fonts")] coords, foreground_color, ); painter.pop_clip(); } 11 => { // PaintColrGlyph let glyph_id = s.read::()?; self.paint_impl( glyph_id, palette, painter, recursion_stack, #[cfg(feature = "variable-fonts")] coords, foreground_color, ); } 12 => { // PaintTransform let paint_offset = s.read::()?; let ts_offset = s.read::()?; let mut s = Stream::new_at(self.data, offset + ts_offset.to_usize())?; let ts = Transform { a: s.read::().map(|n| n.0)?, b: s.read::().map(|n| n.0)?, c: s.read::().map(|n| n.0)?, d: s.read::().map(|n| n.0)?, e: s.read::().map(|n| n.0)?, f: s.read::().map(|n| n.0)?, }; painter.push_transform(ts); self.parse_paint( offset + paint_offset.to_usize(), palette, painter, recursion_stack, #[cfg(feature = "variable-fonts")] coords, foreground_color, ); painter.pop_transform(); } #[cfg(feature = "variable-fonts")] 13 => { // PaintVarTransform let paint_offset = s.read::()?; let ts_offset = s.read::()?; let mut s = Stream::new_at(self.data, offset + ts_offset.to_usize())?; let mut var_s = s.clone(); var_s.advance(24); let var_index_base = var_s.read::()?; let deltas = self .variation_data() .read_deltas::<6>(var_index_base, coords); let ts = Transform { a: s.read::()?.apply_float_delta(deltas[0]), b: s.read::()?.apply_float_delta(deltas[1]), c: s.read::()?.apply_float_delta(deltas[2]), d: s.read::()?.apply_float_delta(deltas[3]), e: s.read::()?.apply_float_delta(deltas[4]), f: s.read::()?.apply_float_delta(deltas[5]), }; painter.push_transform(ts); self.parse_paint( offset + paint_offset.to_usize(), palette, painter, recursion_stack, coords, foreground_color, ); painter.pop_transform(); } 14 => { // PaintTranslate let paint_offset = s.read::()?; let tx = f32::from(s.read::()?); let ty = f32::from(s.read::()?); painter.push_translate(tx, ty); self.parse_paint( offset + paint_offset.to_usize(), palette, painter, recursion_stack, #[cfg(feature = "variable-fonts")] coords, foreground_color, ); painter.pop_transform(); } #[cfg(feature = "variable-fonts")] 15 => { // PaintVarTranslate let paint_offset = s.read::()?; let mut var_s = s.clone(); var_s.advance(4); let var_index_base = var_s.read::()?; let deltas = self .variation_data() .read_deltas::<2>(var_index_base, coords); let tx = f32::from(s.read::()?) + deltas[0]; let ty = f32::from(s.read::()?) + deltas[1]; painter.push_translate(tx, ty); self.parse_paint( offset + paint_offset.to_usize(), palette, painter, recursion_stack, coords, foreground_color, ); painter.pop_transform(); } 16 => { // PaintScale let paint_offset = s.read::()?; let sx = s.read::()?.to_f32(); let sy = s.read::()?.to_f32(); painter.push_scale(sx, sy); self.parse_paint( offset + paint_offset.to_usize(), palette, painter, recursion_stack, #[cfg(feature = "variable-fonts")] coords, foreground_color, ); painter.pop_transform(); } #[cfg(feature = "variable-fonts")] 17 => { // PaintVarScale let paint_offset = s.read::()?; let mut var_s = s.clone(); var_s.advance(4); let var_index_base = var_s.read::()?; let deltas = self .variation_data() .read_deltas::<2>(var_index_base, coords); let sx = s.read::()?.apply_float_delta(deltas[0]); let sy = s.read::()?.apply_float_delta(deltas[1]); painter.push_scale(sx, sy); self.parse_paint( offset + paint_offset.to_usize(), palette, painter, recursion_stack, coords, foreground_color, ); painter.pop_transform(); } 18 => { // PaintScaleAroundCenter let paint_offset = s.read::()?; let sx = s.read::()?.to_f32(); let sy = s.read::()?.to_f32(); let center_x = f32::from(s.read::()?); let center_y = f32::from(s.read::()?); painter.push_translate(center_x, center_y); painter.push_scale(sx, sy); painter.push_translate(-center_x, -center_y); self.parse_paint( offset + paint_offset.to_usize(), palette, painter, recursion_stack, #[cfg(feature = "variable-fonts")] coords, foreground_color, ); painter.pop_transform(); painter.pop_transform(); painter.pop_transform(); } #[cfg(feature = "variable-fonts")] 19 => { // PaintVarScaleAroundCenter let paint_offset = s.read::()?; let mut var_s = s.clone(); var_s.advance(8); let var_index_base = var_s.read::()?; let deltas = self .variation_data() .read_deltas::<4>(var_index_base, coords); let sx = s.read::()?.apply_float_delta(deltas[0]); let sy = s.read::()?.apply_float_delta(deltas[1]); let center_x = f32::from(s.read::()?) + deltas[2]; let center_y = f32::from(s.read::()?) + deltas[3]; painter.push_translate(center_x, center_y); painter.push_scale(sx, sy); painter.push_translate(-center_x, -center_y); self.parse_paint( offset + paint_offset.to_usize(), palette, painter, recursion_stack, coords, foreground_color, ); painter.pop_transform(); painter.pop_transform(); painter.pop_transform(); } 20 => { // PaintScaleUniform let paint_offset = s.read::()?; let scale = s.read::()?.to_f32(); painter.push_scale(scale, scale); self.parse_paint( offset + paint_offset.to_usize(), palette, painter, recursion_stack, #[cfg(feature = "variable-fonts")] coords, foreground_color, ); painter.pop_transform(); } #[cfg(feature = "variable-fonts")] 21 => { // PaintVarScaleUniform let paint_offset = s.read::()?; let mut var_s = s.clone(); var_s.advance(2); let var_index_base = var_s.read::()?; let deltas = self .variation_data() .read_deltas::<1>(var_index_base, coords); let scale = s.read::()?.apply_float_delta(deltas[0]); painter.push_scale(scale, scale); self.parse_paint( offset + paint_offset.to_usize(), palette, painter, recursion_stack, coords, foreground_color, ); painter.pop_transform(); } 22 => { // PaintScaleUniformAroundCenter let paint_offset = s.read::()?; let scale = s.read::()?.to_f32(); let center_x = f32::from(s.read::()?); let center_y = f32::from(s.read::()?); painter.push_translate(center_x, center_y); painter.push_scale(scale, scale); painter.push_translate(-center_x, -center_y); self.parse_paint( offset + paint_offset.to_usize(), palette, painter, recursion_stack, #[cfg(feature = "variable-fonts")] coords, foreground_color, ); painter.pop_transform(); painter.pop_transform(); painter.pop_transform(); } #[cfg(feature = "variable-fonts")] 23 => { // PaintVarScaleUniformAroundCenter let paint_offset = s.read::()?; let mut var_s = s.clone(); var_s.advance(6); let var_index_base = var_s.read::()?; let deltas = self .variation_data() .read_deltas::<3>(var_index_base, coords); let scale = s.read::()?.apply_float_delta(deltas[0]); let center_x = f32::from(s.read::()?) + deltas[1]; let center_y = f32::from(s.read::()?) + deltas[2]; painter.push_translate(center_x, center_y); painter.push_scale(scale, scale); painter.push_translate(-center_x, -center_y); self.parse_paint( offset + paint_offset.to_usize(), palette, painter, recursion_stack, coords, foreground_color, ); painter.pop_transform(); painter.pop_transform(); painter.pop_transform(); } 24 => { // PaintRotate let paint_offset = s.read::()?; let angle = s.read::()?.to_f32(); painter.push_rotate(angle); self.parse_paint( offset + paint_offset.to_usize(), palette, painter, recursion_stack, #[cfg(feature = "variable-fonts")] coords, foreground_color, ); painter.pop_transform(); } #[cfg(feature = "variable-fonts")] 25 => { // PaintVarRotate let paint_offset = s.read::()?; let mut var_s = s.clone(); var_s.advance(2); let var_index_base = var_s.read::()?; let deltas = self .variation_data() .read_deltas::<1>(var_index_base, coords); let angle = s.read::()?.apply_float_delta(deltas[0]); painter.push_rotate(angle); self.parse_paint( offset + paint_offset.to_usize(), palette, painter, recursion_stack, coords, foreground_color, ); painter.pop_transform(); } 26 => { // PaintRotateAroundCenter let paint_offset = s.read::()?; let angle = s.read::()?.to_f32(); let center_x = f32::from(s.read::()?); let center_y = f32::from(s.read::()?); painter.push_translate(center_x, center_y); painter.push_rotate(angle); painter.push_translate(-center_x, -center_y); self.parse_paint( offset + paint_offset.to_usize(), palette, painter, recursion_stack, #[cfg(feature = "variable-fonts")] coords, foreground_color, ); painter.pop_transform(); painter.pop_transform(); painter.pop_transform(); } #[cfg(feature = "variable-fonts")] 27 => { // PaintVarRotateAroundCenter let paint_offset = s.read::()?; let mut var_s = s.clone(); var_s.advance(6); let var_index_base = var_s.read::()?; let deltas = self .variation_data() .read_deltas::<3>(var_index_base, coords); let angle = s.read::()?.apply_float_delta(deltas[0]); let center_x = f32::from(s.read::()?) + deltas[1]; let center_y = f32::from(s.read::()?) + deltas[2]; painter.push_translate(center_x, center_y); painter.push_rotate(angle); painter.push_translate(-center_x, -center_y); self.parse_paint( offset + paint_offset.to_usize(), palette, painter, recursion_stack, coords, foreground_color, ); painter.pop_transform(); painter.pop_transform(); painter.pop_transform(); } 28 => { // PaintSkew let paint_offset = s.read::()?; let skew_x = s.read::()?.to_f32(); let skew_y = s.read::()?.to_f32(); painter.push_skew(skew_x, skew_y); self.parse_paint( offset + paint_offset.to_usize(), palette, painter, recursion_stack, #[cfg(feature = "variable-fonts")] coords, foreground_color, ); painter.pop_transform(); } #[cfg(feature = "variable-fonts")] 29 => { // PaintVarSkew let paint_offset = s.read::()?; let mut var_s = s.clone(); var_s.advance(4); let var_index_base = var_s.read::()?; let deltas = self .variation_data() .read_deltas::<2>(var_index_base, coords); let skew_x = s.read::()?.apply_float_delta(deltas[0]); let skew_y = s.read::()?.apply_float_delta(deltas[1]); painter.push_skew(skew_x, skew_y); self.parse_paint( offset + paint_offset.to_usize(), palette, painter, recursion_stack, coords, foreground_color, ); painter.pop_transform(); } 30 => { // PaintSkewAroundCenter let paint_offset = s.read::()?; let skew_x = s.read::()?.to_f32(); let skew_y = s.read::()?.to_f32(); let center_x = f32::from(s.read::()?); let center_y = f32::from(s.read::()?); painter.push_translate(center_x, center_y); painter.push_skew(skew_x, skew_y); painter.push_translate(-center_x, -center_y); self.parse_paint( offset + paint_offset.to_usize(), palette, painter, recursion_stack, #[cfg(feature = "variable-fonts")] coords, foreground_color, ); painter.pop_transform(); painter.pop_transform(); painter.pop_transform(); } #[cfg(feature = "variable-fonts")] 31 => { // PaintVarSkewAroundCenter let paint_offset = s.read::()?; let mut var_s = s.clone(); var_s.advance(8); let var_index_base = var_s.read::()?; let deltas = self .variation_data() .read_deltas::<4>(var_index_base, coords); let skew_x = s.read::()?.apply_float_delta(deltas[0]); let skew_y = s.read::()?.apply_float_delta(deltas[1]); let center_x = f32::from(s.read::()?) + deltas[2]; let center_y = f32::from(s.read::()?) + deltas[3]; painter.push_translate(center_x, center_y); painter.push_skew(skew_x, skew_y); painter.push_translate(-center_x, -center_y); self.parse_paint( offset + paint_offset.to_usize(), palette, painter, recursion_stack, coords, foreground_color, ); painter.pop_transform(); painter.pop_transform(); painter.pop_transform(); } 32 => { // PaintComposite let source_paint_offset = s.read::()?; let composite_mode = s.read::()?; let backdrop_paint_offset = s.read::()?; painter.push_layer(CompositeMode::SourceOver); self.parse_paint( offset + backdrop_paint_offset.to_usize(), palette, painter, recursion_stack, #[cfg(feature = "variable-fonts")] coords, foreground_color, ); painter.push_layer(composite_mode); self.parse_paint( offset + source_paint_offset.to_usize(), palette, painter, recursion_stack, #[cfg(feature = "variable-fonts")] coords, foreground_color, ); painter.pop_layer(); painter.pop_layer(); } _ => {} } Some(()) } fn parse_color_line( &self, offset: usize, foreground_color: RgbaColor, ) -> Option> { let mut s = Stream::new_at(self.data, offset)?; let extend = s.read::()?; let count = s.read::()?; let colors = s.read_array16::(count)?; Some(NonVarColorLine { extend, colors, foreground_color, palettes: self.palettes, }) } #[cfg(feature = "variable-fonts")] fn parse_var_color_line( &self, offset: usize, foreground_color: RgbaColor, ) -> Option> { let mut s = Stream::new_at(self.data, offset)?; let extend = s.read::()?; let count = s.read::()?; let colors = s.read_array16::(count)?; Some(VarColorLine { extend, colors, foreground_color, palettes: self.palettes, }) } } struct RecursionStack { // The limit of 64 is chosen arbitrarily and not from the spec. But we have to stop somewhere... stack: [usize; 64], len: usize, } impl RecursionStack { #[inline] pub fn is_empty(&self) -> bool { self.len == 0 } #[inline] pub fn push(&mut self, offset: usize) -> Result<(), ()> { if self.len == self.stack.len() { Err(()) } else { self.stack[self.len] = offset; self.len += 1; Ok(()) } } #[inline] pub fn contains(&self, offset: usize) -> bool { if let Some(offsets) = self.stack.get(..self.len) { return offsets.contains(&offset); } false } #[inline] pub fn pop(&mut self) { debug_assert!(!self.is_empty()); self.len -= 1; } } #[cfg(feature = "variable-fonts")] #[derive(Clone, Copy, Debug, Default)] struct VariationData<'a> { variation_store: Option>, delta_map: Option>, } #[cfg(feature = "variable-fonts")] impl VariationData<'_> { // Inspired from `fontations`. fn read_deltas( &self, var_index_base: u32, coordinates: &[NormalizedCoordinate], ) -> [f32; N] { const NO_VARIATION_DELTAS: u32 = 0xFFFFFFFF; let mut deltas = [0.0; N]; if coordinates.is_empty() || self.variation_store.is_none() || var_index_base == NO_VARIATION_DELTAS { return deltas; } let variation_store = self.variation_store.as_ref().unwrap(); for i in 0..N { deltas[i] = self .delta_map .and_then(|d| d.map(var_index_base + i as u32)) .and_then(|d| variation_store.parse_delta(d.0, d.1, coordinates)) .unwrap_or(0.0); } deltas } }