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

Code before:

                let [r, g, b, _] = ColorSpace::Srgb.encode(*self);
                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) -> [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]]),
            ]
        }
    }
}


Review comment (suggestion):
This is correct, but I would prefer if the range was dependent on the color space, for example `fn range(self)` could return `&'static [f32]` instead of a fixed size array, that could already be an improvement as we don't want to waste space.

Code after:
                ctx.set_stroke_color_space(SRGB);

                let [r, g, b, _] = ColorSpace::Srgb.encode(*self);
                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 {