Repository: typst/typst
File: crates/typst-pdf/src/color.rs (Rust)

Code before:
            }
            Color::Cmyk(_) => {
                ctx.reset_stroke_color_space();

                let [c, m, y, k] = ColorSpace::Cmyk.encode(*self);
                ctx.content.set_stroke_cmyk(c, m, y, k);
            }
        }
    }
}

/// Extra color space functions.
pub(super) trait ColorSpaceExt {
    /// Returns the range of the color space.
    fn range(self) -> [f32; 8];

    /// Converts a color to the color space.
    fn convert<U: QuantizedColor>(self, color: Color) -> Vec<U>;
}

impl ColorSpaceExt for ColorSpace {
    fn range(self) -> [f32; 8] {
        [0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0] // Not sure about that
    }

    fn convert<U: QuantizedColor>(self, color: Color) -> Vec<U> {
        let range = self.range();
        let [x, y, z, a] = self.encode(color);

        match self {
            ColorSpace::Cmyk => vec![
                U::quantize(x, [range[0], range[1]]),
                U::quantize(y, [range[2], range[3]]),
                U::quantize(z, [range[4], range[5]]),
                U::quantize(a, [range[6], range[7]]),
            ],
            ColorSpace::D65Gray => vec![
                U::quantize(x, [range[0], range[1]]),
            ],
            _ => vec![
                U::quantize(x, [range[0], range[1]]),
                U::quantize(y, [range[2], range[3]]),
                U::quantize(z, [range[4], range[5]]),
            ]
        }
    }
}

/// Quantizes a color component to a specific type.
pub(super) trait QuantizedColor {
    fn quantize(color: f32, range: [f32; 2]) -> Self;

Review comment (suggestion):
Using a `Vec` is undesirable here since it's used in many places, it should be a fixed sized array otherwise every single color being encoded will cause an allocation. Perhaps a `SmallVec<[U; 3]>` could be okay.

Code after:
                ctx.content.set_stroke_color([r, g, b]);
            }
            Color::Cmyk(_) => {
                ctx.reset_stroke_color_space();

                let [c, m, y, k] = ColorSpace::Cmyk.encode(*self);
                ctx.content.set_stroke_cmyk(c, m, y, k);
            }
        }
    }
}

/// Extra color space functions.
pub(super) trait ColorSpaceExt {
    /// Returns the range of the color space.
    fn range(self) -> &'static [f32];

    /// Converts a color to the color space.
    fn convert<U: QuantizedColor>(self, color: Color) -> ArrayVec<U, 4>;
}

impl ColorSpaceExt for ColorSpace {
    fn range(self) -> &'static [f32] {
        match self {
            ColorSpace::D65Gray => &[0.0, 1.0],
            ColorSpace::Oklab => &[0.0, 1.0, 0.0, 1.0, 0.0, 1.0],
            ColorSpace::Oklch => &[0.0, 1.0, 0.0, 1.0, 0.0, 1.0],
            ColorSpace::LinearRgb => &[0.0, 1.0, 0.0, 1.0, 0.0, 1.0],
            ColorSpace::Srgb => &[0.0, 1.0, 0.0, 1.0, 0.0, 1.0],
            ColorSpace::Cmyk => &[0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0],
            ColorSpace::Hsl => &[0.0, 1.0, 0.0, 1.0, 0.0, 1.0],
            ColorSpace::Hsv => &[0.0, 1.0, 0.0, 1.0, 0.0, 1.0],
        }
    }

    fn convert<U: QuantizedColor>(self, color: Color) -> ArrayVec<U, 4> {
        let components = self.encode(color);

        self.range()
            .chunks(2)
            .zip(components)
            .map(|(range, component)| U::quantize(component, [range[0], range[1]]))
            .collect()
    }
}

/// Quantizes a color component to a specific type.
pub(super) trait QuantizedColor {
    fn quantize(color: f32, range: [f32; 2]) -> Self;
}