Skip to content

Rgba::From<Color32> does not consider alpha #5771

@virtualritz

Description

@virtualritz

I believe the sRGB to linear sRGB conversion function has a bug.

The docs say that both Color32 and Rgba are premultiplied. When converting each component the else branch in linear_f32_from_gamma_u8() fails to divide by alpha (and re-premultiply at the end).

Consider the current implementation. In the if branch alpha can be factored out but not in the else branch, i.e. it is correct only as long as the color has a solid alpha.

pub fn linear_f32_from_gamma_u8(s: u8) -> f32 {
    if s <= 10 {
        s as f32 / 3294.6
    } else {
        ((s as f32 + 14.025) / 269.025).powf(2.4)
    }
}

vs:

// alpha must be 0.0..1.0.
pub fn linear_f32_from_gamma_u8(s: u8, alpha: f32) -> f32 {
    if s <= 10 {
        s as f32 / 3294.6
    } else {
        (((s as f32 / alpha) + 14.025) / 269.025).powf(2.4) * alpha
    }
}

I moved the alpha as f32 / 255.0 out of the function as that can be re-used for all three components.

The more transparent the color is, the more severe the error becomes (and the darker the resulting color than it should be). I came accross this as we're using egui for an in-house compositing app that works in ACEScg internally and I noticed that colors coming from the UI, using just Rgba::From<Color32>, seemed too dark when they had a non-opaque alpha.

For example, if you have a Color32 component that is 122 with an alpha of 127 (~0.5), the current code converts this to ~0.195 whereas I think this should actually end up as ~0.452.

I.e. the current version of linear_f32_from_gamma_u8() is only correct if Color32 was unpremultiplied (or if alpha is 255 resp. 1.0).

Does that make sense?

Metadata

Metadata

Assignees

Labels

bugSomething is brokenvisualsRenderings / graphics releated

Projects

Status

Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions