Fix KTX2 R8_SRGB, R8_UNORM, R8G8_SRGB, R8G8_UNORM, R8G8B8_SRGB, R8G8B8_UNORM support (#4594)

# Objective

- Fixes #4592

## Solution

- Implement `SrgbColorSpace` for `u8` via `f32`
- Convert KTX2 R8 and R8G8 non-linear sRGB to wgpu `R8Unorm` and `Rg8Unorm` as non-linear sRGB are not supported by wgpu for these formats
- Convert KTX2 R8G8B8 formats to `Rgba8Unorm` and `Rgba8UnormSrgb` by adding an alpha channel as the Rgb variants don't exist in wgpu

---

## Changelog

- Added: Support for KTX2 `R8_SRGB`, `R8_UNORM`, `R8G8_SRGB`, `R8G8_UNORM`, `R8G8B8_SRGB`, `R8G8B8_UNORM` formats by converting to supported wgpu formats as appropriate
This commit is contained in:
Robert Swain 2023-01-30 09:04:08 +00:00
parent daa45ebb4d
commit c9a53bf5dd
4 changed files with 67 additions and 8 deletions

View file

@ -31,6 +31,18 @@ impl SrgbColorSpace for f32 {
}
}
impl SrgbColorSpace for u8 {
#[inline]
fn linear_to_nonlinear_srgb(self) -> Self {
((self as f32 / u8::MAX as f32).linear_to_nonlinear_srgb() * u8::MAX as f32) as u8
}
#[inline]
fn nonlinear_to_linear_srgb(self) -> Self {
((self as f32 / u8::MAX as f32).nonlinear_to_linear_srgb() * u8::MAX as f32) as u8
}
}
pub struct HslRepresentation;
impl HslRepresentation {
/// converts a color in HLS space to sRGB space

View file

@ -2,7 +2,6 @@ mod colorspace;
pub use colorspace::*;
use crate::color::{HslRepresentation, SrgbColorSpace};
use bevy_math::{Vec3, Vec4};
use bevy_reflect::{FromReflect, Reflect, ReflectDeserialize, ReflectSerialize};
use serde::{Deserialize, Serialize};

View file

@ -406,9 +406,13 @@ pub enum DataFormat {
#[derive(Clone, Copy, Debug)]
pub enum TranscodeFormat {
Etc1s,
Uastc(DataFormat),
// Has to be transcoded to R8Unorm for use with `wgpu`
R8UnormSrgb,
// Has to be transcoded to R8G8Unorm for use with `wgpu`
Rg8UnormSrgb,
// Has to be transcoded to Rgba8 for use with `wgpu`
Rgb8,
Uastc(DataFormat),
}
/// An error that occurs when loading a texture

View file

@ -1,6 +1,7 @@
#[cfg(any(feature = "flate2", feature = "ruzstd"))]
use std::io::Read;
use crate::color::SrgbColorSpace;
#[cfg(feature = "basis-universal")]
use basis_universal::{
DecodeFlags, LowLevelUastcTranscoder, SliceParametersUastc, TranscoderBlockFormat,
@ -86,6 +87,44 @@ pub fn ktx2_buffer_to_image(
TextureError::FormatRequiresTranscodingError(transcode_format) => {
let mut transcoded = vec![Vec::default(); levels.len()];
let texture_format = match transcode_format {
TranscodeFormat::R8UnormSrgb => {
let (mut original_width, mut original_height) = (width, height);
for level_data in &levels {
transcoded.push(
level_data
.iter()
.copied()
.map(|v| v.nonlinear_to_linear_srgb())
.collect::<Vec<u8>>(),
);
// Next mip dimensions are half the current, minimum 1x1
original_width = (original_width / 2).max(1);
original_height = (original_height / 2).max(1);
}
TextureFormat::R8Unorm
}
TranscodeFormat::Rg8UnormSrgb => {
let (mut original_width, mut original_height) = (width, height);
for level_data in &levels {
transcoded.push(
level_data
.iter()
.copied()
.map(|v| v.nonlinear_to_linear_srgb())
.collect::<Vec<u8>>(),
);
// Next mip dimensions are half the current, minimum 1x1
original_width = (original_width / 2).max(1);
original_height = (original_height / 2).max(1);
}
TextureFormat::Rg8Unorm
}
TranscodeFormat::Rgb8 => {
let mut rgba = vec![255u8; width as usize * height as usize * 4];
for (level, level_data) in levels.iter().enumerate() {
@ -1163,9 +1202,9 @@ pub fn ktx2_format_to_texture_format(
Ok(match ktx2_format {
ktx2::Format::R8_UNORM | ktx2::Format::R8_SRGB => {
if is_srgb {
return Err(TextureError::UnsupportedTextureFormat(format!(
"{ktx2_format:?}"
)));
return Err(TextureError::FormatRequiresTranscodingError(
TranscodeFormat::R8UnormSrgb,
));
}
TextureFormat::R8Unorm
}
@ -1174,15 +1213,20 @@ pub fn ktx2_format_to_texture_format(
ktx2::Format::R8_SINT => TextureFormat::R8Sint,
ktx2::Format::R8G8_UNORM | ktx2::Format::R8G8_SRGB => {
if is_srgb {
return Err(TextureError::UnsupportedTextureFormat(format!(
"{ktx2_format:?}"
)));
return Err(TextureError::FormatRequiresTranscodingError(
TranscodeFormat::Rg8UnormSrgb,
));
}
TextureFormat::Rg8Unorm
}
ktx2::Format::R8G8_SNORM => TextureFormat::Rg8Snorm,
ktx2::Format::R8G8_UINT => TextureFormat::Rg8Uint,
ktx2::Format::R8G8_SINT => TextureFormat::Rg8Sint,
ktx2::Format::R8G8B8_UNORM | ktx2::Format::R8G8B8_SRGB => {
return Err(TextureError::FormatRequiresTranscodingError(
TranscodeFormat::Rgb8,
));
}
ktx2::Format::R8G8B8A8_UNORM | ktx2::Format::R8G8B8A8_SRGB => {
if is_srgb {
TextureFormat::Rgba8UnormSrgb