// 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; dest.dwLinearSize = std::max(4, width) * std::max(4, height); // 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'); dest.dwLinearSize /= 2; 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. uint32 encodeSz = squish::GetStorageRequirements(width, height, flags); compdata.resize(encodeSz); // 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()); }