use std::io::{Read, Write};
use image::{ImageError, ImageResult, DecodingResult, ImageDecoder};
use color::ColorType;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DXTVariant {
DXT1,
DXT3,
DXT5
}
impl DXTVariant {
fn decoded_bytes_per_block(&self) -> usize {
match *self {
DXTVariant::DXT1 => 48,
DXTVariant::DXT3
| DXTVariant::DXT5 => 64
}
}
fn encoded_bytes_per_block(&self) -> usize {
match *self {
DXTVariant::DXT1 => 8,
DXTVariant::DXT3
| DXTVariant::DXT5 => 16
}
}
pub fn colortype(&self) -> ColorType {
match *self {
DXTVariant::DXT1 => ColorType::RGB(8),
DXTVariant::DXT3
| DXTVariant::DXT5 => ColorType::RGBA(8)
}
}
}
pub struct DXTDecoder<R: Read> {
inner: R,
width_blocks: u32,
height_blocks: u32,
variant: DXTVariant,
row: u32,
}
impl <R: Read> DXTDecoder<R> {
pub fn new(r: R, width: u32, height: u32, variant: DXTVariant) -> Result<DXTDecoder<R>, ImageError> {
if width % 4 != 0 || height % 4 != 0 {
return Err(ImageError::DimensionError);
}
let width_blocks = width / 4;
let height_blocks = height / 4;
if width.count_ones() != 1 || height.count_ones() != 1 {
return Err(ImageError::DimensionError);
}
Ok(DXTDecoder {
inner: r,
width_blocks: width_blocks,
height_blocks: height_blocks,
variant: variant,
row: 0
})
}
}
impl <R: Read> ImageDecoder for DXTDecoder<R> {
fn dimensions(&mut self) -> ImageResult<(u32, u32)> {
Ok((self.width_blocks * 4, self.height_blocks * 4))
}
fn colortype(&mut self) -> ImageResult<ColorType> {
Ok(self.variant.colortype())
}
fn row_len(&mut self) -> ImageResult<usize> {
Ok(self.variant.decoded_bytes_per_block() * self.width_blocks as usize)
}
fn read_scanline(&mut self, buf: &mut [u8]) -> ImageResult<u32> {
let mut src = vec![0u8; self.variant.encoded_bytes_per_block() * self.width_blocks as usize];
self.inner.read_exact(&mut src)?;
match self.variant {
DXTVariant::DXT1 => decode_dxt1_row(&src, buf),
DXTVariant::DXT3 => decode_dxt3_row(&src, buf),
DXTVariant::DXT5 => decode_dxt5_row(&src, buf),
}
let rv = self.row;
self.row += 1;
Ok(rv)
}
fn read_image(&mut self) -> ImageResult<DecodingResult> {
let mut dest = vec![0u8; self.height_blocks as usize * self.row_len()?];
for chunk in dest.chunks_mut(self.row_len()?) {
self.read_scanline(chunk)?;
}
Ok(DecodingResult::U8(dest))
}
}
pub struct DXTEncoder<W: Write> {
w: W
}
impl <W: Write> DXTEncoder<W> {
pub fn new(w: W) -> DXTEncoder<W> {
DXTEncoder {
w: w
}
}
pub fn encode(mut self, data: &[u8], width: u32, height: u32, variant: DXTVariant) -> ImageResult<()> {
if width % 4 != 0 || height % 4 != 0 {
return Err(ImageError::DimensionError);
}
let width_blocks = width / 4;
let height_blocks = height / 4;
if width.count_ones() != 1 || height.count_ones() != 1 {
return Err(ImageError::DimensionError);
}
let stride = variant.decoded_bytes_per_block();
assert!(data.len() >= width_blocks as usize * height_blocks as usize * stride);
for chunk in data.chunks(width_blocks as usize * stride) {
let data = match variant {
DXTVariant::DXT1 => encode_dxt1_row(chunk),
DXTVariant::DXT3 => encode_dxt3_row(chunk),
DXTVariant::DXT5 => encode_dxt5_row(chunk),
};
self.w.write_all(&data)?;
}
Ok(())
}
}
use std::mem::swap;
type Rgb = [u8; 3];
fn enc565_decode(value: u16) -> Rgb {
let red = (value >> 11) & 0x1F;
let green = (value >> 5 ) & 0x3F;
let blue = (value ) & 0x1F;
[(red * 0xFF / 0x1F) as u8, (green * 0xFF / 0x3F) as u8, (blue * 0xFF / 0x1F) as u8]
}
fn enc565_encode(rgb: Rgb) -> u16 {
let red = (rgb[0] as u16 * 0x1F + 0x7E) / 0xFF;
let green = (rgb[1] as u16 * 0x3F + 0x7E) / 0xFF;
let blue = (rgb[2] as u16 * 0x1F + 0x7E) / 0xFF;
(red << 11) | (green << 5) | blue
}
fn square(a: i32) -> i32 {
a * a
}
fn diff(a: &Rgb, b: &Rgb) -> i32 {
square(a[0] as i32 - b[0] as i32) +
square(a[1] as i32 - b[1] as i32) +
square(a[2] as i32 - b[2] as i32)
}
fn alpha_table_dxt5(alpha0: u8, alpha1: u8) -> [u8; 8] {
let mut table = [alpha0, alpha1, 0, 0, 0, 0, 0, 0xFF];
if alpha0 > alpha1 {
for i in 2 .. 8u16 {
table[i as usize] = (((8 - i) * alpha0 as u16 + (i - 1) * alpha1 as u16) / 7) as u8;
}
} else {
for i in 2 .. 6u16 {
table[i as usize] = (((6 - i) * alpha0 as u16 + (i - 1) * alpha1 as u16) / 5) as u8;
}
}
table
}
fn decode_dxt_colors(source: &[u8], dest: &mut [u8]) {
assert!(source.len() == 8 && (dest.len() == 48 || dest.len() == 64));
let pitch = dest.len() / 16;
let color0 = source[0] as u16 | ((source[1] as u16) << 8);
let color1 = source[2] as u16 | ((source[3] as u16) << 8);
let color_table = source[4] as u32 |
((source[5] as u32) << 8) |
((source[6] as u32) << 16) |
((source[7] as u32) << 24) ;
let mut colors = [[0; 3]; 4];
colors[0] = enc565_decode(color0);
colors[1] = enc565_decode(color1);
if color0 > color1 {
for i in 0 .. 3 {
colors[2][i] = ((colors[0][i] as u16 * 2 + colors[1][i] as u16 + 1) / 3) as u8;
colors[3][i] = ((colors[0][i] as u16 + colors[1][i] as u16 * 2 + 1) / 3) as u8;
}
} else {
for i in 0 .. 3 {
colors[2][i] = ((colors[0][i] as u16 + colors[1][i] as u16 + 1) / 2) as u8;
}
}
for i in 0 .. 16 {
dest[i * pitch .. i * pitch + 3].copy_from_slice(&colors[ (color_table >> (i * 2)) as usize & 3 ]);
}
}
fn decode_dxt5_block(source: &[u8], dest: &mut [u8]) {
assert!(source.len() == 16 && dest.len() == 64);
let alpha_table = source[2..8].iter().rev().fold(0, |t, &b| (t << 8) | b as u64);
let alphas = alpha_table_dxt5(source[0], source[1]);
for i in 0 .. 16 {
dest[i * 4 + 3] = alphas[(alpha_table >> (i * 3)) as usize & 7];
}
decode_dxt_colors(&source[8 .. 16], dest);
}
fn decode_dxt3_block(source: &[u8], dest: &mut [u8]) {
assert!(source.len() == 16 && dest.len() == 64);
let alpha_table = source[0..8].iter().rev().fold(0, |t, &b| (t << 8) | b as u64);
for i in 0 .. 16 {
dest[i * 4 + 3] = ((alpha_table >> (i * 4)) as u8 & 0xF) * 0x11;
}
decode_dxt_colors(&source[8 .. 16], dest);
}
fn decode_dxt1_block(source: &[u8], dest: &mut [u8]) {
assert!(source.len() == 8 && dest.len() == 48);
decode_dxt_colors(&source, dest);
}
fn decode_dxt1_row(source: &[u8], dest: &mut [u8]) {
assert!(source.len() % 8 == 0);
let block_count = source.len() / 8;
assert!(dest.len() >= block_count * 48);
let mut decoded_block = [0u8; 48];
for (x, encoded_block) in source.chunks(8).enumerate() {
decode_dxt1_block(encoded_block, &mut decoded_block);
for line in 0 .. 4 {
let offset = (block_count * line + x) * 12;
dest[offset .. offset + 12].copy_from_slice(&decoded_block[line * 12 .. (line + 1) * 12]);
}
}
}
fn decode_dxt3_row(source: &[u8], dest: &mut [u8]) {
assert!(source.len() % 16 == 0);
let block_count = source.len() / 16;
assert!(dest.len() >= block_count * 64);
let mut decoded_block = [0u8; 64];
for (x, encoded_block) in source.chunks(16).enumerate() {
decode_dxt3_block(encoded_block, &mut decoded_block);
for line in 0 .. 4 {
let offset = (block_count * line + x) * 16;
dest[offset .. offset + 16].copy_from_slice(&decoded_block[line * 16 .. (line + 1) * 16]);
}
}
}
fn decode_dxt5_row(source: &[u8], dest: &mut [u8]) {
assert!(source.len() % 16 == 0);
let block_count = source.len() / 16;
assert!(dest.len() >= block_count * 64);
let mut decoded_block = [0u8; 64];
for (x, encoded_block) in source.chunks(16).enumerate() {
decode_dxt5_block(encoded_block, &mut decoded_block);
for line in 0 .. 4 {
let offset = (block_count * line + x) * 16;
dest[offset .. offset + 16].copy_from_slice(&decoded_block[line * 16 .. (line + 1) * 16]);
}
}
}
fn encode_dxt_colors(source: &[u8], dest: &mut [u8]) {
assert!((source.len() == 64 || source.len() == 48) && dest.len() == 8);
let stride = source.len() / 16;
let mut colors = [[0u8; 3]; 4];
let mut targets = [[0u8; 3]; 16];
for (s, d) in source.chunks(stride).rev().zip(&mut targets) {
*d = [s[0], s[1], s[2]];
}
let mut colorspace = targets.to_vec();
for rgb in &mut colorspace {
*rgb = enc565_decode(enc565_encode(*rgb));
}
colorspace.dedup();
if colorspace.len() == 1 {
let ref_rgb = colorspace[0];
let mut rgb = targets.iter().cloned().max_by_key(|rgb| diff(rgb, &ref_rgb)).unwrap();
for i in 0 .. 3 {
rgb[i] = ((rgb[i] as i16 - ref_rgb[i] as i16) * 5 / 2 + ref_rgb[i] as i16) as u8;
}
let encoded = enc565_encode(rgb);
let rgb = enc565_decode(encoded);
if rgb == ref_rgb {
dest[0] = encoded as u8;
dest[1] = (encoded >> 8) as u8;
for i in 2 .. 8 {
dest[i] = 0;
}
return;
}
colorspace.push(rgb);
}
let mut chosen_colors = [[0; 3]; 4];
let mut chosen_use_0 = false;
let mut chosen_error = 0xFFFFFFFFu32;
'search: for (i, &c1) in colorspace.iter().enumerate() {
colors[0] = c1;
for &c2 in &colorspace[0 .. i] {
colors[1] = c2;
for use_0 in 0 .. 2 {
if use_0 != 0 {
for i in 0 .. 3 {
colors[2][i] = ((colors[0][i] as u16 + colors[1][i] as u16 + 1) / 2) as u8;
}
colors[3] = [0, 0, 0];
} else {
for i in 0 .. 3 {
colors[2][i] = ((colors[0][i] as u16 * 2 + colors[1][i] as u16 + 1) / 3) as u8;
colors[3][i] = ((colors[0][i] as u16 + colors[1][i] as u16 * 2 + 1) / 3) as u8;
}
}
let total_error = targets.iter().map(|t| colors.iter().map(|c| diff(c, t) as u32).min().unwrap()).sum();
if total_error < chosen_error {
chosen_colors = colors;
chosen_use_0 = use_0 != 0;
chosen_error = total_error;
if total_error < 4 {
break 'search;
}
}
}
}
}
let mut chosen_indices = 0u32;
for t in &targets {
let (idx, _) = chosen_colors.iter()
.enumerate()
.min_by_key(|&(_, c)| diff(c, t)).unwrap();
chosen_indices = (chosen_indices << 2) | idx as u32;
}
let mut color0 = enc565_encode(chosen_colors[0]);
let mut color1 = enc565_encode(chosen_colors[1]);
if color0 > color1 {
if chosen_use_0 {
swap(&mut color0, &mut color1);
let filter = (chosen_indices & 0xAAAAAAAA) >> 1;
chosen_indices ^= filter ^ 0x5555555;
}
} else {
if !chosen_use_0 {
swap(&mut color0, &mut color1);
chosen_indices ^= 0x55555555;
}
}
dest[0] = color0 as u8;
dest[1] = (color0 >> 8) as u8;
dest[2] = color1 as u8;
dest[3] = (color1 >> 8) as u8;
for i in 0 .. 4 {
dest[i + 4] = (chosen_indices >> (i * 8)) as u8;
}
}
fn encode_dxt5_alpha(alpha0: u8, alpha1: u8, alphas: &[u8; 16]) -> (i32, u64) {
let table = alpha_table_dxt5(alpha0, alpha1);
let mut indices = 0u64;
let mut total_error = 0i32;
for (i, &a) in alphas.iter().enumerate() {
let (index, error) = table.iter()
.enumerate()
.map(|(i, &e)| (i, square(e as i32 - a as i32)))
.min_by_key(|&(_, e)| e)
.unwrap();
total_error += error;
indices |= (index as u64) << (i * 3);
}
(total_error, indices)
}
fn encode_dxt5_block(source: &[u8], dest: &mut [u8]) {
assert!(source.len() == 64 && dest.len() == 16);
encode_dxt_colors(source, &mut dest[8 .. 16]);
let mut alphas = [0; 16];
for i in 0 .. 16 {
alphas[i] = source[i * 4 + 3];
}
let alpha07 = alphas.iter().cloned().min().unwrap();
let alpha17 = alphas.iter().cloned().max().unwrap();
let (error7, indices7) = encode_dxt5_alpha(alpha07, alpha17, &alphas);
let alpha05 = alphas.iter().cloned().filter(|&i| i != 255).max().unwrap_or(255);
let alpha15 = alphas.iter().cloned().filter(|&i| i != 0).min().unwrap_or(0);
let (error5, indices5) = encode_dxt5_alpha(alpha05, alpha15, &alphas);
let mut alpha_table = if error5 < error7 {
dest[0] = alpha05;
dest[1] = alpha15;
indices5
} else {
dest[0] = alpha07;
dest[1] = alpha17;
indices7
};
for byte in dest[2 .. 8].iter_mut() {
*byte = alpha_table as u8;
alpha_table >>= 8;
}
}
fn encode_dxt3_block(source: &[u8], dest: &mut [u8]) {
assert!(source.len() == 64 && dest.len() == 16);
encode_dxt_colors(source, &mut dest[8 .. 16]);
let mut alpha_table = 0u64;
for i in 0 .. 16 {
let alpha = source[i * 4 + 3] as u64;
let alpha = (alpha + 0x8) / 0x11;
alpha_table |= alpha << (i * 4);
}
for byte in &mut dest[0 .. 8] {
*byte = alpha_table as u8;
alpha_table >>= 8;
}
}
fn encode_dxt1_block(source: &[u8], dest: &mut [u8]) {
assert!(source.len() == 48 && dest.len() == 8);
encode_dxt_colors(source, dest);
}
fn encode_dxt1_row(source: &[u8]) -> Vec<u8> {
assert!(source.len() % 48 == 0);
let block_count = source.len() / 48;
let mut dest = vec![0u8; block_count * 8];
let mut decoded_block = [0u8; 48];
for (x, encoded_block) in dest.chunks_mut(8).enumerate() {
for line in 0 .. 4 {
let offset = (block_count * line + x) * 12;
decoded_block[line * 12 .. (line + 1) * 12].copy_from_slice(&source[offset .. offset + 12]);
}
encode_dxt1_block(&decoded_block, encoded_block);
}
dest
}
fn encode_dxt3_row(source: &[u8]) -> Vec<u8> {
assert!(source.len() % 64 == 0);
let block_count = source.len() / 64;
let mut dest = vec![0u8; block_count * 16];
let mut decoded_block = [0u8; 64];
for (x, encoded_block) in dest.chunks_mut(16).enumerate() {
for line in 0 .. 4 {
let offset = (block_count * line + x) * 16;
decoded_block[line * 16 .. (line + 1) * 16].copy_from_slice(&source[offset .. offset + 16]);
}
encode_dxt3_block(&decoded_block, encoded_block);
}
dest
}
fn encode_dxt5_row(source: &[u8]) -> Vec<u8> {
assert!(source.len() % 64 == 0);
let block_count = source.len() / 64;
let mut dest = vec![0u8; block_count * 16];
let mut decoded_block = [0u8; 64];
for (x, encoded_block) in dest.chunks_mut(16).enumerate() {
for line in 0 .. 4 {
let offset = (block_count * line + x) * 16;
decoded_block[line * 16 .. (line + 1) * 16].copy_from_slice(&source[offset .. offset + 16]);
}
encode_dxt5_block(&decoded_block, encoded_block);
}
dest
}