// NeL - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
#include "s3tc_compressor.h"
#include
#include
using namespace std;
// ***************************************************************************
static void compressMipMap(uint8 *pixSrc, sint width, sint height, vector &compdata, CS3TCCompressor::DDS_HEADER &dest, sint algo)
{
// Filling DDSURFACEDESC structure for output
//===========================================
memset(&dest, 0, sizeof(dest));
dest.dwSize = sizeof(dest);
dest.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_LINEARSIZE;
dest.dwHeight = height;
dest.dwWidth = width;
dest.ddpf.dwSize = sizeof(CS3TCCompressor::DDS_PIXELFORMAT);
dest.ddpf.dwFlags = DDPF_FOURCC;
dest.dwCaps = DDSCAPS_TEXTURE;
// Setting flags
int flags = squish::kColourIterativeClusterFit; // for best quality
switch(algo)
{
case DXT1:
case DXT1A:
flags |= squish::kDxt1;
dest.ddpf.dwFourCC = MAKEFOURCC('D','X', 'T', '1');
break;
case DXT3:
flags |= squish::kDxt3;
dest.ddpf.dwFourCC = MAKEFOURCC('D','X', 'T', '3');
break;
case DXT5:
flags |= squish::kDxt5;
dest.ddpf.dwFourCC = MAKEFOURCC('D','X', 'T', '5');
break;
}
// Encoding
//===========
// resize dest.
dest.dwLinearSize = squish::GetStorageRequirements(width, height, flags);
compdata.resize(dest.dwLinearSize);
// Go!
float weight[3] = {0.3086f, 0.6094f, 0.0820f};
squish::CompressImage(pixSrc, width, height, &(*compdata.begin()), flags, weight);
/* S3TC is a very good compressor, but make BIG mistakes in some case with DXTC5 and DXTC3
*/
if( algo==DXT5 || algo==DXT3 )
{
sint wBlock= max(1, width/4);
sint hBlock= max(1, height/4);
uint numTotalBlock= wBlock*hBlock;
uint numFixedBlock= 0;
NLMISC::CRGBA *rgbaSrc= (NLMISC::CRGBA*)pixSrc;
for(sint y=0;y>(i*2))&3;
pixVal= invertTable[pixVal];
bits&= ~(3<<(i*2));
bits|= (pixVal<<(i*2));
}
}
// Test: 05 to 1323
/*
uint32 &bits= *(uint32*)(pixDst+12);
for(uint i=0;i<16; i++)
{
uint pixVal= (bits>>(i*2))&3;
if(pixVal==2)
{
uint r= (rand()&0xFF)>>7;
pixVal= r+2;
bits&= ~(3<<(i*2));
bits|= (pixVal<<(i*2));
}
}*/
}
}
}
if(numFixedBlock && numTotalBlock)
{
nlinfo("Fix %d blocks on %d total ", numFixedBlock, numTotalBlock);
}
}
}
// ***************************************************************************
CS3TCCompressor::CS3TCCompressor()
{
}
// ***************************************************************************
void CS3TCCompressor::compress(const NLMISC::CBitmap &bmpSrc, bool optMipMap, uint algo, NLMISC::IStream &output)
{
vector CompressedMipMaps;
DDS_HEADER dest;
NLMISC::CBitmap picSrc= bmpSrc;
// For all mipmaps, compress.
if(optMipMap)
{
// Build the mipmaps.
picSrc.buildMipMaps();
}
for(sint mp= 0;mp<(sint)picSrc.getMipMapCount();mp++)
{
uint8 *pixDest;
uint8 *pixSrc= picSrc.getPixels(mp).getPtr();
sint w= picSrc.getWidth(mp);
sint h= picSrc.getHeight(mp);
vector compdata;
DDS_HEADER temp;
compressMipMap(pixSrc, w, h, compdata, temp, algo);
// Copy the result of the base dds in the dest.
if(mp==0)
dest= temp;
// Append this data to the global data.
uint delta= (uint)CompressedMipMaps.size();
CompressedMipMaps.resize(CompressedMipMaps.size()+compdata.size());
pixDest= &(*CompressedMipMaps.begin())+ delta;
memcpy( pixDest, &(*compdata.begin()), compdata.size());
}
// Setting Nb MipMap.
dest.dwFlags |= DDSD_MIPMAPCOUNT;
dest.dwCaps |= DDSCAPS_MIPMAP;
dest.dwMipMapCount = picSrc.getMipMapCount();
// Saving DDS file
//=================
output.serialBuffer((uint8*)std::string("DDS ").c_str(),4);
output.serialBuffer((uint8*) &dest, sizeof(dest));
output.serialBuffer(&(*CompressedMipMaps.begin()), (uint)CompressedMipMaps.size());
}