#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FormatKind {
U8,
I8,
U16,
I16,
F16,
U32,
I32,
F32,
U64,
I64,
F64,
Srgb8,
Bgra8,
Bgra8Srgb,
Bgr8,
Bgr8Srgb,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FormatFamily {
Unorm,
Snorm,
Uint,
Sint,
Float,
Srgb,
}
impl FormatFamily {
pub fn is_integer(self) -> bool {
matches!(self, FormatFamily::Uint | FormatFamily::Sint)
}
pub fn is_float_side(self) -> bool {
!self.is_integer()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct FormatInfo {
pub kind: FormatKind,
pub family: FormatFamily,
pub channels: usize,
}
pub fn classify(fmt: ktx2::Format, color_space: crate::surface::ColorSpace) -> Option<FormatInfo> {
use crate::surface::ColorSpace;
use crate::vk_format::FormatExt;
use FormatFamily::*;
use FormatKind::*;
use ktx2::Format as F;
let (base, _) = fmt.normalize();
let (mut kind, mut family, channels) = match base {
F::R8_UNORM => (U8, Unorm, 1),
F::R8_SNORM => (I8, Snorm, 1),
F::R8_UINT => (U8, Uint, 1),
F::R8_SINT => (I8, Sint, 1),
F::R8G8_UNORM => (U8, Unorm, 2),
F::R8G8_SNORM => (I8, Snorm, 2),
F::R8G8_UINT => (U8, Uint, 2),
F::R8G8_SINT => (I8, Sint, 2),
F::R8G8B8_UNORM => (U8, Unorm, 3),
F::R8G8B8_SNORM => (I8, Snorm, 3),
F::R8G8B8_UINT => (U8, Uint, 3),
F::R8G8B8_SINT => (I8, Sint, 3),
F::B8G8R8_UNORM => (Bgr8, Unorm, 3),
F::R8G8B8A8_UNORM => (U8, Unorm, 4),
F::R8G8B8A8_SNORM => (I8, Snorm, 4),
F::R8G8B8A8_UINT => (U8, Uint, 4),
F::R8G8B8A8_SINT => (I8, Sint, 4),
F::B8G8R8A8_UNORM => (Bgra8, Unorm, 4),
F::R16_UNORM => (U16, Unorm, 1),
F::R16_SNORM => (I16, Snorm, 1),
F::R16_UINT => (U16, Uint, 1),
F::R16_SINT => (I16, Sint, 1),
F::R16_SFLOAT => (F16, Float, 1),
F::R16G16_UNORM => (U16, Unorm, 2),
F::R16G16_SNORM => (I16, Snorm, 2),
F::R16G16_UINT => (U16, Uint, 2),
F::R16G16_SINT => (I16, Sint, 2),
F::R16G16_SFLOAT => (F16, Float, 2),
F::R16G16B16_UNORM => (U16, Unorm, 3),
F::R16G16B16_SNORM => (I16, Snorm, 3),
F::R16G16B16_UINT => (U16, Uint, 3),
F::R16G16B16_SINT => (I16, Sint, 3),
F::R16G16B16_SFLOAT => (F16, Float, 3),
F::R16G16B16A16_UNORM => (U16, Unorm, 4),
F::R16G16B16A16_SNORM => (I16, Snorm, 4),
F::R16G16B16A16_UINT => (U16, Uint, 4),
F::R16G16B16A16_SINT => (I16, Sint, 4),
F::R16G16B16A16_SFLOAT => (F16, Float, 4),
F::R32_UINT => (U32, Uint, 1),
F::R32_SINT => (I32, Sint, 1),
F::R32_SFLOAT => (F32, Float, 1),
F::R32G32_UINT => (U32, Uint, 2),
F::R32G32_SINT => (I32, Sint, 2),
F::R32G32_SFLOAT => (F32, Float, 2),
F::R32G32B32_UINT => (U32, Uint, 3),
F::R32G32B32_SINT => (I32, Sint, 3),
F::R32G32B32_SFLOAT => (F32, Float, 3),
F::R32G32B32A32_UINT => (U32, Uint, 4),
F::R32G32B32A32_SINT => (I32, Sint, 4),
F::R32G32B32A32_SFLOAT => (F32, Float, 4),
F::R64_UINT => (U64, Uint, 1),
F::R64_SINT => (I64, Sint, 1),
F::R64_SFLOAT => (F64, Float, 1),
F::R64G64_UINT => (U64, Uint, 2),
F::R64G64_SINT => (I64, Sint, 2),
F::R64G64_SFLOAT => (F64, Float, 2),
F::R64G64B64_UINT => (U64, Uint, 3),
F::R64G64B64_SINT => (I64, Sint, 3),
F::R64G64B64_SFLOAT => (F64, Float, 3),
F::R64G64B64A64_UINT => (U64, Uint, 4),
F::R64G64B64A64_SINT => (I64, Sint, 4),
F::R64G64B64A64_SFLOAT => (F64, Float, 4),
_ => return None,
};
if color_space == ColorSpace::Srgb {
match kind {
U8 => {
kind = Srgb8;
family = Srgb;
}
Bgra8 => {
kind = Bgra8Srgb;
family = Srgb;
}
Bgr8 => {
kind = Bgr8Srgb;
family = Srgb;
}
_ => {}
}
}
Some(FormatInfo {
kind,
family,
channels,
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::surface::ColorSpace;
use ktx2::Format as F;
#[test]
fn classify_rgba8_unorm() {
let info = classify(F::R8G8B8A8_UNORM, ColorSpace::Linear).unwrap();
assert_eq!(info.kind, FormatKind::U8);
assert_eq!(info.family, FormatFamily::Unorm);
assert_eq!(info.channels, 4);
}
#[test]
fn classify_r8_uint_vs_unorm() {
let uint = classify(F::R8_UINT, ColorSpace::Linear).unwrap();
let unorm = classify(F::R8_UNORM, ColorSpace::Linear).unwrap();
assert_eq!(uint.kind, unorm.kind); assert_ne!(uint.family, unorm.family); }
#[test]
fn classify_color_space_drives_srgb() {
let linear = classify(F::R8G8B8A8_UNORM, ColorSpace::Linear).unwrap();
assert_eq!(linear.kind, FormatKind::U8);
assert_eq!(linear.family, FormatFamily::Unorm);
let srgb = classify(F::R8G8B8A8_UNORM, ColorSpace::Srgb).unwrap();
assert_eq!(srgb.kind, FormatKind::Srgb8);
assert_eq!(srgb.family, FormatFamily::Srgb);
}
#[test]
fn classify_srgb_format_is_normalized() {
let from_srgb_format = classify(F::B8G8R8A8_SRGB, ColorSpace::Srgb).unwrap();
let from_normalized = classify(F::B8G8R8A8_UNORM, ColorSpace::Srgb).unwrap();
assert_eq!(from_srgb_format, from_normalized);
assert_eq!(from_normalized.kind, FormatKind::Bgra8Srgb);
assert_eq!(from_normalized.family, FormatFamily::Srgb);
}
#[test]
fn classify_srgb_color_space_no_effect_on_16bit() {
let info = classify(F::R16G16B16A16_UNORM, ColorSpace::Srgb).unwrap();
assert_eq!(info.kind, FormatKind::U16);
assert_eq!(info.family, FormatFamily::Unorm);
}
#[test]
fn classify_rgba32_sfloat() {
let info = classify(F::R32G32B32A32_SFLOAT, ColorSpace::Linear).unwrap();
assert_eq!(info.kind, FormatKind::F32);
assert_eq!(info.family, FormatFamily::Float);
}
#[test]
fn classify_r64_uint() {
let info = classify(F::R64_UINT, ColorSpace::Linear).unwrap();
assert_eq!(info.kind, FormatKind::U64);
assert_eq!(info.family, FormatFamily::Uint);
}
#[test]
fn classify_unsupported_packed() {
assert!(classify(F::R5G6B5_UNORM_PACK16, ColorSpace::Linear).is_none());
}
#[test]
fn classify_unsupported_compressed() {
assert!(classify(F::BC7_UNORM_BLOCK, ColorSpace::Linear).is_none());
}
}