use crate::alpha::AlphaMode;
use crate::error::{Error, Result};
use crate::format_kind::{FormatFamily, FormatKind, classify};
use crate::surface::{ColorSpace, Surface};
use crate::vk_format::FormatExt;
use super::alpha;
use super::buffer::Buffer;
use super::store_kernels as k;
fn srgb_oetf_scalar(c: f32) -> f32 {
if c <= 0.0031308 {
c * 12.92
} else {
1.055 * c.clamp(0.0, 1.0).powf(1.0 / 2.4) - 0.055
}
}
pub fn store_f32(
mut buf: Buffer<f32>,
target_format: ktx2::Format,
target_color_space: ColorSpace,
target_alpha: AlphaMode,
) -> Result<Surface> {
profiling::scope!("store_f32");
let info = classify(target_format, target_color_space).ok_or_else(|| {
Error::UnsupportedFormat(format!(
"float pipeline: unsupported target format {target_format:?}"
))
})?;
if info.family.is_integer() {
return Err(Error::UnsupportedConversion(format!(
"cannot store from float pipeline to integer format {target_format:?}"
)));
}
if target_alpha == AlphaMode::Straight {
alpha::unpremultiply_f32(&mut buf);
}
let kind_is_srgb_native = matches!(
info.kind,
FormatKind::Srgb8 | FormatKind::Bgra8Srgb | FormatKind::Bgr8Srgb,
);
let kind_has_srgb_variant = matches!(
info.kind,
FormatKind::U8 | FormatKind::Bgra8 | FormatKind::Bgr8,
);
let need_scalar_oetf =
target_color_space == ColorSpace::Srgb && !kind_is_srgb_native && !kind_has_srgb_variant;
if need_scalar_oetf {
profiling::scope!("srgb_oetf_scalar_f32");
for p in buf.pixels.iter_mut() {
p[0] = srgb_oetf_scalar(p[0]);
p[1] = srgb_oetf_scalar(p[1]);
p[2] = srgb_oetf_scalar(p[2]);
}
}
let write_as_srgb =
kind_is_srgb_native || (target_color_space == ColorSpace::Srgb && kind_has_srgb_variant);
let data = match (info.kind, write_as_srgb) {
(FormatKind::U8, true) => k::store_srgb8_f32(&buf, info.channels),
(FormatKind::U8, false) => k::store_u8_unorm_f32(&buf, info.channels),
(FormatKind::I8, _) => k::store_i8_snorm_f32(&buf, info.channels),
(FormatKind::Srgb8, _) => k::store_srgb8_f32(&buf, info.channels),
(FormatKind::Bgra8, true) => k::store_bgra8_srgb_f32(&buf),
(FormatKind::Bgra8, false) => k::store_bgra8_unorm_f32(&buf),
(FormatKind::Bgra8Srgb, _) => k::store_bgra8_srgb_f32(&buf),
(FormatKind::Bgr8, true) => k::store_bgr8_srgb_f32(&buf),
(FormatKind::Bgr8, false) => k::store_bgr8_unorm_f32(&buf),
(FormatKind::Bgr8Srgb, _) => k::store_bgr8_srgb_f32(&buf),
(FormatKind::U16, _) => k::store_u16_unorm_f32(&buf, info.channels),
(FormatKind::I16, _) => k::store_i16_snorm_f32(&buf, info.channels),
(FormatKind::F16, _) => k::store_f16_f32(&buf, info.channels),
(FormatKind::F32, _) => k::store_f32_f32(&buf, info.channels),
(other, _) => {
return Err(Error::UnsupportedFormat(format!(
"float pipeline: unsupported target kind {other:?}"
)));
}
};
let bpp = target_format.bytes_per_pixel().ok_or_else(|| {
Error::UnsupportedFormat(format!(
"cannot determine bytes_per_pixel for {target_format:?}"
))
})?;
let stride = buf.width * bpp as u32;
Ok(Surface {
data,
width: buf.width,
height: buf.height,
stride,
format: target_format,
color_space: target_color_space,
alpha: target_alpha,
})
}
pub fn store_f64(
mut buf: Buffer<f64>,
target_format: ktx2::Format,
target_color_space: ColorSpace,
target_alpha: AlphaMode,
) -> Result<Surface> {
profiling::scope!("store_f64");
let info = classify(target_format, target_color_space).ok_or_else(|| {
Error::UnsupportedFormat(format!(
"f64 pipeline: unsupported target format {target_format:?}"
))
})?;
if !matches!(info.family, FormatFamily::Float) {
return Err(Error::UnsupportedConversion(format!(
"f64 pipeline can only store to float targets, got {target_format:?}"
)));
}
if target_alpha == AlphaMode::Straight {
alpha::unpremultiply_f64(&mut buf);
}
let data = match info.kind {
FormatKind::F32 => k::store_f32_f64(&buf, info.channels),
FormatKind::F64 => k::store_f64_f64(&buf, info.channels),
other => {
return Err(Error::UnsupportedFormat(format!(
"f64 pipeline: unsupported target kind {other:?}"
)));
}
};
let bpp = target_format.bytes_per_pixel().ok_or_else(|| {
Error::UnsupportedFormat(format!(
"cannot determine bytes_per_pixel for {target_format:?}"
))
})?;
let stride = buf.width * bpp as u32;
Ok(Surface {
data,
width: buf.width,
height: buf.height,
stride,
format: target_format,
color_space: target_color_space,
alpha: target_alpha,
})
}
pub fn store_u32(
buf: Buffer<u32>,
target_format: ktx2::Format,
target_alpha: AlphaMode,
) -> Result<Surface> {
profiling::scope!("store_u32");
let info = classify(target_format, ColorSpace::Linear).ok_or_else(|| {
Error::UnsupportedFormat(format!(
"u32 pipeline: unsupported target format {target_format:?}"
))
})?;
if !info.family.is_integer() {
return Err(Error::UnsupportedConversion(format!(
"u32 pipeline can only store to integer targets, got {target_format:?}"
)));
}
let data = match info.kind {
FormatKind::U8 => k::store_u8_uint_u32(&buf, info.channels),
FormatKind::I8 => k::store_i8_sint_u32(&buf, info.channels),
FormatKind::U16 => k::store_u16_uint_u32(&buf, info.channels),
FormatKind::I16 => k::store_i16_sint_u32(&buf, info.channels),
FormatKind::U32 => k::store_u32_uint_u32(&buf, info.channels),
FormatKind::I32 => k::store_i32_sint_u32(&buf, info.channels),
other => {
return Err(Error::UnsupportedFormat(format!(
"u32 pipeline: unsupported target kind {other:?}"
)));
}
};
let bpp = target_format.bytes_per_pixel().ok_or_else(|| {
Error::UnsupportedFormat(format!(
"cannot determine bytes_per_pixel for {target_format:?}"
))
})?;
let stride = buf.width * bpp as u32;
Ok(Surface {
data,
width: buf.width,
height: buf.height,
stride,
format: target_format,
color_space: ColorSpace::Linear,
alpha: target_alpha,
})
}
pub fn store_u64(
buf: Buffer<u64>,
target_format: ktx2::Format,
target_alpha: AlphaMode,
) -> Result<Surface> {
profiling::scope!("store_u64");
let info = classify(target_format, ColorSpace::Linear).ok_or_else(|| {
Error::UnsupportedFormat(format!(
"u64 pipeline: unsupported target format {target_format:?}"
))
})?;
if !info.family.is_integer() {
return Err(Error::UnsupportedConversion(format!(
"u64 pipeline can only store to integer targets, got {target_format:?}"
)));
}
let data = match info.kind {
FormatKind::U64 => k::store_u64_uint_u64(&buf, info.channels),
FormatKind::I64 => k::store_i64_sint_u64(&buf, info.channels),
other => {
return Err(Error::UnsupportedFormat(format!(
"u64 pipeline: unsupported target kind {other:?}"
)));
}
};
let bpp = target_format.bytes_per_pixel().ok_or_else(|| {
Error::UnsupportedFormat(format!(
"cannot determine bytes_per_pixel for {target_format:?}"
))
})?;
let stride = buf.width * bpp as u32;
Ok(Surface {
data,
width: buf.width,
height: buf.height,
stride,
format: target_format,
color_space: ColorSpace::Linear,
alpha: target_alpha,
})
}