Skip to content

Commit

Permalink
Colors are stupid, but now tints work
Browse files Browse the repository at this point in the history
Comfy's GPU tinting works in linear space, and while textures are being converted from sRGB to linear space, the colors passed to shaders are passed as in. Our shaders then do `a*b`, which means we're implicitly doing tinting in linear space.

But blood canvas runs on the CPU and uses image data from the `image` crate, which apparently works in sRGB at least in the way we're using it. This means that the same tint color we get from `to_quad_draw(...)` can't just multiply, as that would be sRGB * linear. For simplicity we'll just convert it to sRGB and then do .linear_space_tint(...), which does a conversion to linear space, multiplication, and conversion back. We could do this differently, but this works for now.
  • Loading branch information
darthdeus committed Jan 27, 2024
1 parent e3e0564 commit 80a0ee8
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 5 deletions.
13 changes: 8 additions & 5 deletions comfy-core/src/blood_canvas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ impl BloodCanvas {
flip_x: bool,
flip_y: bool,
) {
let tint = tint.to_srgb();

let assets = ASSETS.borrow_mut();
let image_map = assets.texture_image_map.lock();

Expand Down Expand Up @@ -200,17 +202,17 @@ impl BloodCanvas {
read_y = rect.offset.y + rect.size.y - y - 1;
}

let px = image.get_pixel(read_x as u32, read_y as u32);
let src_px = image.get_pixel(read_x as u32, read_y as u32);

if px.0[3] > 0 {
if src_px.0[3] > 0 {
let px_pos = position + vec2(x as f32, y as f32) / 16.0 -
size_offset / 16.0;

if tint.a < 1.0 {
let existing = self.get_pixel(px_pos);

let tinted =
Into::<Color>::into(*px) * tint.alpha(1.0);
let tinted = Into::<Color>::into(*src_px)
.linear_space_tint(tint.alpha(1.0));

self.set_pixel(
px_pos,
Expand All @@ -219,7 +221,8 @@ impl BloodCanvas {
} else {
self.set_pixel(
px_pos,
Into::<Color>::into(*px) * tint,
Into::<Color>::into(*src_px)
.linear_space_tint(tint),
);
}
}
Expand Down
31 changes: 31 additions & 0 deletions comfy-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,37 @@ impl Color {
Vec4::new(self.r, self.g, self.b, self.a)
}

pub fn gamma_space_tint(self, tint: Color) -> Color {
let gamma_a = self.to_srgb();
let gamma_b = tint.to_srgb();

let result = gamma_a * gamma_b;

result.to_linear()
}

pub fn linear_space_tint(self, tint: Color) -> Color {
let lin_a = self.to_linear();
let lin_b = tint.to_linear();

let result = lin_a * lin_b;

result.to_srgb()
}

pub fn to_linear(self) -> Color {
Color::new(self.r.powf(2.2), self.g.powf(2.2), self.b.powf(2.2), self.a)
}

pub fn to_srgb(self) -> Color {
Color::new(
self.r.powf(1.0 / 2.2),
self.g.powf(1.0 / 2.2),
self.b.powf(1.0 / 2.2),
self.a,
)
}

pub fn to_array(self) -> [u8; 4] {
[
(self.r * 255.0) as u8,
Expand Down
1 change: 1 addition & 0 deletions comfy/examples/blood_canvas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ fn setup(c: &mut EngineContext) {
Player,
AnimatedSpriteBuilder::new()
.z_index(10)
.color(DARKRED)
.add_animation("idle", 0.1, true, AnimationSource::Atlas {
name: "player".into(),
offset: ivec2(0, 0),
Expand Down

0 comments on commit 80a0ee8

Please sign in to comment.