From 2b414072bcdfb471080cfed850a62a8117362e13 Mon Sep 17 00:00:00 2001 From: gered Date: Fri, 10 Mar 2023 11:11:04 -0500 Subject: [PATCH] move RGB/RGBA colour related functions into common module these are obviously useful to new 32-bit graphics functionality, but they are also useful to indexed colour functionality such as palettes and blendmaps. --- ggdt/src/graphics/color.rs | 137 ++++++++++++++++++++++ ggdt/src/graphics/indexed/bitmap/pcx.rs | 3 +- ggdt/src/graphics/indexed/blendmap.rs | 3 +- ggdt/src/graphics/indexed/palette.rs | 144 +----------------------- ggdt/src/graphics/indexed/prelude.rs | 1 + ggdt/src/graphics/mod.rs | 1 + ggdt/src/graphics/prelude.rs | 1 + ggdt/src/graphics/rgb/prelude.rs | 1 + 8 files changed, 149 insertions(+), 142 deletions(-) create mode 100644 ggdt/src/graphics/color.rs diff --git a/ggdt/src/graphics/color.rs b/ggdt/src/graphics/color.rs new file mode 100644 index 0000000..fea84b1 --- /dev/null +++ b/ggdt/src/graphics/color.rs @@ -0,0 +1,137 @@ +/// Converts a set of individual ARGB components to a combined 32-bit color value, packed into +/// the format 0xAARRGGBB +/// +/// # Arguments +/// +/// * `a`: the alpha component (0-255) +/// * `r`: the red component (0-255) +/// * `g`: the green component (0-255) +/// * `b`: the blue component (0-255) +/// +/// returns: the u32 packed color +#[inline] +pub fn to_argb32(a: u8, r: u8, g: u8, b: u8) -> u32 { + (b as u32) + ((g as u32) << 8) + ((r as u32) << 16) + ((a as u32) << 24) +} + +/// Extracts the individual ARGB components out of a combined 32-bit color value which is in the +/// format 0xAARRGGBB +/// +/// # Arguments +/// +/// * `argb`: the 32-bit packed color +/// +/// returns: the individual ARGB color components (0-255 each) in order: alpha, red, green, blue +#[inline] +pub fn from_argb32(argb: u32) -> (u8, u8, u8, u8) { + let a = ((argb & 0xff000000) >> 24) as u8; + let r = ((argb & 0x00ff0000) >> 16) as u8; + let g = ((argb & 0x0000ff00) >> 8) as u8; + let b = (argb & 0x000000ff) as u8; + (a, r, g, b) +} + +/// Converts a set of individual RGB components to a combined 32-bit color value, packed into +/// the format 0xAARRGGBB. Substitutes a value of 255 for the missing alpha component. +/// +/// # Arguments +/// +/// * `r`: the red component (0-255) +/// * `g`: the green component (0-255) +/// * `b`: the blue component (0-255) +/// +/// returns: the u32 packed color +#[inline] +pub fn to_rgb32(r: u8, g: u8, b: u8) -> u32 { + to_argb32(255, r, g, b) +} + +/// Extracts the individual RGB components out of a combined 32-bit color value which is in the +/// format 0xAARRGGBB. Ignores the alpha component. +/// +/// # Arguments +/// +/// * `argb`: the 32-bit packed color +/// +/// returns: the individual ARGB color components (0-255 each) in order: red, green, blue +#[inline] +pub fn from_rgb32(rgb: u32) -> (u8, u8, u8) { + // ignore alpha component at 0xff000000 ... + let r = ((rgb & 0x00ff0000) >> 16) as u8; + let g = ((rgb & 0x0000ff00) >> 8) as u8; + let b = (rgb & 0x000000ff) as u8; + (r, g, b) +} + +/// Linearly interpolates between two 32-bit packed colors in the format 0xAARRGGBB. +/// +/// # Arguments +/// +/// * `a`: the first 32-bit packed color +/// * `b`: the second 32-bit packed color +/// * `t`: the amount to interpolate between the two values, specified as a fraction. +#[inline] +pub fn lerp_argb32(a: u32, b: u32, t: f32) -> u32 { + let (a1, r1, g1, b1) = from_argb32(a); + let (a2, r2, g2, b2) = from_argb32(b); + to_argb32( + ((a1 as f32) + ((a2 as f32) - (a1 as f32)) * t) as u8, + ((r1 as f32) + ((r2 as f32) - (r1 as f32)) * t) as u8, + ((g1 as f32) + ((g2 as f32) - (g1 as f32)) * t) as u8, + ((b1 as f32) + ((b2 as f32) - (b1 as f32)) * t) as u8, + ) +} + +/// Linearly interpolates between two 32-bit packed colors in the format 0xAARRGGBB. Ignores the +/// alpha component, which will always be set to 255 in the return value. +/// +/// # Arguments +/// +/// * `a`: the first 32-bit packed color +/// * `b`: the second 32-bit packed color +/// * `t`: the amount to interpolate between the two values, specified as a fraction. +#[inline] +pub fn lerp_rgb32(a: u32, b: u32, t: f32) -> u32 { + let (r1, g1, b1) = from_rgb32(a); + let (r2, g2, b2) = from_rgb32(b); + to_rgb32( + ((r1 as f32) + ((r2 as f32) - (r1 as f32)) * t) as u8, + ((g1 as f32) + ((g2 as f32) - (g1 as f32)) * t) as u8, + ((b1 as f32) + ((b2 as f32) - (b1 as f32)) * t) as u8, + ) +} + +const LUMINANCE_RED: f32 = 0.212655; +const LUMINANCE_GREEN: f32 = 0.715158; +const LUMINANCE_BLUE: f32 = 0.072187; + +fn srgb_to_linearized(color_channel: u8) -> f32 { + let intensity = color_channel as f32 / 255.0; + if intensity <= 0.04045 { + intensity / 12.92 + } else { + ((intensity + 0.055) / (1.055)).powf(2.4) + } +} + +/// Calculates the given sRGB color's luminance, returned as a value between 0.0 and 1.0. +pub fn luminance(r: u8, g: u8, b: u8) -> f32 { + (LUMINANCE_RED * srgb_to_linearized(r)) + + (LUMINANCE_GREEN * srgb_to_linearized(g)) + + (LUMINANCE_BLUE * srgb_to_linearized(b)) +} + +fn brightness(mut luminance: f32) -> f32 { + if luminance <= 0.0031308 { + luminance *= 12.92; + } else { + luminance = 1.055 * luminance.powf(1.0 / 2.4) - 0.055; + } + luminance +} + +/// Calculates the approximate "brightness" / grey-scale value for the given sRGB color, returned +/// as a value between 0 and 255. +pub fn greyscale(r: u8, b: u8, g: u8) -> u8 { + (brightness(luminance(r, g, b)) * 255.0) as u8 +} diff --git a/ggdt/src/graphics/indexed/bitmap/pcx.rs b/ggdt/src/graphics/indexed/bitmap/pcx.rs index dff39c1..2b28a01 100644 --- a/ggdt/src/graphics/indexed/bitmap/pcx.rs +++ b/ggdt/src/graphics/indexed/bitmap/pcx.rs @@ -5,8 +5,9 @@ use std::path::Path; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use thiserror::Error; +use crate::graphics::color::from_rgb32; use crate::graphics::indexed::bitmap::Bitmap; -use crate::graphics::indexed::palette::{from_rgb32, Palette, PaletteError, PaletteFormat}; +use crate::graphics::indexed::palette::{Palette, PaletteError, PaletteFormat}; use crate::utils::bytes::ReadFixedLengthByteArray; #[derive(Error, Debug)] diff --git a/ggdt/src/graphics/indexed/blendmap.rs b/ggdt/src/graphics/indexed/blendmap.rs index e173dfb..bada5a6 100644 --- a/ggdt/src/graphics/indexed/blendmap.rs +++ b/ggdt/src/graphics/indexed/blendmap.rs @@ -5,7 +5,8 @@ use std::path::Path; use byteorder::{ReadBytesExt, WriteBytesExt}; use thiserror::Error; -use crate::graphics::indexed::palette::{from_rgb32, luminance, Palette}; +use crate::graphics::color::{from_rgb32, luminance}; +use crate::graphics::indexed::palette::Palette; use crate::math::lerp; use crate::utils::bytes::ReadFixedLengthByteArray; diff --git a/ggdt/src/graphics/indexed/palette.rs b/ggdt/src/graphics/indexed/palette.rs index 6226048..ad0411e 100644 --- a/ggdt/src/graphics/indexed/palette.rs +++ b/ggdt/src/graphics/indexed/palette.rs @@ -7,155 +7,17 @@ use std::path::Path; use byteorder::{ReadBytesExt, WriteBytesExt}; use thiserror::Error; +use crate::graphics::color::{from_rgb32, lerp_rgb32, to_rgb32}; use crate::graphics::indexed::bitmap::Bitmap; use crate::NUM_COLORS; use crate::utils::abs_diff; -// silly "hack" (???) which allows us to alias the generic constraint `RangeBounds + Iterator` to `ColorRange` +/// Common trait to represent a range of indexed colour values. pub trait ColorRange: RangeBounds + Iterator {} - impl ColorRange for T where T: RangeBounds + Iterator {} pub static VGA_PALETTE_BYTES: &[u8] = include_bytes!("../../../assets/vga.pal"); -/// Converts a set of individual ARGB components to a combined 32-bit color value, packed into -/// the format 0xAARRGGBB -/// -/// # Arguments -/// -/// * `a`: the alpha component (0-255) -/// * `r`: the red component (0-255) -/// * `g`: the green component (0-255) -/// * `b`: the blue component (0-255) -/// -/// returns: the u32 packed color -#[inline] -pub fn to_argb32(a: u8, r: u8, g: u8, b: u8) -> u32 { - (b as u32) + ((g as u32) << 8) + ((r as u32) << 16) + ((a as u32) << 24) -} - -/// Extracts the individual ARGB components out of a combined 32-bit color value which is in the -/// format 0xAARRGGBB -/// -/// # Arguments -/// -/// * `argb`: the 32-bit packed color -/// -/// returns: the individual ARGB color components (0-255 each) in order: alpha, red, green, blue -#[inline] -pub fn from_argb32(argb: u32) -> (u8, u8, u8, u8) { - let a = ((argb & 0xff000000) >> 24) as u8; - let r = ((argb & 0x00ff0000) >> 16) as u8; - let g = ((argb & 0x0000ff00) >> 8) as u8; - let b = (argb & 0x000000ff) as u8; - (a, r, g, b) -} - -/// Converts a set of individual RGB components to a combined 32-bit color value, packed into -/// the format 0xAARRGGBB. Substitutes a value of 255 for the missing alpha component. -/// -/// # Arguments -/// -/// * `r`: the red component (0-255) -/// * `g`: the green component (0-255) -/// * `b`: the blue component (0-255) -/// -/// returns: the u32 packed color -#[inline] -pub fn to_rgb32(r: u8, g: u8, b: u8) -> u32 { - to_argb32(255, r, g, b) -} - -/// Extracts the individual RGB components out of a combined 32-bit color value which is in the -/// format 0xAARRGGBB. Ignores the alpha component. -/// -/// # Arguments -/// -/// * `argb`: the 32-bit packed color -/// -/// returns: the individual ARGB color components (0-255 each) in order: red, green, blue -#[inline] -pub fn from_rgb32(rgb: u32) -> (u8, u8, u8) { - // ignore alpha component at 0xff000000 ... - let r = ((rgb & 0x00ff0000) >> 16) as u8; - let g = ((rgb & 0x0000ff00) >> 8) as u8; - let b = (rgb & 0x000000ff) as u8; - (r, g, b) -} - -/// Linearly interpolates between two 32-bit packed colors in the format 0xAARRGGBB. -/// -/// # Arguments -/// -/// * `a`: the first 32-bit packed color -/// * `b`: the second 32-bit packed color -/// * `t`: the amount to interpolate between the two values, specified as a fraction. -#[inline] -pub fn lerp_argb32(a: u32, b: u32, t: f32) -> u32 { - let (a1, r1, g1, b1) = from_argb32(a); - let (a2, r2, g2, b2) = from_argb32(b); - to_argb32( - ((a1 as f32) + ((a2 as f32) - (a1 as f32)) * t) as u8, - ((r1 as f32) + ((r2 as f32) - (r1 as f32)) * t) as u8, - ((g1 as f32) + ((g2 as f32) - (g1 as f32)) * t) as u8, - ((b1 as f32) + ((b2 as f32) - (b1 as f32)) * t) as u8, - ) -} - -/// Linearly interpolates between two 32-bit packed colors in the format 0xAARRGGBB. Ignores the -/// alpha component, which will always be set to 255 in the return value. -/// -/// # Arguments -/// -/// * `a`: the first 32-bit packed color -/// * `b`: the second 32-bit packed color -/// * `t`: the amount to interpolate between the two values, specified as a fraction. -#[inline] -pub fn lerp_rgb32(a: u32, b: u32, t: f32) -> u32 { - let (r1, g1, b1) = from_rgb32(a); - let (r2, g2, b2) = from_rgb32(b); - to_rgb32( - ((r1 as f32) + ((r2 as f32) - (r1 as f32)) * t) as u8, - ((g1 as f32) + ((g2 as f32) - (g1 as f32)) * t) as u8, - ((b1 as f32) + ((b2 as f32) - (b1 as f32)) * t) as u8, - ) -} - -const LUMINANCE_RED: f32 = 0.212655; -const LUMINANCE_GREEN: f32 = 0.715158; -const LUMINANCE_BLUE: f32 = 0.072187; - -fn srgb_to_linearized(color_channel: u8) -> f32 { - let intensity = color_channel as f32 / 255.0; - if intensity <= 0.04045 { - intensity / 12.92 - } else { - ((intensity + 0.055) / (1.055)).powf(2.4) - } -} - -/// Calculates the given sRGB color's luminance, returned as a value between 0.0 and 1.0. -pub fn luminance(r: u8, g: u8, b: u8) -> f32 { - (LUMINANCE_RED * srgb_to_linearized(r)) + - (LUMINANCE_GREEN * srgb_to_linearized(g)) + - (LUMINANCE_BLUE * srgb_to_linearized(b)) -} - -fn brightness(mut luminance: f32) -> f32 { - if luminance <= 0.0031308 { - luminance *= 12.92; - } else { - luminance = 1.055 * luminance.powf(1.0 / 2.4) - 0.055; - } - luminance -} - -/// Calculates the approximate "brightness" / grey-scale value for the given sRGB color, returned -/// as a value between 0 and 255. -pub fn greyscale(r: u8, b: u8, g: u8) -> u8 { - (brightness(luminance(r, g, b)) * 255.0) as u8 -} - // vga bios (0-63) format fn read_palette_6bit( reader: &mut T, @@ -653,6 +515,8 @@ impl IndexMut for Palette { mod tests { use tempfile::TempDir; + use crate::graphics::color::{from_argb32, to_argb32}; + use super::*; #[test] diff --git a/ggdt/src/graphics/indexed/prelude.rs b/ggdt/src/graphics/indexed/prelude.rs index a901b14..b9dc1b6 100644 --- a/ggdt/src/graphics/indexed/prelude.rs +++ b/ggdt/src/graphics/indexed/prelude.rs @@ -3,6 +3,7 @@ pub use crate::graphics::{ bitmap::*, bitmapatlas::*, + color::*, font::*, indexed::{ *, diff --git a/ggdt/src/graphics/mod.rs b/ggdt/src/graphics/mod.rs index e74b839..47cb75e 100644 --- a/ggdt/src/graphics/mod.rs +++ b/ggdt/src/graphics/mod.rs @@ -2,6 +2,7 @@ use num_traits::{PrimInt, Unsigned}; pub mod bitmap; pub mod bitmapatlas; +pub mod color; pub mod font; pub mod indexed; pub mod rgb; diff --git a/ggdt/src/graphics/prelude.rs b/ggdt/src/graphics/prelude.rs index 71586d3..dc56b36 100644 --- a/ggdt/src/graphics/prelude.rs +++ b/ggdt/src/graphics/prelude.rs @@ -2,6 +2,7 @@ pub use crate::graphics::{ *, bitmap::*, bitmapatlas::*, + color::*, font::*, indexed::prelude::*, rgb::prelude::*, diff --git a/ggdt/src/graphics/rgb/prelude.rs b/ggdt/src/graphics/rgb/prelude.rs index 749ef71..0938162 100644 --- a/ggdt/src/graphics/rgb/prelude.rs +++ b/ggdt/src/graphics/rgb/prelude.rs @@ -3,6 +3,7 @@ pub use crate::graphics::{ bitmap::*, bitmapatlas::*, + color::*, font::*, Pixel, rgb::{