From 01fd28fc029d9d3239485f35a08b03a3874e5cd9 Mon Sep 17 00:00:00 2001 From: Wez Furlong Date: Mon, 21 Aug 2023 09:19:22 -0700 Subject: [PATCH] refactor: extract colr from freetype -> colr.rs --- wezterm-font/src/ftwrap.rs | 2 +- wezterm-font/src/hbwrap.rs | 28 +-- wezterm-font/src/rasterizer/colr.rs | 223 ++++++++++++++++++++++++ wezterm-font/src/rasterizer/freetype.rs | 203 +-------------------- wezterm-font/src/rasterizer/harfbuzz.rs | 5 +- wezterm-font/src/rasterizer/mod.rs | 1 + 6 files changed, 233 insertions(+), 229 deletions(-) create mode 100644 wezterm-font/src/rasterizer/colr.rs diff --git a/wezterm-font/src/ftwrap.rs b/wezterm-font/src/ftwrap.rs index c7dc53f87..482bd11f0 100644 --- a/wezterm-font/src/ftwrap.rs +++ b/wezterm-font/src/ftwrap.rs @@ -1,8 +1,8 @@ //! Higher level freetype bindings -use crate::hbwrap::DrawOp; use crate::locator::{FontDataHandle, FontDataSource}; use crate::parser::ParsedFont; +use crate::rasterizer::colr::DrawOp; use anyhow::{anyhow, Context}; use config::{configuration, FreeTypeLoadFlags, FreeTypeLoadTarget}; pub use freetype::*; diff --git a/wezterm-font/src/hbwrap.rs b/wezterm-font/src/hbwrap.rs index 6935608be..4c940df47 100644 --- a/wezterm-font/src/hbwrap.rs +++ b/wezterm-font/src/hbwrap.rs @@ -4,6 +4,7 @@ use freetype; pub use harfbuzz::*; use crate::locator::{FontDataHandle, FontDataSource}; +use crate::rasterizer::colr::DrawOp; use anyhow::{ensure, Context, Error}; use memmap2::{Mmap, MmapOptions}; use std::ffi::CStr; @@ -753,33 +754,6 @@ impl PaintOp { } } -#[derive(Debug, Clone)] -pub enum DrawOp { - MoveTo { - to_x: f32, - to_y: f32, - }, - LineTo { - to_x: f32, - to_y: f32, - }, - QuadTo { - control_x: f32, - control_y: f32, - to_x: f32, - to_y: f32, - }, - CubicTo { - control1_x: f32, - control1_y: f32, - control2_x: f32, - control2_y: f32, - to_x: f32, - to_y: f32, - }, - ClosePath, -} - impl DrawOp { unsafe fn draw_data(data: *mut ::std::os::raw::c_void) -> &'static mut Vec { &mut *(data as *mut Vec) diff --git a/wezterm-font/src/rasterizer/colr.rs b/wezterm-font/src/rasterizer/colr.rs new file mode 100644 index 000000000..dc0d2fa3e --- /dev/null +++ b/wezterm-font/src/rasterizer/colr.rs @@ -0,0 +1,223 @@ +use cairo::{Context, Extend, LinearGradient, Matrix, Operator, RadialGradient}; +use wezterm_color_types::SrgbaPixel; + +#[derive(Clone, Debug)] +pub struct ColorStop { + pub offset: f32, + pub color: SrgbaPixel, +} + +#[derive(Clone, Debug)] +pub struct ColorLine { + pub color_stops: Vec, + pub extend: Extend, +} + +#[derive(Debug, Clone)] +pub enum PaintOp { + PushTransform(Matrix), + PopTransform, + PushClip(Vec), + PopClip, + PaintSolid(SrgbaPixel), + PaintLinearGradient { + x0: f32, + y0: f32, + x1: f32, + y1: f32, + x2: f32, + y2: f32, + color_line: ColorLine, + }, + PaintRadialGradient { + x0: f32, + y0: f32, + r0: f32, + x1: f32, + y1: f32, + r1: f32, + color_line: ColorLine, + }, + PaintSweepGradient { + x0: f32, + y0: f32, + start_angle: f32, + end_angle: f32, + color_line: ColorLine, + }, + PushGroup, + PopGroup(Operator), +} + +#[derive(Debug, Clone)] +pub enum DrawOp { + MoveTo { + to_x: f32, + to_y: f32, + }, + LineTo { + to_x: f32, + to_y: f32, + }, + QuadTo { + control_x: f32, + control_y: f32, + to_x: f32, + to_y: f32, + }, + CubicTo { + control1_x: f32, + control1_y: f32, + control2_x: f32, + control2_y: f32, + to_x: f32, + to_y: f32, + }, + ClosePath, +} + +pub fn paint_linear_gradient( + context: &Context, + x0: f64, + y0: f64, + x1: f64, + y1: f64, + x2: f64, + y2: f64, + mut color_line: ColorLine, +) -> anyhow::Result<()> { + let (min_stop, max_stop) = normalize_color_line(&mut color_line); + let anchors = reduce_anchors(ReduceAnchorsIn { + x0, + y0, + x1, + y1, + x2, + y2, + }); + + let xxx0 = anchors.xx0 + min_stop * (anchors.xx1 - anchors.xx0); + let yyy0 = anchors.yy0 + min_stop * (anchors.yy1 - anchors.yy0); + let xxx1 = anchors.xx0 + max_stop * (anchors.xx1 - anchors.xx0); + let yyy1 = anchors.yy0 + max_stop * (anchors.yy1 - anchors.yy0); + + let pattern = LinearGradient::new(xxx0, yyy0, xxx1, yyy1); + pattern.set_extend(color_line.extend); + + for stop in &color_line.color_stops { + let (r, g, b, a) = stop.color.as_srgba_tuple(); + pattern.add_color_stop_rgba(stop.offset.into(), r.into(), g.into(), b.into(), a.into()); + } + + context.set_source(pattern)?; + context.paint()?; + + Ok(()) +} + +pub fn paint_radial_gradient( + context: &Context, + x0: f64, + y0: f64, + r0: f64, + x1: f64, + y1: f64, + r1: f64, + mut color_line: ColorLine, +) -> anyhow::Result<()> { + let (min_stop, max_stop) = normalize_color_line(&mut color_line); + + let xx0 = x0 + min_stop * (x1 - x0); + let yy0 = y0 + min_stop * (y1 - y0); + let xx1 = x0 + max_stop * (x1 - x0); + let yy1 = y0 + max_stop * (y1 - y0); + let rr0 = r0 + min_stop * (r1 - r0); + let rr1 = r0 + max_stop * (r1 - r0); + + let pattern = RadialGradient::new(xx0, yy0, rr0, xx1, yy1, rr1); + pattern.set_extend(color_line.extend); + + for stop in &color_line.color_stops { + let (r, g, b, a) = stop.color.as_srgba_tuple(); + pattern.add_color_stop_rgba(stop.offset.into(), r.into(), g.into(), b.into(), a.into()); + } + + context.set_source(pattern)?; + context.paint()?; + + Ok(()) +} + +fn normalize_color_line(color_line: &mut ColorLine) -> (f64, f64) { + let mut smallest = color_line.color_stops[0].offset; + let mut largest = smallest; + + for stop in &color_line.color_stops[1..] { + smallest = smallest.min(stop.offset); + largest = largest.max(stop.offset); + } + + if smallest != largest { + for stop in &mut color_line.color_stops { + stop.offset = (stop.offset - smallest) / (largest - smallest); + } + } + + // NOTE: hb-cairo-utils will call back out to some other state + // to fill in the color when is_foreground is true, defaulting + // to black with alpha varying by the alpha channel of the + // color value in the stop. Do we need to do something like + // that here? + + (smallest as f64, largest as f64) +} + +struct ReduceAnchorsIn { + x0: f64, + y0: f64, + x1: f64, + y1: f64, + x2: f64, + y2: f64, +} + +struct ReduceAnchorsOut { + xx0: f64, + yy0: f64, + xx1: f64, + yy1: f64, +} + +fn reduce_anchors( + ReduceAnchorsIn { + x0, + y0, + x1, + y1, + x2, + y2, + }: ReduceAnchorsIn, +) -> ReduceAnchorsOut { + let q2x = x2 - x0; + let q2y = y2 - y0; + let q1x = x1 - x0; + let q1y = y1 - y0; + + let s = q2x * q2x + q2y * q2y; + if s < 0.000001 { + return ReduceAnchorsOut { + xx0: x0, + yy0: y0, + xx1: x1, + yy1: y1, + }; + } + + let k = (q2x * q1x + q2y * q1y) / s; + ReduceAnchorsOut { + xx0: x0, + yy0: y0, + xx1: x1 - k * q2x, + yy1: y1 - k * q2y, + } +} diff --git a/wezterm-font/src/rasterizer/freetype.rs b/wezterm-font/src/rasterizer/freetype.rs index d8eaabf36..680b642a0 100644 --- a/wezterm-font/src/rasterizer/freetype.rs +++ b/wezterm-font/src/rasterizer/freetype.rs @@ -3,8 +3,10 @@ use crate::ftwrap::{ FT_Fixed, FT_Get_Colorline_Stops, FT_Int32, FT_PaintExtend, IsSvg, SelectedFontSize, FT_LOAD_NO_HINTING, }; -use crate::hbwrap::DrawOp; use crate::parser::ParsedFont; +use crate::rasterizer::colr::{ + paint_linear_gradient, paint_radial_gradient, ColorLine, ColorStop, DrawOp, PaintOp, +}; use crate::rasterizer::harfbuzz::{argb_to_rgba, HarfbuzzRasterizer}; use crate::rasterizer::{FontRasterizer, FAKE_ITALIC_SKEW}; use crate::units::*; @@ -13,10 +15,7 @@ use ::freetype::{ FT_Color_Root_Transform, FT_GlyphSlotRec_, FT_Matrix, FT_Opaque_Paint_, FT_PaintFormat_, }; use anyhow::bail; -use cairo::{ - Content, Context, Extend, Format, ImageSurface, LinearGradient, Matrix, Operator, - RadialGradient, RecordingSurface, -}; +use cairo::{Content, Context, Extend, Format, ImageSurface, Matrix, Operator, RecordingSurface}; use config::{DisplayPixelGeometry, FreeTypeLoadFlags, FreeTypeLoadTarget}; use std::cell::RefCell; use std::f64::consts::PI; @@ -706,54 +705,6 @@ impl<'a> Walker<'a> { } } -#[derive(Clone, Debug)] -pub struct ColorStop { - pub offset: f32, - pub color: SrgbaPixel, -} - -#[derive(Clone, Debug)] -pub struct ColorLine { - pub color_stops: Vec, - pub extend: Extend, -} - -#[derive(Debug, Clone)] -pub enum PaintOp { - PushTransform(Matrix), - PopTransform, - PushClip(Vec), - PopClip, - PaintSolid(SrgbaPixel), - PaintLinearGradient { - x0: f32, - y0: f32, - x1: f32, - y1: f32, - x2: f32, - y2: f32, - color_line: ColorLine, - }, - PaintRadialGradient { - x0: f32, - y0: f32, - r0: f32, - x1: f32, - y1: f32, - r1: f32, - color_line: ColorLine, - }, - PaintSweepGradient { - x0: f32, - y0: f32, - start_angle: f32, - end_angle: f32, - color_line: ColorLine, - }, - PushGroup, - PopGroup(Operator), -} - fn affine2x3_to_matrix(t: FT_Affine23) -> Matrix { Matrix::new( t.xx.to_num(), @@ -931,149 +882,3 @@ fn apply_draw_ops_to_context(ops: &[DrawOp], context: &Context) -> anyhow::Resul } Ok(()) } - -fn normalize_color_line(color_line: &mut ColorLine) -> (f64, f64) { - let mut smallest = color_line.color_stops[0].offset; - let mut largest = smallest; - - for stop in &color_line.color_stops[1..] { - smallest = smallest.min(stop.offset); - largest = largest.max(stop.offset); - } - - if smallest != largest { - for stop in &mut color_line.color_stops { - stop.offset = (stop.offset - smallest) / (largest - smallest); - } - } - - // NOTE: hb-cairo-utils will call back out to some other state - // to fill in the color when is_foreground is true, defaulting - // to black with alpha varying by the alpha channel of the - // color value in the stop. Do we need to do something like - // that here? - - (smallest as f64, largest as f64) -} - -struct ReduceAnchorsIn { - x0: f64, - y0: f64, - x1: f64, - y1: f64, - x2: f64, - y2: f64, -} - -struct ReduceAnchorsOut { - xx0: f64, - yy0: f64, - xx1: f64, - yy1: f64, -} - -fn reduce_anchors( - ReduceAnchorsIn { - x0, - y0, - x1, - y1, - x2, - y2, - }: ReduceAnchorsIn, -) -> ReduceAnchorsOut { - let q2x = x2 - x0; - let q2y = y2 - y0; - let q1x = x1 - x0; - let q1y = y1 - y0; - - let s = q2x * q2x + q2y * q2y; - if s < 0.000001 { - return ReduceAnchorsOut { - xx0: x0, - yy0: y0, - xx1: x1, - yy1: y1, - }; - } - - let k = (q2x * q1x + q2y * q1y) / s; - ReduceAnchorsOut { - xx0: x0, - yy0: y0, - xx1: x1 - k * q2x, - yy1: y1 - k * q2y, - } -} - -fn paint_linear_gradient( - context: &Context, - x0: f64, - y0: f64, - x1: f64, - y1: f64, - x2: f64, - y2: f64, - mut color_line: ColorLine, -) -> anyhow::Result<()> { - let (min_stop, max_stop) = normalize_color_line(&mut color_line); - let anchors = reduce_anchors(ReduceAnchorsIn { - x0, - y0, - x1, - y1, - x2, - y2, - }); - - let xxx0 = anchors.xx0 + min_stop * (anchors.xx1 - anchors.xx0); - let yyy0 = anchors.yy0 + min_stop * (anchors.yy1 - anchors.yy0); - let xxx1 = anchors.xx0 + max_stop * (anchors.xx1 - anchors.xx0); - let yyy1 = anchors.yy0 + max_stop * (anchors.yy1 - anchors.yy0); - - let pattern = LinearGradient::new(xxx0, yyy0, xxx1, yyy1); - pattern.set_extend(color_line.extend); - - for stop in &color_line.color_stops { - let (r, g, b, a) = stop.color.as_srgba_tuple(); - pattern.add_color_stop_rgba(stop.offset.into(), r.into(), g.into(), b.into(), a.into()); - } - - context.set_source(pattern)?; - context.paint()?; - - Ok(()) -} - -fn paint_radial_gradient( - context: &Context, - x0: f64, - y0: f64, - r0: f64, - x1: f64, - y1: f64, - r1: f64, - mut color_line: ColorLine, -) -> anyhow::Result<()> { - let (min_stop, max_stop) = normalize_color_line(&mut color_line); - - let xx0 = x0 + min_stop * (x1 - x0); - let yy0 = y0 + min_stop * (y1 - y0); - let xx1 = x0 + max_stop * (x1 - x0); - let yy1 = y0 + max_stop * (y1 - y0); - let rr0 = r0 + min_stop * (r1 - r0); - let rr1 = r0 + max_stop * (r1 - r0); - - let pattern = RadialGradient::new(xx0, yy0, rr0, xx1, yy1, rr1); - pattern.set_extend(color_line.extend); - - for stop in &color_line.color_stops { - let (r, g, b, a) = stop.color.as_srgba_tuple(); - pattern.add_color_stop_rgba(stop.offset.into(), r.into(), g.into(), b.into(), a.into()); - } - - context.set_source(pattern)?; - context.paint()?; - - Ok(()) -} diff --git a/wezterm-font/src/rasterizer/harfbuzz.rs b/wezterm-font/src/rasterizer/harfbuzz.rs index c1db1293d..53250192f 100644 --- a/wezterm-font/src/rasterizer/harfbuzz.rs +++ b/wezterm-font/src/rasterizer/harfbuzz.rs @@ -1,8 +1,9 @@ use crate::hbwrap::{ hb_color, hb_color_get_alpha, hb_color_get_blue, hb_color_get_green, hb_color_get_red, - hb_color_t, hb_paint_composite_mode_t, hb_paint_extend_t, hb_tag_to_string, ColorLine, DrawOp, - Font, PaintOp, IS_PNG, + hb_color_t, hb_paint_composite_mode_t, hb_paint_extend_t, hb_tag_to_string, ColorLine, Font, + PaintOp, IS_PNG, }; +use crate::rasterizer::colr::DrawOp; use crate::rasterizer::FAKE_ITALIC_SKEW; use crate::units::PixelLength; use crate::{FontRasterizer, ParsedFont, RasterizedGlyph}; diff --git a/wezterm-font/src/rasterizer/mod.rs b/wezterm-font/src/rasterizer/mod.rs index c6d2ada80..0ae8c9f99 100644 --- a/wezterm-font/src/rasterizer/mod.rs +++ b/wezterm-font/src/rasterizer/mod.rs @@ -7,6 +7,7 @@ use image::{ImageBuffer, Rgba}; /// italics pub(crate) const FAKE_ITALIC_SKEW: f64 = 0.2; +pub mod colr; pub mod freetype; pub mod harfbuzz;