mirror of
https://github.com/yuzu-emu/FasTC.git
synced 2024-11-27 21:04:20 +01:00
Add DXT encoder from J.M.P. Van Waveren
This commit is contained in:
parent
4f2db726b7
commit
674c18b9d9
@ -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);
|
||||
|
@ -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})
|
||||
|
@ -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 )
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
62
DXTEncoder/CMakeLists.txt
Executable 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 )
|
34
DXTEncoder/include/DXTCompressor.h
Executable file
34
DXTEncoder/include/DXTCompressor.h
Executable 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
322
DXTEncoder/src/DXTCompressor.cpp
Executable 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));
|
||||
}
|
||||
}
|
552
DXTEncoder/src/DXTCompressorSSE2DLL.cpp
Executable file
552
DXTEncoder/src/DXTCompressorSSE2DLL.cpp
Executable 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;
|
||||
}
|
||||
}
|
120
DXTEncoder/src/DXTDecompressor.cpp
Normal file
120
DXTEncoder/src/DXTDecompressor.cpp
Normal 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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user