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:
parent
06f055c042
commit
2b414072bc
137
ggdt/src/graphics/color.rs
Normal file
137
ggdt/src/graphics/color.rs
Normal 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
|
||||||
|
}
|
|
@ -5,8 +5,9 @@ use std::path::Path;
|
||||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::graphics::color::from_rgb32;
|
||||||
use crate::graphics::indexed::bitmap::Bitmap;
|
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;
|
use crate::utils::bytes::ReadFixedLengthByteArray;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
|
|
|
@ -5,7 +5,8 @@ use std::path::Path;
|
||||||
use byteorder::{ReadBytesExt, WriteBytesExt};
|
use byteorder::{ReadBytesExt, WriteBytesExt};
|
||||||
use thiserror::Error;
|
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::math::lerp;
|
||||||
use crate::utils::bytes::ReadFixedLengthByteArray;
|
use crate::utils::bytes::ReadFixedLengthByteArray;
|
||||||
|
|
||||||
|
|
|
@ -7,155 +7,17 @@ use std::path::Path;
|
||||||
use byteorder::{ReadBytesExt, WriteBytesExt};
|
use byteorder::{ReadBytesExt, WriteBytesExt};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::graphics::color::{from_rgb32, lerp_rgb32, to_rgb32};
|
||||||
use crate::graphics::indexed::bitmap::Bitmap;
|
use crate::graphics::indexed::bitmap::Bitmap;
|
||||||
use crate::NUM_COLORS;
|
use crate::NUM_COLORS;
|
||||||
use crate::utils::abs_diff;
|
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> {}
|
pub trait ColorRange: RangeBounds<u8> + Iterator<Item=u8> {}
|
||||||
|
|
||||||
impl<T> ColorRange for T where T: 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");
|
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
|
// vga bios (0-63) format
|
||||||
fn read_palette_6bit<T: ReadBytesExt>(
|
fn read_palette_6bit<T: ReadBytesExt>(
|
||||||
reader: &mut T,
|
reader: &mut T,
|
||||||
|
@ -653,6 +515,8 @@ impl IndexMut<u8> for Palette {
|
||||||
mod tests {
|
mod tests {
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
|
|
||||||
|
use crate::graphics::color::{from_argb32, to_argb32};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
pub use crate::graphics::{
|
pub use crate::graphics::{
|
||||||
bitmap::*,
|
bitmap::*,
|
||||||
bitmapatlas::*,
|
bitmapatlas::*,
|
||||||
|
color::*,
|
||||||
font::*,
|
font::*,
|
||||||
indexed::{
|
indexed::{
|
||||||
*,
|
*,
|
||||||
|
|
|
@ -2,6 +2,7 @@ use num_traits::{PrimInt, Unsigned};
|
||||||
|
|
||||||
pub mod bitmap;
|
pub mod bitmap;
|
||||||
pub mod bitmapatlas;
|
pub mod bitmapatlas;
|
||||||
|
pub mod color;
|
||||||
pub mod font;
|
pub mod font;
|
||||||
pub mod indexed;
|
pub mod indexed;
|
||||||
pub mod rgb;
|
pub mod rgb;
|
||||||
|
|
|
@ -2,6 +2,7 @@ pub use crate::graphics::{
|
||||||
*,
|
*,
|
||||||
bitmap::*,
|
bitmap::*,
|
||||||
bitmapatlas::*,
|
bitmapatlas::*,
|
||||||
|
color::*,
|
||||||
font::*,
|
font::*,
|
||||||
indexed::prelude::*,
|
indexed::prelude::*,
|
||||||
rgb::prelude::*,
|
rgb::prelude::*,
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
pub use crate::graphics::{
|
pub use crate::graphics::{
|
||||||
bitmap::*,
|
bitmap::*,
|
||||||
bitmapatlas::*,
|
bitmapatlas::*,
|
||||||
|
color::*,
|
||||||
font::*,
|
font::*,
|
||||||
Pixel,
|
Pixel,
|
||||||
rgb::{
|
rgb::{
|
||||||
|
|
Loading…
Reference in a new issue