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

Code before:
    fn range(self) -> SmallVec<[f32; 8]> {
        match self {
            ColorSpace::Cmyk => smallvec![0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0],
            ColorSpace::D65Gray => smallvec![0.0, 1.0],
            _ => smallvec![0.0, 1.0, 0.0, 1.0, 0.0, 1.0],
        }
    }

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

        match self {
            ColorSpace::Cmyk => smallvec![
                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 => smallvec![U::quantize(x, [range[0], range[1]])],
            _ => smallvec![
                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;
}

impl QuantizedColor for u16 {
    fn quantize(color: f32, [min, max]: [f32; 2]) -> Self {
        let value = (color - min) / (max - min);
        (value * Self::MAX as f32).round().clamp(0.0, Self::MAX as f32) as Self
    }
}

impl QuantizedColor for f32 {
    fn quantize(color: f32, [min, max]: [f32; 2]) -> Self {
        color.clamp(min, max)
    }
}


Review comment (suggestion):
Maybe you could use the result from the `range` function above to generalize this:
```rs
let ranges = self.range().chunks(2);
let components = self.encode(color);

ranges
    .zip(components)
    .map(|(range, component)| U::quantize(component, [range[0], range[1]]))
    .collect()
```

Code after:
    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;
}

impl QuantizedColor for u16 {
    fn quantize(color: f32, [min, max]: [f32; 2]) -> Self {
        let value = (color - min) / (max - min);
        (value * Self::MAX as f32).round().clamp(0.0, Self::MAX as f32) as Self
    }
}

impl QuantizedColor for f32 {
    fn quantize(color: f32, [min, max]: [f32; 2]) -> Self {
        color.clamp(min, max)
    }
}