mirror of
https://github.com/encounter/squish-rs.git
synced 2026-07-11 06:18:39 -07:00
164 lines
5.4 KiB
Rust
164 lines
5.4 KiB
Rust
// Copyright (c) 2006 Simon Brown <si@sjbrown.co.uk>
|
|
// Copyright (c) 2018-2021 Jan Solanti <jhs@psonet.com>
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining
|
|
// a copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
|
// permit persons to whom the Software is furnished to do so, subject to
|
|
// the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included
|
|
// in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
use crate::colourblock;
|
|
use crate::colourset::ColourSet;
|
|
use crate::math::{f32_to_i32_clamped, Vec3};
|
|
use crate::Format;
|
|
|
|
use super::single_lut::*;
|
|
use super::ColourFitImpl;
|
|
|
|
pub struct SingleColourFit<'a> {
|
|
colourset: &'a ColourSet,
|
|
format: Format,
|
|
start: Vec3,
|
|
end: Vec3,
|
|
index: u8,
|
|
error: u32,
|
|
best_error: u32,
|
|
best_compressed: [u8; 8],
|
|
}
|
|
|
|
impl<'a> SingleColourFit<'a> {
|
|
pub fn new(colourset: &'a ColourSet, format: Format) -> Self {
|
|
SingleColourFit {
|
|
colourset,
|
|
format,
|
|
start: Vec3::new(0.0, 0.0, 0.0),
|
|
end: Vec3::new(0.0, 0.0, 0.0),
|
|
index: 0,
|
|
error: u32::MAX,
|
|
best_error: u32::MAX,
|
|
best_compressed: [0u8; 8],
|
|
}
|
|
}
|
|
|
|
fn compute_endpoints(&mut self, lut: [&[SingleColourLookup; 256]; 3]) {
|
|
// get colour for this block
|
|
let colour = [
|
|
f32_to_i32_clamped(self.colourset.points()[0].x() * 255.0, 255),
|
|
f32_to_i32_clamped(self.colourset.points()[0].y() * 255.0, 255),
|
|
f32_to_i32_clamped(self.colourset.points()[0].z() * 255.0, 255),
|
|
];
|
|
|
|
// check each index combination (endpoint and intermediate)
|
|
self.error = u32::MAX;
|
|
for index in 0..2 {
|
|
// check the error for this codebook index
|
|
let mut error = 0u32;
|
|
let mut sources = [
|
|
&lut[0][0].sources[0],
|
|
&lut[1][0].sources[0],
|
|
&lut[2][0].sources[0],
|
|
];
|
|
|
|
for channel in 0..3 {
|
|
// grab the lookup table and index for this channel
|
|
let lookup = &lut[channel];
|
|
let target = colour[channel];
|
|
|
|
// store a reference to the source for this channel
|
|
sources[channel] = &lookup[target as usize].sources[index];
|
|
|
|
// accumulate the error
|
|
let diff = u32::from(sources[channel].error);
|
|
error += diff * diff;
|
|
}
|
|
|
|
// keep these if the error is lower
|
|
if error < self.error {
|
|
self.start = Vec3::new(
|
|
f32::from(sources[0].start) / 31.0,
|
|
f32::from(sources[1].start) / 63.0,
|
|
f32::from(sources[2].start) / 31.0,
|
|
);
|
|
self.end = Vec3::new(
|
|
f32::from(sources[0].end) / 31.0,
|
|
f32::from(sources[1].end) / 63.0,
|
|
f32::from(sources[2].end) / 31.0,
|
|
);
|
|
self.index = 2 * index as u8;
|
|
self.error = error;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> ColourFitImpl<'a> for SingleColourFit<'a> {
|
|
fn is_bc1(&self) -> bool {
|
|
self.format == Format::Bc1 || self.format == Format::Bc1Gcn
|
|
}
|
|
|
|
fn is_transparent(&self) -> bool {
|
|
self.colourset.is_transparent()
|
|
}
|
|
|
|
fn best_compressed(&'a self) -> &'a [u8] {
|
|
&self.best_compressed
|
|
}
|
|
|
|
fn compress3(&mut self) {
|
|
// build lookup table
|
|
let lut = [&LOOKUP_5_3, &LOOKUP_6_3, &LOOKUP_5_3];
|
|
|
|
// find best endpoints and index
|
|
self.compute_endpoints(lut);
|
|
|
|
// build the block if we win
|
|
if self.error < self.best_error {
|
|
// remap the indices
|
|
let mut indices = [0u8; 16];
|
|
self.colourset
|
|
.remap_indices(&[self.index; 16], &mut indices);
|
|
|
|
// build the compressed blob
|
|
colourblock::write3(&self.start, &self.end, &indices, &mut self.best_compressed, self.format);
|
|
|
|
// save the error
|
|
self.best_error = self.error;
|
|
}
|
|
}
|
|
|
|
fn compress4(&mut self) {
|
|
// build lookup table
|
|
let lut = [&LOOKUP_5_4, &LOOKUP_6_4, &LOOKUP_5_4];
|
|
|
|
// find best endpoints and index
|
|
self.compute_endpoints(lut);
|
|
|
|
// build the block if we win
|
|
if self.error < self.best_error {
|
|
// remap the indices
|
|
let mut indices = [0u8; 16];
|
|
self.colourset
|
|
.remap_indices(&[self.index; 16], &mut indices);
|
|
|
|
// build the compressed blob
|
|
colourblock::write4(&self.start, &self.end, &indices, &mut self.best_compressed, self.format);
|
|
|
|
// save the error
|
|
self.best_error = self.error;
|
|
}
|
|
}
|
|
}
|