Skip to content

Commit

Permalink
Fix KTX2 R8_SRGB, R8_UNORM, R8G8_SRGB, R8G8_UNORM, R8G8B8_SRGB, R8G8B…
Browse files Browse the repository at this point in the history
…8_UNORM support (bevyengine#4594)

# Objective

- Fixes bevyengine#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
  • Loading branch information
superdump authored and ItsDoot committed Feb 1, 2023
1 parent 90f0d00 commit b84d9ca
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 8 deletions.
12 changes: 12 additions & 0 deletions crates/bevy_render/src/color/colorspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 0 additions & 1 deletion crates/bevy_render/src/color/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down
6 changes: 5 additions & 1 deletion crates/bevy_render/src/texture/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
56 changes: 50 additions & 6 deletions crates/bevy_render/src/texture/ktx2.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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
}
Expand All @@ -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
Expand Down

0 comments on commit b84d9ca

Please sign in to comment.