palette icon indicating copy to clipboard operation
palette copied to clipboard

Support fixed point like u8 operates like Real

Open clouds56 opened this issue 5 months ago • 3 comments

Description

When we write LinSrgba<u8> we actually want to treat 0u8 like 0.0f32 and 255u8 like 1.0f32, especially when doing multiple, this code would emit wrong result if relax T: Real to things like u8 https://github.com/Ogeon/palette/blob/b8fcb95ce59f84971a7f3d0451804156b1dad296/palette/src/encoding/rec_standards.rs#L140-L146

So we shall introduce a new Real-like wrapping type, just like FixedU8<U8>, except the range of FixedU8 is 0 <= x < 1 while we need 0 <= x <= 1 here.

pub struct Fixed<T>(T);
impl std::ops::Mul for Fixed<u8> {
    type Output = Self;
    fn mul(self, rhs: Self) -> Self::Output {
        Self((self.0 as u16 * rhs.0 as u16 / u8::max_intensity()) as u8)
    }
}
impl num::Real for Fixed<u8> {
  fn from_f64(n: f64) -> Self {
    Self((n / u8::max_intensity() as f64).round() as u8)
  }
}

Alternative

We could also write our own num::Mul trait to do mul, instead of relies on std::ops::Mul

Notes

Furthermore, we might also make TransformFn marker in this Fixed.

pub struct Fixed<S=Srgb, T=u8> {
  value: T,
  transfer_fn: PhantomData<S>,
}
// Fixed<LinearFn, u8>

clouds56 avatar Sep 19 '25 19:09 clouds56

Hi, thanks for the suggestion, but adding specialized number types is beyond the scope of palette. Even the number traits are and will be moved out at a later point. You could implement it as a third party crate, but I would still advice against using u8 for linear RGB, due to its low resolution. It may cause visible banding in dark colors. That's part of what non-linear encodings, like sRGB, help with. When processing the color in linear space, I recommend to also convert the components to f32.

Is there a particular problem you need to solve that requires the components to be u8?

Ogeon avatar Sep 20 '25 12:09 Ogeon

Thanks for kindly reply, I'm trying to port agg to rust, the original algorithm in agg takes heavy dependency on u8 math like multiply_u8, lerp_u8, etc.

I do think it's a good time to upgrade it to f32 based color space when porting. But for the time being, I'd like to first make it compatible to the original implementation.

clouds56 avatar Sep 20 '25 15:09 clouds56

I see, I suppose they deemed the quality good enough for that use and prioritized speed and memory usage. It may be something to support it in the longer run, with some more specific traits. Maybe a Scale trait for what multiplication usually does, for example. That could avoid requiring specialization and wrapper types. Although the plan is to move the math traits out of the main crate, so it's not necessarily as simple in the future. 🤔

Ogeon avatar Sep 20 '25 15:09 Ogeon