use rafx_api::{RafxResourceType, RafxResult};
use rafx_framework::upload::GpuImageDataColorSpace;
use rafx_framework::{ImageResource, ImageViewResource, ResourceArc};
use serde::{Deserialize, Serialize};
use type_uuid::*;
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub enum ImageAssetColorSpaceConfig {
Srgb,
Linear,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)]
pub enum ImageAssetMipGeneration {
NoMips,
Precomupted,
Runtime,
}
impl Into<GpuImageDataColorSpace> for ImageAssetColorSpaceConfig {
fn into(self) -> GpuImageDataColorSpace {
match self {
ImageAssetColorSpaceConfig::Srgb => GpuImageDataColorSpace::Srgb,
ImageAssetColorSpaceConfig::Linear => GpuImageDataColorSpace::Linear,
}
}
}
#[derive(Serialize, Deserialize, Clone, Copy, Debug)]
pub enum ImageAssetBasisCompressionType {
Etc1S,
Uastc,
}
#[cfg(feature = "basis-universal")]
impl Into<basis_universal::BasisTextureFormat> for ImageAssetBasisCompressionType {
fn into(self) -> basis_universal::BasisTextureFormat {
match self {
ImageAssetBasisCompressionType::Etc1S => basis_universal::BasisTextureFormat::ETC1S,
ImageAssetBasisCompressionType::Uastc => basis_universal::BasisTextureFormat::UASTC4x4,
}
}
}
#[derive(Serialize, Deserialize, Clone, Copy, Debug)]
pub struct ImageAssetBasisCompressionSettings {
pub compression_type: ImageAssetBasisCompressionType,
pub quality: u32,
}
#[cfg(feature = "basis-universal")]
impl ImageAssetBasisCompressionSettings {
pub fn default_uastc() -> Self {
ImageAssetBasisCompressionSettings {
compression_type: ImageAssetBasisCompressionType::Uastc,
quality: basis_universal::UASTC_QUALITY_DEFAULT,
}
}
pub fn default_etc1s() -> Self {
ImageAssetBasisCompressionSettings {
compression_type: ImageAssetBasisCompressionType::Etc1S,
quality: basis_universal::ETC1S_QUALITY_DEFAULT,
}
}
}
#[derive(Serialize, Deserialize, Clone, Copy, Debug)]
#[allow(non_camel_case_types)]
pub enum ImageAssetDataFormat {
RGBA32_Linear,
RGBA32_Srgb,
Basis_Linear,
Basis_Srgb,
BC1_UNorm_Linear,
BC1_UNorm_Srgb,
BC2_UNorm_Linear,
BC2_UNorm_Srgb,
BC3_UNorm_Linear,
BC3_UNorm_Srgb,
BC4_UNorm,
BC4_SNorm,
BC5_UNorm,
BC5_SNorm,
BC6H_UFloat,
BC6H_SFloat,
BC7_Unorm_Linear,
BC7_Unorm_Srgb,
}
#[derive(Serialize, Deserialize, Clone, Copy, Debug)]
pub enum ImageAssetDataFormatConfig {
Uncompressed,
BasisCompressed(ImageAssetBasisCompressionSettings),
}
#[derive(Serialize, Deserialize, Clone)]
pub struct ImageAssetDataMipLevel {
pub width: u32,
pub height: u32,
#[serde(with = "serde_bytes")]
pub bytes: Vec<u8>,
}
#[derive(Serialize, Deserialize, Clone)]
pub struct ImageAssetDataLayer {
pub mip_levels: Vec<ImageAssetDataMipLevel>,
}
#[derive(Serialize, Deserialize, Clone)]
pub struct ImageAssetDataPayloadSubresources {
pub layers: Vec<ImageAssetDataLayer>,
}
#[derive(Serialize, Deserialize, Clone)]
pub struct ImageAssetDataPayloadSingleBuffer {
#[serde(with = "serde_bytes")]
pub buffer: Vec<u8>,
}
#[derive(Serialize, Deserialize, Clone)]
pub enum ImageAssetDataPayload {
Subresources(ImageAssetDataPayloadSubresources),
SingleBuffer(ImageAssetDataPayloadSingleBuffer),
}
#[derive(TypeUuid, Serialize, Deserialize, Clone)]
#[uuid = "e6166902-8716-401b-9d2e-8b01701c5626"]
pub struct ImageAssetData {
pub width: u32,
pub height: u32,
pub format: ImageAssetDataFormat,
pub resource_type: RafxResourceType,
pub generate_mips_at_runtime: bool,
pub data: ImageAssetDataPayload,
}
impl std::fmt::Debug for ImageAssetData {
fn fmt(
&self,
f: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
f.debug_struct("Point")
.field("width", &self.width)
.field("width", &self.height)
.field("format", &self.format)
.finish()
}
}
impl ImageAssetData {
pub fn default_format_and_mip_generation(
) -> (ImageAssetDataFormatConfig, ImageAssetMipGeneration) {
let compress_textures = false;
if compress_textures {
#[cfg(feature = "basis-universal")]
{
let basis_settings = ImageAssetBasisCompressionSettings::default_uastc();
let format_config = ImageAssetDataFormatConfig::BasisCompressed(basis_settings);
let mipmap_generation = ImageAssetMipGeneration::Precomupted;
(format_config, mipmap_generation)
}
#[cfg(not(feature = "basis-universal"))]
{
unimplemented!("Not built with basis-universal feature")
}
} else {
let format_config = ImageAssetDataFormatConfig::Uncompressed;
let mipmap_generation = ImageAssetMipGeneration::Runtime;
(format_config, mipmap_generation)
}
}
pub fn from_raw_rgba32(
width: u32,
height: u32,
color_space: ImageAssetColorSpaceConfig,
format_config: ImageAssetDataFormatConfig,
mip_generation: ImageAssetMipGeneration,
resource_type: RafxResourceType,
raw_rgba32: &[u8],
) -> RafxResult<ImageAssetData> {
match format_config {
ImageAssetDataFormatConfig::Uncompressed => {
let generate_mips_at_runtime = match mip_generation {
ImageAssetMipGeneration::NoMips => false,
ImageAssetMipGeneration::Precomupted => Err(
"Uncompressed ImageAssetDataFormatConfig cannot store precomputed mipmaps",
)?,
ImageAssetMipGeneration::Runtime => true,
};
let mip = ImageAssetDataMipLevel {
width,
height,
bytes: raw_rgba32.to_vec(),
};
let layer = ImageAssetDataLayer {
mip_levels: vec![mip],
};
let format = match color_space {
ImageAssetColorSpaceConfig::Linear => ImageAssetDataFormat::RGBA32_Linear,
ImageAssetColorSpaceConfig::Srgb => ImageAssetDataFormat::RGBA32_Srgb,
};
Ok(ImageAssetData {
width,
height,
format,
generate_mips_at_runtime,
resource_type,
data: ImageAssetDataPayload::Subresources(ImageAssetDataPayloadSubresources {
layers: vec![layer],
}),
})
}
#[cfg(not(feature = "basis-universal"))]
ImageAssetDataFormatConfig::BasisCompressed(_) => {
unimplemented!("crate not built with basis-universal feature");
}
#[cfg(feature = "basis-universal")]
ImageAssetDataFormatConfig::BasisCompressed(settings) => {
let generate_mips_at_runtime = match mip_generation {
ImageAssetMipGeneration::NoMips => false,
ImageAssetMipGeneration::Precomupted => false,
ImageAssetMipGeneration::Runtime => true,
};
let basis_color_space = match color_space {
ImageAssetColorSpaceConfig::Srgb => basis_universal::ColorSpace::Srgb,
ImageAssetColorSpaceConfig::Linear => basis_universal::ColorSpace::Linear,
};
let mut compressor_params = basis_universal::CompressorParams::new();
compressor_params.set_basis_format(settings.compression_type.into());
compressor_params
.set_generate_mipmaps(mip_generation == ImageAssetMipGeneration::Precomupted);
compressor_params.set_color_space(basis_color_space);
match settings.compression_type {
ImageAssetBasisCompressionType::Etc1S => {
let quality_level = settings.quality.clamp(
basis_universal::ETC1S_QUALITY_MIN,
basis_universal::ETC1S_QUALITY_MAX,
);
compressor_params.set_etc1s_quality_level(quality_level)
}
ImageAssetBasisCompressionType::Uastc => {
let quality_level = settings.quality.clamp(
basis_universal::ETC1S_QUALITY_MIN,
basis_universal::ETC1S_QUALITY_MAX,
);
compressor_params.set_uastc_quality_level(quality_level)
}
}
let mut source_image = compressor_params.source_image_mut(0);
source_image.init(raw_rgba32, width, height, 4);
let mut compressor = basis_universal::Compressor::new(4);
unsafe {
compressor.init(&compressor_params);
log::debug!("Compressing texture");
compressor.process().unwrap();
log::debug!("Compressed texture");
}
let compressed_basis_data = compressor.basis_file();
let format = match color_space {
ImageAssetColorSpaceConfig::Linear => ImageAssetDataFormat::Basis_Linear,
ImageAssetColorSpaceConfig::Srgb => ImageAssetDataFormat::Basis_Srgb,
};
Ok(ImageAssetData {
width,
height,
format,
generate_mips_at_runtime,
resource_type,
data: ImageAssetDataPayload::SingleBuffer(ImageAssetDataPayloadSingleBuffer {
buffer: compressed_basis_data.to_vec(),
}),
})
}
}
}
}
#[derive(TypeUuid, Clone)]
#[uuid = "7a67b850-17f9-4877-8a6e-293a1589bbd8"]
pub struct ImageAsset {
pub image: ResourceArc<ImageResource>,
pub image_view: ResourceArc<ImageViewResource>,
}