mirror of
https://github.com/encounter/squish-rs.git
synced 2026-07-11 06:18:39 -07:00
Update RangeFit & ColourWeights for GCN/CMPR
This commit is contained in:
@@ -20,7 +20,7 @@
|
||||
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
use core::{mem, u8};
|
||||
use core::mem;
|
||||
|
||||
use crate::Format;
|
||||
use crate::math::{f32_to_i32_clamped, Vec3};
|
||||
@@ -110,7 +110,7 @@ pub fn write4(start: &Vec3, end: &Vec3, indices: &[u8; 16], block: &mut [u8], fo
|
||||
|
||||
/// Convert a little endian 565-packed colour to 8bpc RGBA
|
||||
fn unpack_565(packed: &[u8], format: Format) -> [u8; 4] {
|
||||
assert!(packed.len() == 2);
|
||||
assert_eq!(packed.len(), 2);
|
||||
// get components
|
||||
let mut tmp = [0u8; 2];
|
||||
tmp.copy_from_slice(&packed[0..2]);
|
||||
@@ -133,7 +133,7 @@ fn unpack_565(packed: &[u8], format: Format) -> [u8; 4] {
|
||||
|
||||
/// Decompress a BC1/2/3 block to 4x4 RGBA pixels
|
||||
pub fn decompress(bytes: &[u8], format: Format) -> [[u8; 4]; 16] {
|
||||
assert!(bytes.len() == 8);
|
||||
assert_eq!(bytes.len(), 8);
|
||||
|
||||
let is_gcn = format == Format::Bc1Gcn;
|
||||
let is_bc1 = format == Format::Bc1 || is_gcn;
|
||||
|
||||
@@ -151,17 +151,17 @@ impl<'a> ColourFitImpl<'a> for ClusterFit<'a> {
|
||||
|
||||
fn compress3(&mut self) {
|
||||
let count = self.colourset.count();
|
||||
let two = Vec4::new(2.0, 2.0, 2.0, 2.0);
|
||||
let one = Vec4::new(1.0, 1.0, 1.0, 1.0);
|
||||
let half_half2 = Vec4::new(0.5, 0.5, 0.5, 0.25);
|
||||
let zero = Vec4::new(0.0, 0.0, 0.0, 0.0);
|
||||
let half = Vec4::new(0.5, 0.5, 0.5, 0.5);
|
||||
let grid = Vec4::new(31.0, 63.0, 31.0, 0.0);
|
||||
let gridrcp = Vec4::new(1.0 / 31.0, 1.0 / 63.0, 1.0 / 31.0, 0.0);
|
||||
const TWO: Vec4 = Vec4::new(2.0, 2.0, 2.0, 2.0);
|
||||
const ONE: Vec4 = Vec4::new(1.0, 1.0, 1.0, 1.0);
|
||||
const HALF_HALF2: Vec4 = Vec4::new(0.5, 0.5, 0.5, 0.25);
|
||||
const ZERO: Vec4 = Vec4::new(0.0, 0.0, 0.0, 0.0);
|
||||
const HALF: Vec4 = Vec4::new(0.5, 0.5, 0.5, 0.5);
|
||||
const GRID: Vec4 = Vec4::new(31.0, 63.0, 31.0, 0.0);
|
||||
const GRID_RCP: Vec4 = Vec4::new(1.0 / 31.0, 1.0 / 63.0, 1.0 / 31.0, 0.0);
|
||||
|
||||
// check all possible clusters and iterate on the total order
|
||||
let mut best_start = zero;
|
||||
let mut best_end = zero;
|
||||
let mut best_start = ZERO;
|
||||
let mut best_end = ZERO;
|
||||
let mut best_error = self.best_error;
|
||||
let mut best_indices = [0u8; 16];
|
||||
let mut best_iteration = 0;
|
||||
@@ -178,11 +178,11 @@ impl<'a> ColourFitImpl<'a> for ClusterFit<'a> {
|
||||
}
|
||||
|
||||
// first cluster [0,i) is at the start
|
||||
let mut part0 = zero;
|
||||
let mut part0 = ZERO;
|
||||
for i in 0..count {
|
||||
// second cluster [i,j) is halfway along
|
||||
let mut part1 =
|
||||
if i == 0 { self.points_weights[0] } else { zero };
|
||||
if i == 0 { self.points_weights[0] } else { ZERO };
|
||||
let jmin = if i == 0 { 1 } else { i };
|
||||
|
||||
for j in jmin..=count {
|
||||
@@ -190,13 +190,13 @@ impl<'a> ColourFitImpl<'a> for ClusterFit<'a> {
|
||||
let part2 = self.xsum_wsum - part1 - part0;
|
||||
|
||||
// compute least squares term directly
|
||||
let alphax_sum = part1 * half_half2 + part0;
|
||||
let alphax_sum = part1 * HALF_HALF2 + part0;
|
||||
let alpha2_sum = alphax_sum.splat_w();
|
||||
|
||||
let betax_sum = part1 * half_half2 + part2;
|
||||
let betax_sum = part1 * HALF_HALF2 + part2;
|
||||
let beta2_sum = betax_sum.splat_w();
|
||||
|
||||
let alphabeta_sum = (part1 * half_half2).splat_w();
|
||||
let alphabeta_sum = (part1 * HALF_HALF2).splat_w();
|
||||
|
||||
// compute the least-squares optimal points
|
||||
let factor =
|
||||
@@ -205,16 +205,16 @@ impl<'a> ColourFitImpl<'a> for ClusterFit<'a> {
|
||||
let b = ((betax_sum * alpha2_sum) - alphax_sum * alphabeta_sum) * factor;
|
||||
|
||||
// clamp to the grid
|
||||
let a = one.min(zero.max(a));
|
||||
let b = one.min(zero.max(b));
|
||||
let a = (grid * a + half).truncate() * gridrcp;
|
||||
let b = (grid * b + half).truncate() * gridrcp;
|
||||
let a = ONE.min(ZERO.max(a));
|
||||
let b = ONE.min(ZERO.max(b));
|
||||
let a = (GRID * a + HALF).truncate() * GRID_RCP;
|
||||
let b = (GRID * b + HALF).truncate() * GRID_RCP;
|
||||
|
||||
// compute the error (we skip the constant xxsum)
|
||||
let e1 = (a * a) * alpha2_sum + (b * b * beta2_sum);
|
||||
let e2 = (a * b * alphabeta_sum) - a * alphax_sum;
|
||||
let e3 = e2 - b * betax_sum;
|
||||
let e4 = two * e3 + e1;
|
||||
let e4 = TWO * e3 + e1;
|
||||
|
||||
// apply the channel weights to the error term
|
||||
let e5 = e4 * self.weights;
|
||||
@@ -276,19 +276,19 @@ impl<'a> ColourFitImpl<'a> for ClusterFit<'a> {
|
||||
|
||||
fn compress4(&mut self) {
|
||||
let count = self.colourset.count();
|
||||
let two = Vec4::new(2.0, 2.0, 2.0, 2.0);
|
||||
let one = Vec4::new(1.0, 1.0, 1.0, 1.0);
|
||||
let onethird_onethird2 = Vec4::new(1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0, 1.0 / 9.0);
|
||||
let twothirds_twothirds2 = Vec4::new(2.0 / 3.0, 2.0 / 3.0, 2.0 / 3.0, 4.0 / 9.0);
|
||||
let twoninths = Vec4::new(2.0 / 9.0, 2.0 / 9.0, 2.0 / 9.0, 2.0 / 9.0);
|
||||
let zero = Vec4::new(0.0, 0.0, 0.0, 0.0);
|
||||
let half = Vec4::new(0.5, 0.5, 0.5, 0.5);
|
||||
let grid = Vec4::new(31.0, 63.0, 31.0, 0.0);
|
||||
let gridrcp = Vec4::new(1.0 / 31.0, 1.0 / 63.0, 1.0 / 31.0, 0.0);
|
||||
const TWO: Vec4 = Vec4::new(2.0, 2.0, 2.0, 2.0);
|
||||
const ONE: Vec4 = Vec4::new(1.0, 1.0, 1.0, 1.0);
|
||||
const ONETHIRD_ONETHIRD2: Vec4 = Vec4::new(1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0, 1.0 / 9.0);
|
||||
const TWOTHIRDS_TWOTHIRDS2: Vec4 = Vec4::new(2.0 / 3.0, 2.0 / 3.0, 2.0 / 3.0, 4.0 / 9.0);
|
||||
const TWONINTHS: Vec4 = Vec4::new(2.0 / 9.0, 2.0 / 9.0, 2.0 / 9.0, 2.0 / 9.0);
|
||||
const ZERO: Vec4 = Vec4::new(0.0, 0.0, 0.0, 0.0);
|
||||
const HALF: Vec4 = Vec4::new(0.5, 0.5, 0.5, 0.5);
|
||||
const GRID: Vec4 = Vec4::new(31.0, 63.0, 31.0, 0.0);
|
||||
const GRID_RCP: Vec4 = Vec4::new(1.0 / 31.0, 1.0 / 63.0, 1.0 / 31.0, 0.0);
|
||||
|
||||
// check all possible clusters and iterate on the total order
|
||||
let mut best_start = zero;
|
||||
let mut best_end = zero;
|
||||
let mut best_start = ZERO;
|
||||
let mut best_end = ZERO;
|
||||
let mut best_error = self.best_error;
|
||||
let mut best_indices = [0u8; 16];
|
||||
let mut best_iteration = 0;
|
||||
@@ -306,17 +306,17 @@ impl<'a> ColourFitImpl<'a> for ClusterFit<'a> {
|
||||
}
|
||||
|
||||
// first cluster [0,i) is at the start
|
||||
let mut part0 = zero;
|
||||
let mut part0 = ZERO;
|
||||
for i in 0..count {
|
||||
// second cluster [i,j) is one third along
|
||||
let mut part1 = zero;
|
||||
let mut part1 = ZERO;
|
||||
|
||||
for j in i..=count {
|
||||
// third cluster [j, k) is two thirds along
|
||||
let mut part2 = if j == 0 {
|
||||
self.points_weights[0]
|
||||
} else {
|
||||
zero
|
||||
ZERO
|
||||
};
|
||||
let kmin = if j == 0 { 1 } else { j };
|
||||
|
||||
@@ -326,14 +326,14 @@ impl<'a> ColourFitImpl<'a> for ClusterFit<'a> {
|
||||
|
||||
// compute least squares terms directly
|
||||
let alphax_sum =
|
||||
part2 * onethird_onethird2 + (part1 * twothirds_twothirds2 + part0);
|
||||
part2 * ONETHIRD_ONETHIRD2 + (part1 * TWOTHIRDS_TWOTHIRDS2 + part0);
|
||||
let alpha2_sum = alphax_sum.splat_w();
|
||||
|
||||
let betax_sum =
|
||||
part1 * onethird_onethird2 + (part2 * twothirds_twothirds2 + part3);
|
||||
part1 * ONETHIRD_ONETHIRD2 + (part2 * TWOTHIRDS_TWOTHIRDS2 + part3);
|
||||
let beta2_sum = betax_sum.splat_w();
|
||||
|
||||
let alphabeta_sum = twoninths * (part1 + part2).splat_w();
|
||||
let alphabeta_sum = TWONINTHS * (part1 + part2).splat_w();
|
||||
|
||||
// compute the least-squares optimal points
|
||||
let factor =
|
||||
@@ -342,16 +342,16 @@ impl<'a> ColourFitImpl<'a> for ClusterFit<'a> {
|
||||
let b = ((betax_sum * alpha2_sum) - alphax_sum * alphabeta_sum) * factor;
|
||||
|
||||
// clamp to the grid
|
||||
let a = one.min(zero.max(a));
|
||||
let b = one.min(zero.max(b));
|
||||
let a = (grid * a + half).truncate() * gridrcp;
|
||||
let b = (grid * b + half).truncate() * gridrcp;
|
||||
let a = ONE.min(ZERO.max(a));
|
||||
let b = ONE.min(ZERO.max(b));
|
||||
let a = (GRID * a + HALF).truncate() * GRID_RCP;
|
||||
let b = (GRID * b + HALF).truncate() * GRID_RCP;
|
||||
|
||||
// compute the error (we skip the constant xxsum)
|
||||
let e1 = (a * a) * alpha2_sum + (b * b * beta2_sum);
|
||||
let e2 = (a * b * alphabeta_sum) - a * alphax_sum;
|
||||
let e3 = e2 - b * betax_sum;
|
||||
let e4 = two * e3 + e1;
|
||||
let e4 = TWO * e3 + e1;
|
||||
|
||||
// apply the channel weights to the error term
|
||||
let e5 = e4 * self.weights;
|
||||
|
||||
@@ -20,12 +20,10 @@
|
||||
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
use core::f32;
|
||||
|
||||
use crate::{ColourWeights, Format};
|
||||
use crate::colourblock;
|
||||
use crate::colourset::ColourSet;
|
||||
use crate::math::{Sym3x3, Vec3};
|
||||
use crate::{ColourWeights, Format};
|
||||
|
||||
use super::ColourFitImpl;
|
||||
|
||||
@@ -174,11 +172,13 @@ impl<'a> ColourFitImpl<'a> for RangeFit<'a> {
|
||||
|
||||
fn compress4(&mut self) {
|
||||
// create a codebook
|
||||
let one_third = if self.format == Format::Bc1Gcn { 3.0 / 8.0 } else { 1.0 / 3.0 };
|
||||
let two_thirds = if self.format == Format::Bc1Gcn { 5.0 / 8.0 } else { 2.0 / 3.0 };
|
||||
let codes = [
|
||||
self.start,
|
||||
self.end,
|
||||
self.start * (2.0 / 3.0) + self.end * (1.0 / 3.0),
|
||||
self.start * (1.0 / 3.0) + self.end * (2.0 / 3.0),
|
||||
self.start * two_thirds + self.end * one_third,
|
||||
self.start * one_third + self.end * two_thirds,
|
||||
];
|
||||
|
||||
if self.compression_helper(&codes) {
|
||||
|
||||
@@ -20,8 +20,6 @@
|
||||
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
use core::u32;
|
||||
|
||||
use crate::colourblock;
|
||||
use crate::colourset::ColourSet;
|
||||
use crate::math::{f32_to_i32_clamped, Vec3};
|
||||
|
||||
+63
-31
@@ -25,17 +25,18 @@
|
||||
|
||||
#![no_std]
|
||||
|
||||
#[cfg(feature="rayon")]
|
||||
use rayon::prelude::*;
|
||||
|
||||
use crate::colourfit::{ClusterFit, ColourFit, RangeFit, SingleColourFit};
|
||||
use crate::colourset::ColourSet;
|
||||
|
||||
mod alpha;
|
||||
mod colourblock;
|
||||
mod colourfit;
|
||||
mod colourset;
|
||||
mod math;
|
||||
|
||||
use crate::colourfit::{ClusterFit, ColourFit, RangeFit, SingleColourFit};
|
||||
use crate::colourset::ColourSet;
|
||||
#[cfg(feature="rayon")]
|
||||
use rayon::prelude::*;
|
||||
|
||||
/// Defines a compression format
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum Format {
|
||||
@@ -78,6 +79,9 @@ pub const COLOUR_WEIGHTS_UNIFORM: ColourWeights = [1.0, 1.0, 1.0];
|
||||
/// Weights based on the perceived brightness of each colour channel
|
||||
pub const COLOUR_WEIGHTS_PERCEPTUAL: ColourWeights = [0.2126, 0.7152, 0.0722];
|
||||
|
||||
/// Perceptual weights used for GCN/CMPR encoding
|
||||
pub const COLOUR_WEIGHTS_PERCEPTUAL_GCN: ColourWeights = [0.3086, 0.6094, 0.0820];
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Params {
|
||||
/// The compression algorithm to be used
|
||||
@@ -199,7 +203,7 @@ impl Format {
|
||||
) {
|
||||
// compress alpha block(s)
|
||||
match self {
|
||||
Format::Bc1 => {},
|
||||
Format::Bc1 | Format::Bc1Gcn => {},
|
||||
Format::Bc2 => alpha::compress_bc2(&rgba, mask, &mut output[..8]),
|
||||
Format::Bc3 => alpha::compress_bc3(&rgba, 3, mask, &mut output[..8]),
|
||||
Format::Bc4 => alpha::compress_bc3(&rgba, 0, mask, &mut output[..8]),
|
||||
@@ -211,11 +215,11 @@ impl Format {
|
||||
|
||||
// compress colour block if the format has one
|
||||
match self {
|
||||
Format::Bc1 | Format::Bc2 | Format::Bc3 => {
|
||||
Format::Bc1 | Format::Bc1Gcn | Format::Bc2 | Format::Bc3 => {
|
||||
// create the minimal point set
|
||||
let colours = ColourSet::new(&rgba, mask, self, params.weigh_colour_by_alpha);
|
||||
|
||||
let colour_offset = if self == Format::Bc1 { 0 } else { 8 };
|
||||
let colour_offset = if self == Format::Bc1 || self == Format::Bc1Gcn { 0 } else { 8 };
|
||||
let colour_block = &mut output[colour_offset..colour_offset + 8];
|
||||
|
||||
// compress with appropriate compression algorithm
|
||||
@@ -246,9 +250,9 @@ impl Format {
|
||||
let mut rgba;
|
||||
// decompress colour block
|
||||
match self {
|
||||
Format::Bc1 | Format::Bc2 | Format::Bc3 => {
|
||||
Format::Bc1 | Format::Bc1Gcn | Format::Bc2 | Format::Bc3 => {
|
||||
// get reference to the actual colour block
|
||||
let colour_offset = if self == Format::Bc1 { 0 } else { 8 };
|
||||
let colour_offset = if self == Format::Bc1 || self == Format::Bc1Gcn { 0 } else { 8 };
|
||||
let colour_block = &block[colour_offset..colour_offset + 8];
|
||||
|
||||
// decompress colour block
|
||||
@@ -261,7 +265,7 @@ impl Format {
|
||||
|
||||
// decompress alpha block(s)
|
||||
match self {
|
||||
Format::Bc1 => (),
|
||||
Format::Bc1 | Format::Bc1Gcn => (),
|
||||
Format::Bc2 => alpha::decompress_bc2(&mut rgba, &block[..8]),
|
||||
Format::Bc3 => alpha::decompress_bc3(&mut rgba, 3, &block[..8]),
|
||||
Format::Bc4 => {
|
||||
@@ -352,6 +356,8 @@ mod tests {
|
||||
fn test_storage_requirements() {
|
||||
assert_eq!(Format::Bc1.compressed_size(16, 32), 256);
|
||||
assert_eq!(Format::Bc1.compressed_size(15, 32), 256);
|
||||
assert_eq!(Format::Bc1Gcn.compressed_size(16, 32), 256);
|
||||
assert_eq!(Format::Bc1Gcn.compressed_size(15, 32), 256);
|
||||
assert_eq!(Format::Bc2.compressed_size(16, 32), 512);
|
||||
assert_eq!(Format::Bc2.compressed_size(15, 32), 512);
|
||||
assert_eq!(Format::Bc3.compressed_size(16, 32), 512);
|
||||
@@ -391,18 +397,6 @@ mod tests {
|
||||
assert_eq!(output_actual, decoded_block_gray_4x4_as_rgba());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_storage_requirements_bc1_gcn_exact() {
|
||||
let estimate = Format::Bc1Gcn.compressed_size(16, 32);
|
||||
assert_eq!(estimate, 256);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_storage_requirements_bc1_gcn_padded() {
|
||||
let estimate = Format::Bc1Gcn.compressed_size(15, 30);
|
||||
assert_eq!(estimate, 256);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bc1_compression_gray() {
|
||||
fn test(algorithm: Algorithm) {
|
||||
@@ -431,23 +425,30 @@ mod tests {
|
||||
|
||||
// A colour test-pattern (RGB) with the first row in one colour,
|
||||
// the second in another and the third and last row in a third colour.
|
||||
static DECODED_BLOCK_COLOUR_4X4: &[u8] = &[
|
||||
static DECODED_BLOCK_COLOUR_4X4: [u8; 4 * 4 * 3] = [
|
||||
255, 150, 74, 255, 150, 74, 255, 150, 74, 255, 150, 74, // row 0
|
||||
255, 120, 52, 255, 120, 52, 255, 120, 52, 255, 120, 52, // row 1
|
||||
255, 105, 41, 255, 105, 41, 255, 105, 41, 255, 105, 41, // row 2
|
||||
255, 105, 41, 255, 105, 41, 255, 105, 41, 255, 105, 41, // row 3
|
||||
];
|
||||
static DECODED_BLOCK_COLOUR_4X4_GCN: [u8; 4 * 4 * 3] = [
|
||||
255, 150, 74, 255, 150, 74, 255, 150, 74, 255, 150, 74, // row 0
|
||||
255, 121, 53, 255, 121, 53, 255, 121, 53, 255, 121, 53, // row 1
|
||||
255, 105, 41, 255, 105, 41, 255, 105, 41, 255, 105, 41, // row 2
|
||||
255, 105, 41, 255, 105, 41, 255, 105, 41, 255, 105, 41, // row 3
|
||||
];
|
||||
|
||||
// BC1 data created with AMD Compressonator v4.1.5083 and is the same as libsquish
|
||||
static ENCODED_BLOCK_COLOUR_4X4: [u8; 8] = [0xA9, 0xFC, 0x45, 0xFB, 0x00, 0xFF, 0x55, 0x55];
|
||||
static ENCODED_BLOCK_COLOUR_4X4_GCN: [u8; 8] = [0xFC, 0xA9, 0xFB, 0x45, 0x00, 0xFF, 0x55, 0x55];
|
||||
|
||||
fn decoded_block_colour_4x4_as_rgba() -> [u8; 4 * 4 * 4] {
|
||||
fn rgb_to_rgba(block: &[u8; 4 * 4 * 3]) -> [u8; 4 * 4 * 4] {
|
||||
let mut output = [0u8; 4 * 4 * 4];
|
||||
for i in 0..4 * 4 {
|
||||
output[i * 4 + 0] = DECODED_BLOCK_COLOUR_4X4[i * 3 + 0]; // R
|
||||
output[i * 4 + 1] = DECODED_BLOCK_COLOUR_4X4[i * 3 + 1]; // G
|
||||
output[i * 4 + 2] = DECODED_BLOCK_COLOUR_4X4[i * 3 + 2]; // B
|
||||
output[i * 4 + 3] = 0xFF; //A
|
||||
output[i * 4 + 0] = block[i * 3 + 0]; // R
|
||||
output[i * 4 + 1] = block[i * 3 + 1]; // G
|
||||
output[i * 4 + 2] = block[i * 3 + 2]; // B
|
||||
output[i * 4 + 3] = 0xFF; // A
|
||||
}
|
||||
output
|
||||
}
|
||||
@@ -457,7 +458,7 @@ mod tests {
|
||||
let encoded: [u8; 8] = ENCODED_BLOCK_COLOUR_4X4;
|
||||
let mut output_actual = [0u8; 4 * 4 * 4];
|
||||
Format::Bc1.decompress(&encoded, 4, 4, &mut output_actual);
|
||||
assert_eq!(output_actual, decoded_block_colour_4x4_as_rgba());
|
||||
assert_eq!(output_actual, rgb_to_rgba(&DECODED_BLOCK_COLOUR_4X4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -465,7 +466,7 @@ mod tests {
|
||||
fn test(algorithm: Algorithm) {
|
||||
let mut output_actual = [0u8; 8];
|
||||
Format::Bc1.compress(
|
||||
&decoded_block_colour_4x4_as_rgba(),
|
||||
&rgb_to_rgba(&DECODED_BLOCK_COLOUR_4X4),
|
||||
4,
|
||||
4,
|
||||
Params {
|
||||
@@ -484,4 +485,35 @@ mod tests {
|
||||
test(Algorithm::RangeFit);
|
||||
test(Algorithm::IterativeClusterFit);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bc1gcn_decompression_colour() {
|
||||
let encoded: [u8; 8] = ENCODED_BLOCK_COLOUR_4X4_GCN;
|
||||
let mut output_actual = [0u8; 4 * 4 * 4];
|
||||
Format::Bc1Gcn.decompress(&encoded, 4, 4, &mut output_actual);
|
||||
assert_eq!(output_actual, rgb_to_rgba(&DECODED_BLOCK_COLOUR_4X4_GCN));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bc1gcn_compression_colour() {
|
||||
fn test(algorithm: Algorithm) {
|
||||
let mut output_actual = [0u8; 8];
|
||||
Format::Bc1Gcn.compress(
|
||||
&rgb_to_rgba(&DECODED_BLOCK_COLOUR_4X4_GCN),
|
||||
4,
|
||||
4,
|
||||
Params {
|
||||
algorithm,
|
||||
weights: COLOUR_WEIGHTS_UNIFORM,
|
||||
weigh_colour_by_alpha: false,
|
||||
},
|
||||
&mut output_actual,
|
||||
);
|
||||
let output_expected = ENCODED_BLOCK_COLOUR_4X4_GCN;
|
||||
assert_eq!(output_actual, output_expected);
|
||||
}
|
||||
|
||||
// only RangeFit implemented for GCN
|
||||
test(Algorithm::RangeFit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,19 +32,19 @@ pub struct Vec3 {
|
||||
}
|
||||
|
||||
impl Vec3 {
|
||||
pub fn new(x: f32, y: f32, z: f32) -> Self {
|
||||
pub const fn new(x: f32, y: f32, z: f32) -> Self {
|
||||
Self { x, y, z }
|
||||
}
|
||||
|
||||
pub fn x(&self) -> f32 {
|
||||
pub const fn x(&self) -> f32 {
|
||||
self.x
|
||||
}
|
||||
|
||||
pub fn y(&self) -> f32 {
|
||||
pub const fn y(&self) -> f32 {
|
||||
self.y
|
||||
}
|
||||
|
||||
pub fn z(&self) -> f32 {
|
||||
pub const fn z(&self) -> f32 {
|
||||
self.z
|
||||
}
|
||||
|
||||
|
||||
+10
-10
@@ -33,43 +33,43 @@ pub struct Vec4 {
|
||||
}
|
||||
|
||||
impl Vec4 {
|
||||
pub fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
|
||||
pub const fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
|
||||
Self { x, y, z, w }
|
||||
}
|
||||
|
||||
pub fn x(&self) -> f32 {
|
||||
pub const fn x(&self) -> f32 {
|
||||
self.x
|
||||
}
|
||||
|
||||
pub fn y(&self) -> f32 {
|
||||
pub const fn y(&self) -> f32 {
|
||||
self.y
|
||||
}
|
||||
|
||||
pub fn z(&self) -> f32 {
|
||||
pub const fn z(&self) -> f32 {
|
||||
self.z
|
||||
}
|
||||
|
||||
pub fn w(&self) -> f32 {
|
||||
pub const fn w(&self) -> f32 {
|
||||
self.w
|
||||
}
|
||||
|
||||
pub fn to_vec3(&self) -> Vec3 {
|
||||
pub const fn to_vec3(&self) -> Vec3 {
|
||||
Vec3::new(self.x, self.y, self.z)
|
||||
}
|
||||
|
||||
pub fn splat_x(&self) -> Vec4 {
|
||||
pub const fn splat_x(&self) -> Vec4 {
|
||||
Vec4::new(self.x, self.x, self.x, self.x)
|
||||
}
|
||||
|
||||
pub fn splat_y(&self) -> Vec4 {
|
||||
pub const fn splat_y(&self) -> Vec4 {
|
||||
Vec4::new(self.y, self.y, self.y, self.y)
|
||||
}
|
||||
|
||||
pub fn splat_z(&self) -> Vec4 {
|
||||
pub const fn splat_z(&self) -> Vec4 {
|
||||
Vec4::new(self.z, self.z, self.z, self.z)
|
||||
}
|
||||
|
||||
pub fn splat_w(&self) -> Vec4 {
|
||||
pub const fn splat_w(&self) -> Vec4 {
|
||||
Vec4::new(self.w, self.w, self.w, self.w)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user