Add DXT encoder from J.M.P. Van Waveren

This commit is contained in:
Pavel Krajcevski 2013-10-16 16:38:53 -04:00
parent 4f2db726b7
commit 674c18b9d9
10 changed files with 1118 additions and 4 deletions

View File

@ -56,7 +56,7 @@
void PrintUsage() {
fprintf(stderr, "Usage: tc [OPTIONS] imagefile\n");
fprintf(stderr, "\n");
fprintf(stderr, "\t-f\t\tFormat to use. Either \"BPTC\" or \"PVRTC\". Default: BPTC\n");
fprintf(stderr, "\t-f\t\tFormat to use. Either \"BPTC\", \"DXT1\", \"DXT5\", or \"PVRTC\". Default: BPTC\n");
fprintf(stderr, "\t-l\t\tSave an output log.\n");
fprintf(stderr, "\t-q <quality>\tSet compression quality level. Default: 50\n");
fprintf(stderr, "\t-n <num>\tCompress the image num times and give the average time and PSNR. Default: 1\n");
@ -133,6 +133,10 @@ int main(int argc, char **argv) {
} else if(!strcmp(argv[fileArg], "PVRTCLib")) {
format = eCompressionFormat_PVRTC;
bUsePVRTexLib = true;
} else if(!strcmp(argv[fileArg], "DXT1")) {
format = eCompressionFormat_DXT1;
} else if(!strcmp(argv[fileArg], "DXT5")) {
format = eCompressionFormat_DXT5;
}
}
@ -218,7 +222,7 @@ int main(int argc, char **argv) {
}
FasTC::Image<> img(*file.GetImage());
if(format == eCompressionFormat_PVRTC) {
if(format != eCompressionFormat_BPTC) {
img.SetBlockStreamOrder(false);
}
@ -271,6 +275,8 @@ int main(int argc, char **argv) {
strcat(basename, "-bc7.png");
} else if(format == eCompressionFormat_PVRTC) {
strcat(basename, "-pvrtc.png");
} else if(format == eCompressionFormat_DXT1) {
strcat(basename, "-dxt1.png");
}
ImageFile cImgFile (basename, eFileFormat_PNG, *ci);

View File

@ -93,7 +93,7 @@ SET(CMAKE_MODULE_PATH "${FasTC_SOURCE_DIR}/CMakeModules" ${CMAKE_MODULE_PATH})
FIND_PACKAGE(PVRTexLib)
SET(FASTC_DIRECTORIES
Base Core IO BPTCEncoder PVRTCEncoder
Base Core IO BPTCEncoder PVRTCEncoder DXTEncoder
)
FOREACH(DIR ${FASTC_DIRECTORIES})

View File

@ -77,6 +77,8 @@ ENDIF()
INCLUDE_DIRECTORIES( ${FasTC_SOURCE_DIR}/Base/include )
INCLUDE_DIRECTORIES( ${FasTC_SOURCE_DIR} )
INCLUDE_DIRECTORIES( ${FasTC_SOURCE_DIR}/DXTEncoder/include )
INCLUDE_DIRECTORIES( ${FasTC_SOURCE_DIR}/PVRTCEncoder/include )
INCLUDE_DIRECTORIES( ${FasTC_BINARY_DIR}/PVRTCEncoder/include)
@ -160,6 +162,7 @@ ADD_LIBRARY( FasTCCore
TARGET_LINK_LIBRARIES( FasTCCore FasTCBase )
TARGET_LINK_LIBRARIES( FasTCCore FasTCIO )
TARGET_LINK_LIBRARIES( FasTCCore DXTEncoder )
TARGET_LINK_LIBRARIES( FasTCCore BPTCEncoder )
TARGET_LINK_LIBRARIES( FasTCCore PVRTCEncoder )

View File

@ -53,6 +53,7 @@
#include "TexCompTypes.h"
#include "BC7Compressor.h"
#include "PVRTCCompressor.h"
#include "DXTCompressor.h"
CompressedImage::CompressedImage( const CompressedImage &other )
: Image(other)
@ -74,7 +75,7 @@ CompressedImage::CompressedImage(
)
: FasTC::Image<>(width, height,
reinterpret_cast<uint32 *>(NULL),
format != eCompressionFormat_PVRTC)
format == eCompressionFormat_BPTC)
, m_Format(format)
, m_CompressedData(0)
{
@ -111,6 +112,10 @@ bool CompressedImage::DecompressImage(unsigned char *outBuf, unsigned int outBuf
uint8 *byteData = reinterpret_cast<uint8 *>(m_CompressedData);
DecompressionJob dj (byteData, outBuf, GetWidth(), GetHeight());
switch(m_Format) {
case eCompressionFormat_DXT1:
DXTC::DecompressDXT1(dj);
break;
case eCompressionFormat_PVRTC:
{
#ifndef NDEBUG

View File

@ -50,6 +50,7 @@
#include <cassert>
#include <iostream>
#include "DXTCompressor.h"
#include "BC7Compressor.h"
#include "CompressionFuncs.h"
#include "Image.h"
@ -95,12 +96,15 @@ SCompressionSettings:: SCompressionSettings()
static CompressionFuncWithStats ChooseFuncFromSettingsWithStats(const SCompressionSettings &s) {
switch(s.format) {
case eCompressionFormat_BPTC:
{
return BC7C::CompressWithStats;
}
break;
case eCompressionFormat_DXT1:
case eCompressionFormat_DXT5:
case eCompressionFormat_PVRTC:
{
// !FIXME! actually implement one of these methods...
@ -130,6 +134,12 @@ static CompressionFunc ChooseFuncFromSettings(const SCompressionSettings &s) {
}
break;
case eCompressionFormat_DXT1:
return DXTC::CompressImageDXT1;
case eCompressionFormat_DXT5:
return DXTC::CompressImageDXT5;
case eCompressionFormat_PVRTC:
{
if(s.bUsePVRTexLib) {

62
DXTEncoder/CMakeLists.txt Executable file
View File

@ -0,0 +1,62 @@
# FasTC
# Copyright (c) 2012 University of North Carolina at Chapel Hill. All rights reserved.
#
# Permission to use, copy, modify, and distribute this software and its documentation for educational,
# research, and non-profit purposes, without fee, and without a written agreement is hereby granted,
# provided that the above copyright notice, this paragraph, and the following four paragraphs appear
# in all copies.
#
# Permission to incorporate this software into commercial products may be obtained by contacting the
# authors or the Office of Technology Development at the University of North Carolina at Chapel Hill <otd@unc.edu>.
#
# This software program and documentation are copyrighted by the University of North Carolina at Chapel Hill.
# The software program and documentation are supplied "as is," without any accompanying services from the
# University of North Carolina at Chapel Hill or the authors. The University of North Carolina at Chapel Hill
# and the authors do not warrant that the operation of the program will be uninterrupted or error-free. The
# end-user understands that the program was developed for research purposes and is advised not to rely
# exclusively on the program for any reason.
#
# IN NO EVENT SHALL THE UNIVERSITY OF NORTH CAROLINA AT CHAPEL HILL OR THE AUTHORS BE LIABLE TO ANY PARTY FOR
# DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE
# USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF NORTH CAROLINA AT CHAPEL HILL OR THE
# AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# THE UNIVERSITY OF NORTH CAROLINA AT CHAPEL HILL AND THE AUTHORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING,
# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE AND ANY
# STATUTORY WARRANTY OF NON-INFRINGEMENT. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY
# OF NORTH CAROLINA AT CHAPEL HILL AND THE AUTHORS HAVE NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
# ENHANCEMENTS, OR MODIFICATIONS.
#
# Please send all BUG REPORTS to <pavel@cs.unc.edu>.
#
# The authors may be contacted via:
#
# Pavel Krajcevski
# Dept of Computer Science
# 201 S Columbia St
# Frederick P. Brooks, Jr. Computer Science Bldg
# Chapel Hill, NC 27599-3175
# USA
#
# <http://gamma.cs.unc.edu/FasTC/>
INCLUDE_DIRECTORIES(${FasTC_SOURCE_DIR}/Base/include)
INCLUDE_DIRECTORIES(${FasTC_SOURCE_DIR}/DXTEncoder/include)
INCLUDE_DIRECTORIES(${FasTC_BINARY_DIR}/DXTEncoder/include)
SET( HEADERS
include/DXTCompressor.h
)
SET( SOURCES
src/DXTCompressor.cpp
src/DXTDecompressor.cpp
)
ADD_LIBRARY( DXTEncoder
${HEADERS}
${SOURCES}
)
TARGET_LINK_LIBRARIES( DXTEncoder FasTCBase )

View File

@ -0,0 +1,34 @@
/*
This code is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
*/
#include "TexCompTypes.h"
#include "CompressionJob.h"
namespace DXTC
{
// DXT compressor (scalar version).
void CompressImageDXT1(const CompressionJob &);
void CompressImageDXT5(const CompressionJob &);
void DecompressDXT1(const DecompressionJob &);
uint16 ColorTo565(const uint8* color);
void EmitByte(uint8*& dest, uint8 b);
void EmitWord(uint8*& dest, uint16 s);
void EmitDoubleWord(uint8*& dest, uint32 i);
#if 0
// DXT compressor (SSE2 version).
void CompressImageDXT1SSE2(const uint8* inBuf, uint8* outBuf, uint32 width, uint32 height);
void CompressImageDXT5SSE2(const uint8* inBuf, uint8* outBuf, uint32 width, uint32 height);
#endif
}

322
DXTEncoder/src/DXTCompressor.cpp Executable file
View File

@ -0,0 +1,322 @@
/*
This code is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
*/
// Refer to "Real-Time DXT Compression" by J.M.P. van Waveren for a more thorough discussion of the
// algorithms used in this code.
#include "DXTCompressor.h"
#include <cassert>
#include <cstdlib>
#include <cstring>
#define INSET_SHIFT 4 // Inset the bounding box with (range >> shift).
#define C565_5_MASK 0xF8 // 0xFF minus last three bits
#define C565_6_MASK 0xFC // 0xFF minus last two bits
namespace DXTC
{
// Function prototypes
void ExtractBlock(const uint8* inPtr, uint32 width, uint8* colorBlock);
void GetMinMaxColors(const uint8* colorBlock, uint8* minColor, uint8* maxColor);
void GetMinMaxColorsWithAlpha(const uint8* colorBlock, uint8* minColor, uint8* maxColor);
void EmitColorIndices(const uint8* colorBlock, uint8*& outBuf, const uint8* minColor, const uint8* maxColor);
void EmitAlphaIndices(const uint8* colorBlock, uint8*& outBuf, const uint8 minAlpha, const uint8 maxAlpha);
// Compress an image using DXT1 compression. Use the inBuf parameter to point to an image in
// 4-byte RGBA format. The width and height parameters specify the size of the image in pixels.
// The buffer pointed to by outBuf should be large enough to store the compressed image. This
// implementation has an 8:1 compression ratio.
void CompressImageDXT1(const CompressionJob &cj) {
uint8 block[64];
uint8 minColor[4];
uint8 maxColor[4];
uint8 *outBuf = cj.outBuf;
const uint8 *inBuf = cj.inBuf;
for(int j = 0; j < cj.height; j += 4, inBuf += cj.width * 4 * 4)
{
for(int i = 0; i < cj.width; i += 4)
{
ExtractBlock(inBuf + i * 4, cj.width, block);
GetMinMaxColors(block, minColor, maxColor);
EmitWord(outBuf, ColorTo565(maxColor));
EmitWord(outBuf, ColorTo565(minColor));
EmitColorIndices(block, outBuf, minColor, maxColor);
}
}
}
// Compress an image using DXT5 compression. Use the inBuf parameter to point to an image in
// 4-byte RGBA format. The width and height parameters specify the size of the image in pixels.
// The buffer pointed to by outBuf should be large enough to store the compressed image. This
// implementation has an 4:1 compression ratio.
void CompressImageDXT5(const CompressionJob &cj) {
uint8 block[64];
uint8 minColor[4];
uint8 maxColor[4];
uint8 *outBuf = cj.outBuf;
const uint8 *inBuf = cj.inBuf;
for(int j = 0; j < cj.height; j += 4, inBuf += cj.width * 4 * 4)
{
for(int i = 0; i < cj.width; i += 4)
{
ExtractBlock(inBuf + i * 4, cj.width, block);
GetMinMaxColorsWithAlpha(block, minColor, maxColor);
EmitByte(outBuf, maxColor[3]);
EmitByte(outBuf, minColor[3]);
EmitAlphaIndices(block, outBuf, minColor[3], maxColor[3]);
EmitWord(outBuf, ColorTo565(maxColor));
EmitWord(outBuf, ColorTo565(minColor));
EmitColorIndices(block, outBuf, minColor, maxColor);
}
}
}
// Convert a color in 24-bit RGB888 format to 16-bit RGB565 format.
uint16 ColorTo565(const uint8* color)
{
return ((color[0] >> 3) << 11) | ((color[1] >> 2) << 5) | (color[2] >> 3);
}
// Write a single byte to dest.
void EmitByte(uint8*& dest, uint8 b)
{
dest[0] = b;
dest += 1;
}
// Write a word to dest.
void EmitWord(uint8*& dest, uint16 s)
{
dest[0] = (s >> 0) & 255;
dest[1] = (s >> 8) & 255;
dest += 2;
}
// Write a double word to dest.
void EmitDoubleWord(uint8*& dest, uint32 i)
{
dest[0] = (i >> 0) & 255;
dest[1] = (i >> 8) & 255;
dest[2] = (i >> 16) & 255;
dest[3] = (i >> 24) & 255;
dest += 4;
}
// Extract a 4 by 4 block of pixels from inPtr and store it in colorBlock. The width parameter
// specifies the size of the image in pixels.
void ExtractBlock(const uint8* inPtr, uint32 width, uint8* colorBlock)
{
for(int j = 0; j < 4; j++)
{
memcpy(&colorBlock[j * 4 * 4], inPtr, 4 * 4);
inPtr += width * 4;
}
}
// Find a line of best fit through the color space of colorBlock. The line is approximated using
// the extents of the bounding box of the color space. This function does not include the alpha
// channel.
void GetMinMaxColors(const uint8* colorBlock, uint8* minColor, uint8* maxColor)
{
int32 i;
uint8 inset[3];
minColor[0] = minColor[1] = minColor[2] = 255;
maxColor[0] = maxColor[1] = maxColor[2] = 0;
// Find the bounding box (defined by minimum and maximum color).
for(i = 0; i < 16; i++) {
if(colorBlock[i * 4 + 0] < minColor[0]) {
minColor[0] = colorBlock[i * 4 + 0];
}
if(colorBlock[i * 4 + 1] < minColor[1]) {
minColor[1] = colorBlock[i * 4 + 1];
}
if(colorBlock[i * 4 + 2] < minColor[2]) {
minColor[2] = colorBlock[i * 4 + 2];
}
if(colorBlock[i * 4 + 0] > maxColor[0]) {
maxColor[0] = colorBlock[i * 4 + 0];
}
if(colorBlock[i * 4 + 1] > maxColor[1]) {
maxColor[1] = colorBlock[i * 4 + 1];
}
if(colorBlock[i * 4 + 2] > maxColor[2]) {
maxColor[2] = colorBlock[i * 4 + 2];
}
}
// Inset the bounding box by 1/16 of it's size. (i.e. shift right by 4).
inset[0] = (maxColor[0] - minColor[0]) >> INSET_SHIFT;
inset[1] = (maxColor[1] - minColor[1]) >> INSET_SHIFT;
inset[2] = (maxColor[2] - minColor[2]) >> INSET_SHIFT;
// Clamp the inset bounding box to 255.
minColor[0] = (minColor[0] + inset[0] <= 255) ? minColor[0] + inset[0] : 255;
minColor[1] = (minColor[1] + inset[1] <= 255) ? minColor[1] + inset[1] : 255;
minColor[2] = (minColor[2] + inset[2] <= 255) ? minColor[2] + inset[2] : 255;
// Clamp the inset bounding box to 0.
maxColor[0] = (maxColor[0] >= inset[0]) ? maxColor[0] - inset[0] : 0;
maxColor[1] = (maxColor[1] >= inset[1]) ? maxColor[1] - inset[1] : 0;
maxColor[2] = (maxColor[2] >= inset[2]) ? maxColor[2] - inset[2] : 0;
}
// Find a line of best fit through the color space of colorBlock. The line is approximated using
// the extents of the bounding box of the color space. This function includes the alpha channel.
void GetMinMaxColorsWithAlpha(const uint8* colorBlock, uint8* minColor, uint8* maxColor)
{
int32 i;
uint8 inset[4];
minColor[0] = minColor[1] = minColor[2] = minColor[3] = 255;
maxColor[0] = maxColor[1] = maxColor[2] = maxColor[3] = 0;
// Find the bounding box (defined by minimum and maximum color).
for(i = 0; i < 16; i++) {
if(colorBlock[i * 4 + 0] < minColor[0]) {
minColor[0] = colorBlock[i * 4 + 0];
}
if(colorBlock[i * 4 + 1] < minColor[1]) {
minColor[1] = colorBlock[i * 4 + 1];
}
if(colorBlock[i * 4 + 2] < minColor[2]) {
minColor[2] = colorBlock[i * 4 + 2];
}
if(colorBlock[i * 4 + 3] < minColor[3]) {
minColor[3] = colorBlock[i * 4 + 3];
}
if(colorBlock[i * 4 + 0] > maxColor[0]) {
maxColor[0] = colorBlock[i * 4 + 0];
}
if(colorBlock[i * 4 + 1] > maxColor[1]) {
maxColor[1] = colorBlock[i * 4 + 1];
}
if(colorBlock[i * 4 + 2] > maxColor[2]) {
maxColor[2] = colorBlock[i * 4 + 2];
}
if(colorBlock[i * 4 + 3] > maxColor[3]) {
maxColor[3] = colorBlock[i * 4 + 3];
}
}
// Inset the bounding box by 1/16 of it's size. (i.e. shift right by 4).
inset[0] = (maxColor[0] - minColor[0]) >> INSET_SHIFT;
inset[1] = (maxColor[1] - minColor[1]) >> INSET_SHIFT;
inset[2] = (maxColor[2] - minColor[2]) >> INSET_SHIFT;
inset[3] = (maxColor[3] - minColor[3]) >> INSET_SHIFT;
// Clamp the inset bounding box to 255.
minColor[0] = (minColor[0] + inset[0] <= 255) ? minColor[0] + inset[0] : 255;
minColor[1] = (minColor[1] + inset[1] <= 255) ? minColor[1] + inset[1] : 255;
minColor[2] = (minColor[2] + inset[2] <= 255) ? minColor[2] + inset[2] : 255;
minColor[3] = (minColor[3] + inset[3] <= 255) ? minColor[3] + inset[3] : 255;
// Clamp the inset bounding box to 0.
maxColor[0] = (maxColor[0] >= inset[0]) ? maxColor[0] - inset[0] : 0;
maxColor[1] = (maxColor[1] >= inset[1]) ? maxColor[1] - inset[1] : 0;
maxColor[2] = (maxColor[2] >= inset[2]) ? maxColor[2] - inset[2] : 0;
maxColor[3] = (maxColor[3] >= inset[3]) ? maxColor[3] - inset[3] : 0;
}
// Quantize the pixels of the colorBlock to 4 colors that lie on the line through the color space
// of colorBlock. The paramaters minColor and maxColor approximate the line through the color
// space. 32 bits (2 bits per pixel) are written to outBuf, which represent the indices of the 4
// colors. This function does not include the alpha channel.
void EmitColorIndices(const uint8* colorBlock, uint8*& outBuf, const uint8* minColor, const uint8* maxColor)
{
uint16 colors[4][4];
uint32 result = 0;
colors[0][0] = (maxColor[0] & C565_5_MASK) | (maxColor[0] >> 5);
colors[0][1] = (maxColor[1] & C565_6_MASK) | (maxColor[1] >> 6);
colors[0][2] = (maxColor[2] & C565_5_MASK) | (maxColor[2] >> 5);
colors[1][0] = (minColor[0] & C565_5_MASK) | (minColor[0] >> 5);
colors[1][1] = (minColor[1] & C565_6_MASK) | (minColor[1] >> 6);
colors[1][2] = (minColor[2] & C565_5_MASK) | (minColor[2] >> 5);
colors[2][0] = (2 * colors[0][0] + 1 * colors[1][0]) / 3;
colors[2][1] = (2 * colors[0][1] + 1 * colors[1][1]) / 3;
colors[2][2] = (2 * colors[0][2] + 1 * colors[1][2]) / 3;
colors[3][0] = (1 * colors[0][0] + 2 * colors[1][0]) / 3;
colors[3][1] = (1 * colors[0][1] + 2 * colors[1][1]) / 3;
colors[3][2] = (1 * colors[0][2] + 2 * colors[1][2]) / 3;
for(int i = 15; i >= 0; i--) {
int32 c0 = colorBlock[i * 4 + 0];
int32 c1 = colorBlock[i * 4 + 1];
int32 c2 = colorBlock[i * 4 + 2];
int32 d0 = abs(colors[0][0] - c0) + abs(colors[0][1] - c1) + abs(colors[0][2] - c2);
int32 d1 = abs(colors[1][0] - c0) + abs(colors[1][1] - c1) + abs(colors[1][2] - c2);
int32 d2 = abs(colors[2][0] - c0) + abs(colors[2][1] - c1) + abs(colors[2][2] - c2);
int32 d3 = abs(colors[3][0] - c0) + abs(colors[3][1] - c1) + abs(colors[3][2] - c2);
int32 b0 = d0 > d3;
int32 b1 = d1 > d2;
int32 b2 = d0 > d2;
int32 b3 = d1 > d3;
int32 b4 = d2 > d3;
int32 x0 = b1 & b2;
int32 x1 = b0 & b3;
int32 x2 = b0 & b4;
result |= (x2 | ((x0 | x1) << 1)) << (i << 1);
}
EmitDoubleWord(outBuf, result);
}
// Quantize the alpha channel of the pixels in colorBlock to 8 alpha values that are equally
// spaced along the interval defined by minAlpha and maxAlpha. 48 bits (3 bits per alpha) are
// written to outBuf, which represent the indices of the 8 alpha values.
void EmitAlphaIndices(const uint8* colorBlock, uint8*& outBuf, const uint8 minAlpha, const uint8 maxAlpha)
{
assert(maxAlpha >= minAlpha);
uint8 indices[16];
uint8 mid = (maxAlpha - minAlpha) / (2 * 7);
uint8 ab1 = minAlpha + mid;
uint8 ab2 = (6 * maxAlpha + 1 * minAlpha) / 7 + mid;
uint8 ab3 = (5 * maxAlpha + 2 * minAlpha) / 7 + mid;
uint8 ab4 = (4 * maxAlpha + 3 * minAlpha) / 7 + mid;
uint8 ab5 = (3 * maxAlpha + 4 * minAlpha) / 7 + mid;
uint8 ab6 = (2 * maxAlpha + 5 * minAlpha) / 7 + mid;
uint8 ab7 = (1 * maxAlpha + 6 * minAlpha) / 7 + mid;
colorBlock += 3;
for(int i = 0; i < 16; i++) {
uint8 a = colorBlock[i * 4];
int32 b1 = (a <= ab1);
int32 b2 = (a <= ab2);
int32 b3 = (a <= ab3);
int32 b4 = (a <= ab4);
int32 b5 = (a <= ab5);
int32 b6 = (a <= ab6);
int32 b7 = (a <= ab7);
int32 index = (b1 + b2 + b3 + b4 + b5 + b6 + b7 + 1) & 7;
indices[i] = index ^ (2 > index);
}
EmitByte(outBuf, (indices[0] >> 0) | (indices[1] << 3) | (indices[2] << 6));
EmitByte(outBuf, (indices[2] >> 2) | (indices[3] << 1) | (indices[4] << 4) | (indices[ 5] << 7));
EmitByte(outBuf, (indices[5] >> 1) | (indices[6] << 2) | (indices[7] << 5));
EmitByte(outBuf, (indices[8] >> 0) | (indices[9] << 3) | (indices[10] << 6));
EmitByte(outBuf, (indices[10] >> 2) | (indices[11] << 1) | (indices[12] << 4) | (indices[13] << 7));
EmitByte(outBuf, (indices[13] >> 1) | (indices[14] << 2) | (indices[15] << 5));
}
}

View File

@ -0,0 +1,552 @@
/*
This code is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
*/
// Refer to "Real-Time DXT Compression" by J.M.P. van Waveren for a more thorough discussion of the
// algorithms used in this code.
#include "DXTCompressorDLL.h"
#include <cassert>
#include <cstdlib>
#include <cstring>
#include <smmintrin.h>
#define ALIGN16(x) __declspec(align(16)) x
#define INSET_SHIFT 4 // Inset the bounding box with (range >> shift).
#define C565_5_MASK 0xF8 // 0xFF minus last three bits
#define C565_6_MASK 0xFC // 0xFF minus last two bits
#define R_SHUFFLE_D( x, y, z, w ) (( (w) & 3 ) << 6 | ( (z) & 3 ) << 4 | ( (y) & 3 ) << 2 | ( (x) & 3 ))
namespace DXTC
{
// SSE2 Constants
ALIGN16(static const BYTE SIMD_byte_0[16]) = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
ALIGN16(static const BYTE SIMD_byte_1[16]) = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 };
ALIGN16(static const BYTE SIMD_byte_2[16]) = { 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 };
ALIGN16(static const BYTE SIMD_byte_7[16]) = { 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07 };
ALIGN16(static const BYTE SIMD_byte_colorMask[16]) = { C565_5_MASK, C565_6_MASK, C565_5_MASK, 0x00, 0x00, 0x00, 0x00, 0x00, C565_5_MASK, C565_6_MASK, C565_5_MASK, 0x00, 0x00, 0x00, 0x00, 0x00 };
ALIGN16(static const WORD SIMD_word_0[8]) = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 };
ALIGN16(static const WORD SIMD_word_1[8]) = { 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001 };
ALIGN16(static const WORD SIMD_word_2[8]) = { 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002 };
ALIGN16(static const WORD SIMD_word_div_by_3[8]) = { (1 << 16) / 3 + 1, (1 << 16) / 3 + 1, (1 << 16) / 3 + 1, (1 << 16) / 3 + 1, (1 << 16) / 3 + 1, (1 << 16) / 3 + 1, (1 << 16) / 3 + 1, (1 << 16) / 3 + 1 };
ALIGN16(static const WORD SIMD_word_div_by_7[8]) = { (1 << 16) / 7 + 1, (1 << 16) / 7 + 1, (1 << 16) / 7 + 1, (1 << 16) / 7 + 1, (1 << 16) / 7 + 1, (1 << 16) / 7 + 1, (1 << 16) / 7 + 1, (1 << 16) / 7 + 1 };
ALIGN16(static const WORD SIMD_word_div_by_14[8]) = { (1 << 16) / 14 + 1, (1 << 16) / 14 + 1, (1 << 16) / 14 + 1, (1 << 16) / 14 + 1, (1 << 16) / 14 + 1, (1 << 16) / 14 + 1, (1 << 16) / 14 + 1, (1 << 16) / 14 + 1 };
ALIGN16(static const WORD SIMD_word_scale66554400[8]) = { 6, 6, 5, 5, 4, 4, 0, 0 };
ALIGN16(static const WORD SIMD_word_scale11223300[8]) = { 1, 1, 2, 2, 3, 3, 0, 0 };
ALIGN16(static const DWORD SIMD_dword_alpha_bit_mask0[4]) = { 7 << 0, 0, 7 << 0, 0 };
ALIGN16(static const DWORD SIMD_dword_alpha_bit_mask1[4]) = { 7 << 3, 0, 7 << 3, 0 };
ALIGN16(static const DWORD SIMD_dword_alpha_bit_mask2[4]) = { 7 << 6, 0, 7 << 6, 0 };
ALIGN16(static const DWORD SIMD_dword_alpha_bit_mask3[4]) = { 7 << 9, 0, 7 << 9, 0 };
ALIGN16(static const DWORD SIMD_dword_alpha_bit_mask4[4]) = { 7 << 12, 0, 7 << 12, 0 };
ALIGN16(static const DWORD SIMD_dword_alpha_bit_mask5[4]) = { 7 << 15, 0, 7 << 15, 0 };
ALIGN16(static const DWORD SIMD_dword_alpha_bit_mask6[4]) = { 7 << 18, 0, 7 << 18, 0 };
ALIGN16(static const DWORD SIMD_dword_alpha_bit_mask7[4]) = { 7 << 21, 0, 7 << 21, 0 };
static void ExtractBlock(const BYTE* inPtr, int width, BYTE* colorBlock);
static void GetMinMaxColors(const BYTE* colorBlock, BYTE* minColor, BYTE* maxColor);
static void EmitColorIndices(const BYTE* colorBlock, BYTE*& outBuf, const BYTE* minColor, const BYTE* maxColor);
static void EmitAlphaIndices(const BYTE* colorBlock, BYTE*& outBuf, const BYTE minAlpha, const BYTE maxAlpha);
// Compress an image using SSE2-optimized DXT1 compression. Use the inBuf parameter to point to an
// image in 4-byte RGBA format. The address pointed to by inBuf must be 16-byte aligned. The width
// and height parameters specify the size of the image in pixels. The buffer pointed to by outBuf
// must be 16-byte aligned and should be large enough to store the compressed image. This
// implementation has an 8:1 compression ratio.
void CompressImageDXT1SSE2(const BYTE* inBuf, BYTE* outBuf, int width, int height)
{
ALIGN16(BYTE block[64]);
ALIGN16(BYTE minColor[4]);
ALIGN16(BYTE maxColor[4]);
for(int j = 0; j < height; j += 4, inBuf += width * 4 * 4)
{
for(int i = 0; i < width; i += 4)
{
ExtractBlock(inBuf + i * 4, width, block);
GetMinMaxColors(block, minColor, maxColor);
EmitWord(outBuf, ColorTo565(maxColor));
EmitWord(outBuf, ColorTo565(minColor));
EmitColorIndices(block, outBuf, minColor, maxColor);
}
}
}
// Compress an image using SSE2-optimized DXT5 compression. Use the inBuf parameter to point to an
// image in 4-byte RGBA format. The address pointed to by inBuf must be 16-byte aligned. The width
// and height parameters specify the size of the image in pixels. The buffer pointed to by outBuf
// must be 16-byte aligned and should be large enough to store the compressed image. This
// implementation has an 4:1 compression ratio.
void CompressImageDXT5SSE2(const BYTE* inBuf, BYTE* outBuf, int width, int height)
{
ALIGN16(BYTE block[64]);
ALIGN16(BYTE minColor[4]);
ALIGN16(BYTE maxColor[4]);
for(int j = 0; j < height; j += 4, inBuf += width * 4 * 4)
{
for(int i = 0; i < width; i += 4)
{
ExtractBlock(inBuf + i * 4, width, block);
GetMinMaxColors(block, minColor, maxColor);
EmitByte(outBuf, maxColor[3]);
EmitByte(outBuf, minColor[3]);
EmitAlphaIndices(block, outBuf, minColor[3], maxColor[3]);
EmitWord(outBuf, ColorTo565(maxColor));
EmitWord(outBuf, ColorTo565(minColor));
EmitColorIndices(block, outBuf, minColor, maxColor);
}
}
}
// Compress the blocks assigned to this task using SSE2-optimized DXT1 compression.
VOID CompressImageDXT1SSE2Task(VOID* taskData, INT taskContext, UINT taskId, UINT taskCount)
{
const DXTTaskData* data = (const DXTTaskData*)taskData;
// Compress the block.
ALIGN16(BYTE block[64]);
ALIGN16(BYTE minColor[4]);
ALIGN16(BYTE maxColor[4]);
// Interate over the block set.
for (int blockOffset = 0; blockOffset < data->kBlocksPerTask; ++blockOffset)
{
// Check for out of bounds.
const INT blockIndex = (INT)taskId * data->kBlocksPerTask + blockOffset;
if(blockIndex >= data->numBlocks)
{
break;
}
// Compute the offsets into the input and output buffers.
const INT blockWidth = data->width / 4;
const INT blockRow = blockIndex / blockWidth;
const INT blockCol = blockIndex % blockWidth;
const INT inOffset = blockRow * blockWidth * 4 * 4 * 4 + blockCol * 4 * 4;
const INT outOffset = blockIndex * 8;
const BYTE* inBuf = data->inBuf + inOffset;
BYTE* outBuf = data->outBuf + outOffset;
ExtractBlock(inBuf, data->width, block);
GetMinMaxColors(block, minColor, maxColor);
EmitWord(outBuf, ColorTo565(maxColor));
EmitWord(outBuf, ColorTo565(minColor));
EmitColorIndices(block, outBuf, minColor, maxColor);
}
}
// Compress the blocks assigned to this task using SSE2-optimized DXT5 compression.
VOID CompressImageDXT5SSE2Task(VOID* taskData, INT taskContext, UINT taskId, UINT taskCount)
{
const DXTTaskData* data = (const DXTTaskData*)taskData;
// Compress the block.
ALIGN16(BYTE block[64]);
ALIGN16(BYTE minColor[4]);
ALIGN16(BYTE maxColor[4]);
// Interate over the block set.
for (int blockOffset = 0; blockOffset < data->kBlocksPerTask; ++blockOffset)
{
// Check for out of bounds.
const INT blockIndex = (INT)taskId * data->kBlocksPerTask + blockOffset;
if(blockIndex >= data->numBlocks)
{
break;
}
// Compute the offsets into the input and output buffers.
const INT blockWidth = data->width / 4;
const INT blockRow = blockIndex / blockWidth;
const INT blockCol = blockIndex % blockWidth;
const INT inOffset = blockRow * blockWidth * 4 * 4 * 4 + blockCol * 4 * 4;
const INT outOffset = blockIndex * 16;
const BYTE* inBuf = data->inBuf + inOffset;
BYTE* outBuf = data->outBuf + outOffset;
ExtractBlock(inBuf, data->width, block);
GetMinMaxColors(block, minColor, maxColor);
EmitByte(outBuf, maxColor[3]);
EmitByte(outBuf, minColor[3]);
EmitAlphaIndices(block, outBuf, minColor[3], maxColor[3]);
EmitWord(outBuf, ColorTo565(maxColor));
EmitWord(outBuf, ColorTo565(minColor));
EmitColorIndices(block, outBuf, minColor, maxColor);
}
}
// Extract a 4 by 4 block of pixels from inPtr and store it in colorBlock. The width parameter
// specifies the size of the image in pixels.
void ExtractBlock(const BYTE* inPtr, int width, BYTE* colorBlock)
{
// Compute the stride.
const int stride = width * 4;
// Copy the first row of pixels from inPtr into colorBlock.
_mm_store_si128((__m128i*)colorBlock, _mm_load_si128((__m128i*)inPtr));
inPtr += stride;
// Copy the second row of pixels from inPtr into colorBlock.
_mm_store_si128((__m128i*)(colorBlock + 16), _mm_load_si128((__m128i*)inPtr));
inPtr += stride;
// Copy the third row of pixels from inPtr into colorBlock.
_mm_store_si128((__m128i*)(colorBlock + 32), _mm_load_si128((__m128i*)inPtr));
inPtr += stride;
// Copy the forth row of pixels from inPtr into colorBlock.
_mm_store_si128((__m128i*)(colorBlock + 48), _mm_load_si128((__m128i*)inPtr));
}
// Find a line of best fit through the color space of colorBlock. The line is approximated using
// the extents of the bounding box of the color space. This function does not include the alpha
// channel.
void GetMinMaxColors(const BYTE* colorBlock, BYTE* minColor, BYTE* maxColor)
{
// Compute the min/max of each column of pixels.
__m128i min = _mm_load_si128((__m128i*)colorBlock);
__m128i max = _mm_load_si128((__m128i*)colorBlock);
min = _mm_min_epu8(min, *((__m128i*)(colorBlock + 16)));
max = _mm_max_epu8(max, *((__m128i*)(colorBlock + 16)));
min = _mm_min_epu8(min, *((__m128i*)(colorBlock + 32)));
max = _mm_max_epu8(max, *((__m128i*)(colorBlock + 32)));
min = _mm_min_epu8(min, *((__m128i*)(colorBlock + 48)));
max = _mm_max_epu8(max, *((__m128i*)(colorBlock + 48)));
// Compute the min/max of the 1st and 3rd DWORD and the 2nd and 4th DWORD.
__m128i minShuf = _mm_shuffle_epi32(min, R_SHUFFLE_D(2, 3, 2, 3));
__m128i maxShuf = _mm_shuffle_epi32(max, R_SHUFFLE_D(2, 3, 2, 3));
min = _mm_min_epu8(min, minShuf);
max = _mm_max_epu8(max, maxShuf);
// Compute the min/max of the 1st and 2nd DWORD.
minShuf = _mm_shufflelo_epi16(min, R_SHUFFLE_D(2, 3, 2, 3));
maxShuf = _mm_shufflelo_epi16(max, R_SHUFFLE_D(2, 3, 2, 3));
min = _mm_min_epu8(min, minShuf);
max = _mm_max_epu8(max, maxShuf);
// Compute the inset value.
const __m128i zero = _mm_setzero_si128();
min = _mm_unpacklo_epi8(min, zero);
max = _mm_unpacklo_epi8(max, zero);
__m128i inset = _mm_sub_epi16(max, min);
inset = _mm_srli_epi16(inset, INSET_SHIFT);
// Inset the bounding box.
min = _mm_add_epi16(min, inset);
max = _mm_sub_epi16(max, inset);
// Store the bounding box.
min = _mm_packus_epi16(min, min);
max = _mm_packus_epi16(max, max);
*((int*)minColor) = _mm_cvtsi128_si32(min);
*((int*)maxColor) = _mm_cvtsi128_si32(max);
}
// Quantize the pixels of the colorBlock to 4 colors that lie on the line through the color space
// of colorBlock. The paramaters minColor and maxColor approximate the line through the color
// space. 32 bits (2 bits per pixel) are written to outBuf, which represent the indices of the 4
// colors. This function does not include the alpha channel.
void EmitColorIndices(const BYTE* colorBlock, BYTE*& outBuf, const BYTE* minColor, const BYTE* maxColor)
{
const __m128i RGB565Mask = _mm_load_si128((__m128i*)SIMD_byte_colorMask);
const __m128i zero = _mm_setzero_si128();
// Find 4 colors on the line through maxColor and minColor.
// Compute color0 (maxColor).
__m128i color0 = _mm_cvtsi32_si128(*((int*)maxColor));
color0 = _mm_and_si128(color0, RGB565Mask);
color0 = _mm_unpacklo_epi8(color0, zero);
__m128i redBlue = _mm_shufflelo_epi16(color0, R_SHUFFLE_D(0, 3, 2, 3));
__m128i green = _mm_shufflelo_epi16(color0, R_SHUFFLE_D(3, 1, 3, 3));
redBlue = _mm_srli_epi16(redBlue, 5);
green = _mm_srli_epi16(green, 6);
color0 = _mm_or_si128(color0, redBlue);
color0 = _mm_or_si128(color0, green);
// Compute color1 (minColor).
__m128i color1 = _mm_cvtsi32_si128(*((int*)minColor));
color1 = _mm_and_si128(color1, RGB565Mask);
color1 = _mm_unpacklo_epi8(color1, zero);
redBlue = _mm_shufflelo_epi16(color1, R_SHUFFLE_D(0, 3, 2, 3));
green = _mm_shufflelo_epi16(color1, R_SHUFFLE_D(3, 1, 3, 3));
redBlue = _mm_srli_epi16(redBlue, 5);
green = _mm_srli_epi16(green, 6);
color1 = _mm_or_si128(color1, redBlue);
color1 = _mm_or_si128(color1, green);
// Compute and pack color3.
__m128i color3 = _mm_add_epi16(color1, color1);
color3 = _mm_add_epi16(color0, color3);
color3 = _mm_mulhi_epi16(color3, *((__m128i*)SIMD_word_div_by_3));
color3 = _mm_packus_epi16(color3, zero);
color3 = _mm_shuffle_epi32(color3, R_SHUFFLE_D(0, 1, 0, 1));
// Compute and pack color2.
__m128i color2 = _mm_add_epi16(color0, color0);
color2 = _mm_add_epi16(color2, color1);
color2 = _mm_mulhi_epi16(color2, *((__m128i*)SIMD_word_div_by_3));
color2 = _mm_packus_epi16(color2, zero);
color2 = _mm_shuffle_epi32(color2, R_SHUFFLE_D(0, 1, 0, 1));
// Pack color1.
color1 = _mm_packus_epi16(color1, zero);
color1 = _mm_shuffle_epi32(color1, R_SHUFFLE_D(0, 1, 0, 1));
// Pack color0.
color0 = _mm_packus_epi16(color0, zero);
color0 = _mm_shuffle_epi32(color0, R_SHUFFLE_D(0, 1, 0, 1));
// Assign a color index for each of the 16 colors in the colorblock.
// This loop iterates twice (computes 8 indexes per iteration).
__m128i result = zero;
for(int i = 32; i >= 0; i -= 32)
{
// Load 4 colors.
__m128i colorHi = _mm_loadl_epi64((__m128i*)(colorBlock + i));
colorHi = _mm_shuffle_epi32(colorHi, R_SHUFFLE_D(0, 2, 1, 3));
__m128i colorLo = _mm_loadl_epi64((__m128i*)(colorBlock + i + 8));
colorLo = _mm_shuffle_epi32(colorLo, R_SHUFFLE_D(0, 2, 1, 3));
// Compute the sum of absolute differences for each color.
__m128i dHi = _mm_sad_epu8(colorHi, color0);
__m128i dLo = _mm_sad_epu8(colorLo, color0);
__m128i d0 = _mm_packs_epi32(dHi, dLo);
dHi = _mm_sad_epu8(colorHi, color1);
dLo = _mm_sad_epu8(colorLo, color1);
__m128i d1 = _mm_packs_epi32(dHi, dLo);
dHi = _mm_sad_epu8(colorHi, color2);
dLo = _mm_sad_epu8(colorLo, color2);
__m128i d2 = _mm_packs_epi32(dHi, dLo);
dHi = _mm_sad_epu8(colorHi, color3);
dLo = _mm_sad_epu8(colorLo, color3);
__m128i d3 = _mm_packs_epi32(dHi, dLo);
// Load 4 more colors.
colorHi = _mm_loadl_epi64((__m128i*)(colorBlock + i + 16));
colorHi = _mm_shuffle_epi32(colorHi, R_SHUFFLE_D(0, 2, 1, 3));
colorLo = _mm_loadl_epi64((__m128i*)(colorBlock + i + 24));
colorLo = _mm_shuffle_epi32(colorLo, R_SHUFFLE_D(0, 2, 1, 3));
// Compute the sum of absolute differences for each color. Pack result into previous 4 results.
dHi = _mm_sad_epu8(colorHi, color0);
dLo = _mm_sad_epu8(colorLo, color0);
dLo = _mm_packs_epi32(dHi, dLo);
d0 = _mm_packs_epi32(d0, dLo);
dHi = _mm_sad_epu8(colorHi, color1);
dLo = _mm_sad_epu8(colorLo, color1);
dLo = _mm_packs_epi32(dHi, dLo);
d1 = _mm_packs_epi32(d1, dLo);
dHi = _mm_sad_epu8(colorHi, color2);
dLo = _mm_sad_epu8(colorLo, color2);
dLo = _mm_packs_epi32(dHi, dLo);
d2 = _mm_packs_epi32(d2, dLo);
dHi = _mm_sad_epu8(colorHi, color3);
dLo = _mm_sad_epu8(colorLo, color3);
dLo = _mm_packs_epi32(dHi, dLo);
d3 = _mm_packs_epi32(d3, dLo);
// Compare the distances.
__m128i b0 = _mm_cmpgt_epi16(d0, d3);
__m128i b1 = _mm_cmpgt_epi16(d1, d2);
__m128i b2 = _mm_cmpgt_epi16(d0, d2);
__m128i b3 = _mm_cmpgt_epi16(d1, d3);
__m128i b4 = _mm_cmpgt_epi16(d2, d3);
// Compute the color index.
__m128i x0 = _mm_and_si128(b2, b1);
__m128i x1 = _mm_and_si128(b3, b0);
__m128i x2 = _mm_and_si128(b4, b0);
__m128i indexBit0 = _mm_or_si128(x0, x1);
indexBit0 = _mm_and_si128(indexBit0, *((__m128i*)SIMD_word_2));
__m128i indexBit1 = _mm_and_si128(x2, *((__m128i*)SIMD_word_1));
__m128i index = _mm_or_si128(indexBit1, indexBit0);
// Pack the index into the result.
__m128i indexHi = _mm_shuffle_epi32(index, R_SHUFFLE_D(2, 3, 0, 1));
indexHi = _mm_unpacklo_epi16(indexHi, *((__m128i*)SIMD_word_0));
indexHi = _mm_slli_epi32(indexHi, 8);
__m128i indexLo = _mm_unpacklo_epi16(index, *((__m128i*)SIMD_word_0));
result = _mm_slli_epi32(result, 16);
result = _mm_or_si128(result, indexHi);
result = _mm_or_si128(result, indexLo);
}
// Pack the 16 2-bit color indices into a single 32-bit value.
__m128i result1 = _mm_shuffle_epi32(result, R_SHUFFLE_D(1, 2, 3, 0));
__m128i result2 = _mm_shuffle_epi32(result, R_SHUFFLE_D(2, 3, 0, 1));
__m128i result3 = _mm_shuffle_epi32(result, R_SHUFFLE_D(3, 0, 1, 2));
result1 = _mm_slli_epi32(result1, 2);
result2 = _mm_slli_epi32(result2, 4);
result3 = _mm_slli_epi32(result3, 6);
result = _mm_or_si128(result, result1);
result = _mm_or_si128(result, result2);
result = _mm_or_si128(result, result3);
// Store the result.
*((int*)outBuf) = _mm_cvtsi128_si32(result);
outBuf += 4;
}
// Quantize the alpha channel of the pixels in colorBlock to 8 alpha values that are equally
// spaced along the interval defined by minAlpha and maxAlpha. 48 bits (3 bits per alpha) are
// written to outBuf, which represent the indices of the 8 alpha values.
void EmitAlphaIndices(const BYTE* colorBlock, BYTE*& outBuf, const BYTE minAlpha, const BYTE maxAlpha)
{
// Pack the alpha values of the first two rows of colorBlock.
__m128i alpha1 = _mm_load_si128((__m128i*)colorBlock);
alpha1 = _mm_srli_epi32(alpha1, 24);
__m128i alpha2 = _mm_load_si128((__m128i*)(colorBlock + 16));
alpha2 = _mm_srli_epi32(alpha2, 24);
alpha1 = _mm_packus_epi16(alpha1, alpha2);
// Pack the alpha values of the last two rows of colorBlock.
__m128i alpha3 = _mm_load_si128((__m128i*)(colorBlock + 32));
alpha3 = _mm_srli_epi32(alpha3, 24);
__m128i alpha4 = _mm_load_si128((__m128i*)(colorBlock + 48));
alpha4 = _mm_srli_epi32(alpha4, 24);
alpha3 = _mm_packus_epi16(alpha3, alpha4);
// Pack all 16 alpha values together.
__m128i alpha = _mm_packus_epi16(alpha1, alpha3);
// Unpack the maximum alpha value.
__m128i max = _mm_cvtsi32_si128(maxAlpha);
max = _mm_shufflelo_epi16(max, R_SHUFFLE_D(0, 0, 0, 0));
max = _mm_shuffle_epi32(max, R_SHUFFLE_D(0, 0, 0, 0));
// Unpack the minimum alpha value.
__m128i min = _mm_cvtsi32_si128(minAlpha);
min = _mm_shufflelo_epi16(min, R_SHUFFLE_D(0, 0, 0, 0));
min = _mm_shuffle_epi32(min, R_SHUFFLE_D(0, 0, 0, 0));
// Compute the midpoint offset between any two interpolated alpha values.
__m128i mid = _mm_sub_epi16(max, min);
mid = _mm_mulhi_epi16(mid, *((__m128i*)SIMD_word_div_by_14));
// Compute the first midpoint.
__m128i ab1 = min;
ab1 = _mm_add_epi16(ab1, mid);
ab1 = _mm_packus_epi16(ab1, ab1);
// Compute the next three midpoints.
__m128i max456 = _mm_mullo_epi16(max, *((__m128i*)SIMD_word_scale66554400));
__m128i min123 = _mm_mullo_epi16(min, *((__m128i*)SIMD_word_scale11223300));
__m128i ab234 = _mm_add_epi16(max456, min123);
ab234 = _mm_mulhi_epi16(ab234, *((__m128i*)SIMD_word_div_by_7));
ab234 = _mm_add_epi16(ab234, mid);
__m128i ab2 = _mm_shuffle_epi32(ab234, R_SHUFFLE_D(0, 0, 0, 0));
ab2 = _mm_packus_epi16(ab2, ab2);
__m128i ab3 = _mm_shuffle_epi32(ab234, R_SHUFFLE_D(1, 1, 1, 1));
ab3 = _mm_packus_epi16(ab3, ab3);
__m128i ab4 = _mm_shuffle_epi32(ab234, R_SHUFFLE_D(2, 2, 2, 2));
ab4 = _mm_packus_epi16(ab4, ab4);
// Compute the last three midpoints.
__m128i max123 = _mm_mullo_epi16(max, *((__m128i*)SIMD_word_scale11223300));
__m128i min456 = _mm_mullo_epi16(min, *((__m128i*)SIMD_word_scale66554400));
__m128i ab567 = _mm_add_epi16(max123, min456);
ab567 = _mm_mulhi_epi16(ab567, *((__m128i*)SIMD_word_div_by_7));
ab567 = _mm_add_epi16(ab567, mid);
__m128i ab5 = _mm_shuffle_epi32(ab567, R_SHUFFLE_D(2, 2, 2, 2));
ab5 = _mm_packus_epi16(ab5, ab5);
__m128i ab6 = _mm_shuffle_epi32(ab567, R_SHUFFLE_D(1, 1, 1, 1));
ab6 = _mm_packus_epi16(ab6, ab6);
__m128i ab7 = _mm_shuffle_epi32(ab567, R_SHUFFLE_D(0, 0, 0, 0));
ab7 = _mm_packus_epi16(ab7, ab7);
// Compare the alpha values to the midpoints.
__m128i b1 = _mm_min_epu8(ab1, alpha);
b1 = _mm_cmpeq_epi8(b1, alpha);
b1 = _mm_and_si128(b1, *((__m128i*)SIMD_byte_1));
__m128i b2 = _mm_min_epu8(ab2, alpha);
b2 = _mm_cmpeq_epi8(b2, alpha);
b2 = _mm_and_si128(b2, *((__m128i*)SIMD_byte_1));
__m128i b3 = _mm_min_epu8(ab3, alpha);
b3 = _mm_cmpeq_epi8(b3, alpha);
b3 = _mm_and_si128(b3, *((__m128i*)SIMD_byte_1));
__m128i b4 = _mm_min_epu8(ab4, alpha);
b4 = _mm_cmpeq_epi8(b4, alpha);
b4 = _mm_and_si128(b4, *((__m128i*)SIMD_byte_1));
__m128i b5 = _mm_min_epu8(ab5, alpha);
b5 = _mm_cmpeq_epi8(b5, alpha);
b5 = _mm_and_si128(b5, *((__m128i*)SIMD_byte_1));
__m128i b6 = _mm_min_epu8(ab6, alpha);
b6 = _mm_cmpeq_epi8(b6, alpha);
b6 = _mm_and_si128(b6, *((__m128i*)SIMD_byte_1));
__m128i b7 = _mm_min_epu8(ab7, alpha);
b7 = _mm_cmpeq_epi8(b7, alpha);
b7 = _mm_and_si128(b7, *((__m128i*)SIMD_byte_1));
// Compute the alpha indexes.
__m128i index = _mm_adds_epu8(b1, b2);
index = _mm_adds_epu8(index, b3);
index = _mm_adds_epu8(index, b4);
index = _mm_adds_epu8(index, b5);
index = _mm_adds_epu8(index, b6);
index = _mm_adds_epu8(index, b7);
// Convert natural index ordering to DXT index ordering.
__m128i byte1 = _mm_load_si128((__m128i*)SIMD_byte_1);
index = _mm_adds_epu8(index, byte1);
__m128i byte7 = _mm_load_si128((__m128i*)SIMD_byte_7);
index = _mm_and_si128(index, byte7);
__m128i byte2 = _mm_load_si128((__m128i*)SIMD_byte_2);
__m128i swapMinMax = _mm_cmpgt_epi8(byte2, index);
swapMinMax = _mm_and_si128(swapMinMax, byte1);
index = _mm_xor_si128(index, swapMinMax);
// Pack the 16 3-bit indices into 6 bytes.
__m128i alphaBitMask0 = _mm_load_si128((__m128i*)SIMD_dword_alpha_bit_mask0);
__m128i index0 = _mm_and_si128(index, alphaBitMask0);
__m128i index1 = _mm_srli_epi64(index, 8 - 3);
__m128i alphaBitMask1 = _mm_load_si128((__m128i*)SIMD_dword_alpha_bit_mask1);
index1 = _mm_and_si128(index1, alphaBitMask1);
__m128i index2 = _mm_srli_epi64(index, 16 - 6);
__m128i alphaBitMask2 = _mm_load_si128((__m128i*)SIMD_dword_alpha_bit_mask2);
index2 = _mm_and_si128(index2, alphaBitMask2);
__m128i index3 = _mm_srli_epi64(index, 24 - 9);
__m128i alphaBitMask3 = _mm_load_si128((__m128i*)SIMD_dword_alpha_bit_mask3);
index3 = _mm_and_si128(index3, alphaBitMask3);
__m128i index4 = _mm_srli_epi64(index, 32 - 12);
__m128i alphaBitMask4 = _mm_load_si128((__m128i*)SIMD_dword_alpha_bit_mask4);
index4 = _mm_and_si128(index4, alphaBitMask4);
__m128i index5 = _mm_srli_epi64(index, 40 - 15);
__m128i alphaBitMask5 = _mm_load_si128((__m128i*)SIMD_dword_alpha_bit_mask5);
index5 = _mm_and_si128(index5, alphaBitMask5);
__m128i index6 = _mm_srli_epi64(index, 48 - 18);
__m128i alphaBitMask6 = _mm_load_si128((__m128i*)SIMD_dword_alpha_bit_mask6);
index6 = _mm_and_si128(index6, alphaBitMask6);
__m128i index7 = _mm_srli_epi64(index, 56 - 21);
__m128i alphaBitMask7 = _mm_load_si128((__m128i*)SIMD_dword_alpha_bit_mask7);
index7 = _mm_and_si128(index7, alphaBitMask7);
index = _mm_or_si128(index0, index1);
index = _mm_or_si128(index, index2);
index = _mm_or_si128(index, index3);
index = _mm_or_si128(index, index4);
index = _mm_or_si128(index, index5);
index = _mm_or_si128(index, index6);
index = _mm_or_si128(index, index7);
// Store the indexes.
*((int*)outBuf) = _mm_cvtsi128_si32(index);
index = _mm_shuffle_epi32(index, R_SHUFFLE_D(2, 3, 0, 1));
*((int*)(outBuf + 3)) = _mm_cvtsi128_si32(index);
outBuf += 6;
}
}

View File

@ -0,0 +1,120 @@
/* FasTC
* Copyright (c) 2013 University of North Carolina at Chapel Hill.
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for educational, research, and non-profit purposes, without
* fee, and without a written agreement is hereby granted, provided that the
* above copyright notice, this paragraph, and the following four paragraphs
* appear in all copies.
*
* Permission to incorporate this software into commercial products may be
* obtained by contacting the authors or the Office of Technology Development
* at the University of North Carolina at Chapel Hill <otd@unc.edu>.
*
* This software program and documentation are copyrighted by the University of
* North Carolina at Chapel Hill. The software program and documentation are
* supplied "as is," without any accompanying services from the University of
* North Carolina at Chapel Hill or the authors. The University of North
* Carolina at Chapel Hill and the authors do not warrant that the operation of
* the program will be uninterrupted or error-free. The end-user understands
* that the program was developed for research purposes and is advised not to
* rely exclusively on the program for any reason.
*
* IN NO EVENT SHALL THE UNIVERSITY OF NORTH CAROLINA AT CHAPEL HILL OR THE
* AUTHORS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL,
* OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF
* THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF NORTH CAROLINA
* AT CHAPEL HILL OR THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE UNIVERSITY OF NORTH CAROLINA AT CHAPEL HILL AND THE AUTHORS SPECIFICALLY
* DISCLAIM ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE AND ANY
* STATUTORY WARRANTY OF NON-INFRINGEMENT. THE SOFTWARE PROVIDED HEREUNDER IS ON
* AN "AS IS" BASIS, AND THE UNIVERSITY OF NORTH CAROLINA AT CHAPEL HILL AND
* THE AUTHORS HAVE NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
* ENHANCEMENTS, OR MODIFICATIONS.
*
* Please send all BUG REPORTS to <pavel@cs.unc.edu>.
*
* The authors may be contacted via:
*
* Pavel Krajcevski
* Dept of Computer Science
* 201 S Columbia St
* Frederick P. Brooks, Jr. Computer Science Bldg
* Chapel Hill, NC 27599-3175
* USA
*
* <http://gamma.cs.unc.edu/FasTC/>
*/
#include "DXTCompressor.h"
#include <cassert>
#include <cstdlib>
#include <cstring>
#include "Pixel.h"
namespace DXTC
{
void DecompressDXT1Block(const uint8 *block, uint32 *outBuf) {
uint16 colorA = block[0];
colorA <<= 8;
colorA |= block[1];
uint16 colorB = block[2];
colorB <<= 8;
colorB |= block[3];
uint32 mod = reinterpret_cast<const uint32 *>(block + 4)[0];
uint8 kFiveSixFive[4] = { 0, 5, 6, 5 };
FasTC::Pixel a, b, c, d;
a.FromBits(reinterpret_cast<const uint8 *>(&colorA), kFiveSixFive);
b.FromBits(reinterpret_cast<const uint8 *>(&colorB), kFiveSixFive);
uint8 kFullDepth[4] = {8, 8, 8, 8};
a.ChangeBitDepth(kFullDepth);
b.ChangeBitDepth(kFullDepth);
d = (a + b*2) / 3;
c = (a*2 + b) / 3;
FasTC::Pixel *colors[4] = { &a, &b, &c, &d };
uint32 *outPixels = reinterpret_cast<uint32 *>(outBuf);
for(uint32 i = 0; i < 16; i++) {
outPixels[i] = colors[(mod >> (i*2)) & 3]->Pack();
}
}
void DecompressDXT1(const DecompressionJob &dcj)
{
assert(!(dcj.height & 3));
assert(!(dcj.width & 3));
uint32 blockW = dcj.width >> 2;
uint32 blockH = dcj.height >> 2;
const uint32 blockSz = 8;
uint32 *outPixels = reinterpret_cast<uint32 *>(dcj.outBuf);
uint32 outBlock[16];
for(int j = 0; j < blockH; j++) {
for(int i = 0; i < blockW; i++) {
uint32 offset = (j * blockW + i) * blockSz;
DecompressDXT1Block(dcj.inBuf + offset, outBlock);
for(uint32 y = 0; y < 4; y++)
for(uint32 x = 0; x < 4; x++) {
offset = (j*4 + y)*dcj.width + ((i*4)+x);
outPixels[offset] = outBlock[y*4 + x];
}
}
}
}
}