Why am I getting unexpected colors?

Viewed 97

I am trying to create gradient blobs. I supply the gen function with a Vec<ColorPoint> and each point should have go from a maximum intensity at the center to no effect at the radius. The main problem is that I am not getting the colors I expect in the output image.

use image::{ImageBuffer, RgbImage, Rgb};
use std::ops::Add;

/// Color with red, green, blue and alpha channels between 0.0 and 1.0
#[derive(Debug)]
struct RgbaColor {
    r: f64,
    g: f64,
    b: f64,
    a: f64,
}

impl RgbaColor {
    fn to_rgb(&self) -> Rgb<u8> {
        let r = (&self.r*255.0) as u8;
        let g = (&self.g*255.0) as u8;
        let b = (&self.b*255.0) as u8;
        Rgb::from([r, g, b])
    }
}

impl Add for RgbaColor {
    type Output = Self;

    fn add(self, fg: Self) -> Self {
        // self is the background and fg is the foreground
        let new_alpha = fg.a + self.a * (1 - fg.a);
        Self {
            r: (fg.r * fg.a + self.r * self.a * (1.0 - fg.a)) / new_alpha,
            g: (fg.g * fg.a + self.g * self.a * (1.0 - fg.a)) / new_alpha,
            b: (fg.b * fg.a + self.b * self.a * (1.0 - fg.a)) / new_alpha,
            a: new_alpha
        }
    }
}

#[derive(Debug)]
struct ColorPoint {
    color: RgbaColor,
    center: (u32, u32),
    radius: u32,
}

impl ColorPoint {
    fn rgba_color_at_point(&self, x: u32, y: u32) -> RgbaColor {
        let x_dist = x as f64 - self.center.0 as f64;
        let y_dist = y as f64 - self.center.1 as f64;
        let dist = (x_dist.powf(2.0) + y_dist.powf(2.0)).sqrt();
        RgbaColor {
            r: self.color.r,
            g: self.color.g,
            b: self.color.b,
            a: self.color.a * dist / self.radius as f64,
        }
    }
}

fn main() {
    let color_points = vec![
        ColorPoint{color: RgbaColor{r: 1.0, g:1.0, b:0.0, a:1.0}, center: (0, 50), radius: 20},
        ColorPoint{color: RgbaColor{r: 1.0, g:0.0, b:0.0, a:1.0}, center: (10, 0), radius: 30}
    ];
    gen(color_points)
}

fn gen(color_points: Vec<ColorPoint>) {
    let geometry = (50, 100);
    let mut background: RgbImage = ImageBuffer::new(geometry.0 as u32, geometry.1 as u32);
    for (x, y, pixel) in background.enumerate_pixels_mut() {
        let mut curr_color = RgbaColor{ r:0.0, g:0.0, b:0.0, a:1.0 }; // hardcoded background color
        for color_point in color_points.iter() {
            curr_color = curr_color + color_point.rgba_color_at_point(x,y);
        }
        *pixel = curr_color.to_rgb();
    }
    background.save("image.png").unwrap();
}

Output:

enter image description here

This code almost does what I expect although the position of the yellow and red blobs seem to have swapped and when I change the hard coded background color to white RgbaColor{ r:1.0, g:1.0, b:1.0, a:1.0 } I seem to have a magenta background.

enter image description here

I'm not sure whether my color model is wrong or if it's something else because when adding individual RgbaColors I get the correct colors.

0 Answers
Related