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.
This commit is contained in:
Gered 2023-03-10 11:11:04 -05:00
parent 06f055c042
commit 2b414072bc
8 changed files with 149 additions and 142 deletions

137
ggdt/src/graphics/color.rs Normal file
View file

@ -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
}

View file

@ -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)]

View file

@ -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;

View file

@ -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<u8> + Iterator<Item = u8>` to `ColorRange`
/// Common trait to represent a range of indexed colour values.
pub trait ColorRange: RangeBounds<u8> + Iterator<Item=u8> {}
impl<T> ColorRange for T where T: RangeBounds<u8> + Iterator<Item=u8> {}
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<T: ReadBytesExt>(
reader: &mut T,
@ -653,6 +515,8 @@ impl IndexMut<u8> for Palette {
mod tests {
use tempfile::TempDir;
use crate::graphics::color::{from_argb32, to_argb32};
use super::*;
#[test]

View file

@ -3,6 +3,7 @@
pub use crate::graphics::{
bitmap::*,
bitmapatlas::*,
color::*,
font::*,
indexed::{
*,

View file

@ -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;

View file

@ -2,6 +2,7 @@ pub use crate::graphics::{
*,
bitmap::*,
bitmapatlas::*,
color::*,
font::*,
indexed::prelude::*,
rgb::prelude::*,

View file

@ -3,6 +3,7 @@
pub use crate::graphics::{
bitmap::*,
bitmapatlas::*,
color::*,
font::*,
Pixel,
rgb::{