Reorganize a lot of code. Should probably split image and image file classes to separate IO operations and whatnot.

This commit is contained in:
Pavel Krajcevski 2012-09-21 12:39:09 -04:00
parent 72c44f51d1
commit c1222d75f9
7 changed files with 121 additions and 55 deletions

View file

@ -3,6 +3,7 @@
#include <string.h> #include <string.h>
#include "TexComp.h" #include "TexComp.h"
#include "ImageFile.h"
void PrintUsage() { void PrintUsage() {
fprintf(stderr, "Usage: tc [-s|-t <num>] <imagefile>\n"); fprintf(stderr, "Usage: tc [-s|-t <num>] <imagefile>\n");
@ -82,7 +83,7 @@ int main(int argc, char **argv) {
settings.iQuality = quality; settings.iQuality = quality;
settings.iNumCompressions = numCompressions; settings.iNumCompressions = numCompressions;
CompressedImage *ci = CompressImage(file, settings); CompressedImage *ci = file.Compress(settings);
if(NULL == ci) { if(NULL == ci) {
fprintf(stderr, "Error compressing image!\n"); fprintf(stderr, "Error compressing image!\n");
return 1; return 1;

View file

@ -1,30 +1,62 @@
#ifndef _TEX_COMP_H_ #ifndef _TEX_COMP_H_
#define _TEX_COMP_H_ #define _TEX_COMP_H_
#include "ImageFile.h"
#include "CompressedImage.h" #include "CompressedImage.h"
// Forward declarations
class ImageFile;
class CompressedImage;
struct SCompressionSettings { struct SCompressionSettings {
SCompressionSettings(); // defaults SCompressionSettings(); // defaults
// The compression format for the image.
ECompressionFormat format; ECompressionFormat format;
// The flag that requests us to use SIMD, if it is available
bool bUseSIMD; bool bUseSIMD;
// The number of threads to spawn in order to process the data
int iNumThreads; int iNumThreads;
// Some compression formats take a measurement of quality when
// compressing an image. If the format supports it, this value
// will be used for quality purposes.
int iQuality; int iQuality;
// The number of compressions to perform. The program will compress
// the image this many times, and then take the average of the timing.
int iNumCompressions; int iNumCompressions;
// This setting measures the number of blocks that a thread
// will process at any given time. If this value is zero,
// which is the default, the work will be divided by the
// number of threads, and each thread will do it's job and
// exit.
int iJobSize;
}; };
extern CompressedImage * CompressImage( extern bool CompressImageData(
const ImageFile &, const unsigned char *data,
const unsigned int dataSz,
unsigned char *cmpData,
const unsigned int cmpDataSz,
const SCompressionSettings &settings const SCompressionSettings &settings
); );
// A compression function format. It takes the raw data and image dimensions and
// returns the compressed image data into outData. It is assumed that there is
// enough space allocated for outData to store the compressed data. Allocation
// is dependent on the compression format.
typedef void (* CompressionFunc)( typedef void (* CompressionFunc)(
const unsigned char *inData, const unsigned char *inData, // Raw image data
unsigned char *outData, unsigned char *outData, // Buffer to store compressed data.
unsigned int width, unsigned int width, // Image width
unsigned int height unsigned int height // Image height
); );
extern double ComputePSNR(const CompressedImage &, const ImageFile &); // This function computes the Peak Signal to Noise Ratio between a
// compressed image and a raw image.
extern double ComputePSNR(const CompressedImage &ci, const ImageFile &file);
#endif //_TEX_COMP_H_ #endif //_TEX_COMP_H_

View file

@ -1,6 +1,7 @@
#include "BC7Compressor.h" #include "BC7Compressor.h"
#include "TexComp.h" #include "TexComp.h"
#include "ThreadGroup.h" #include "ThreadGroup.h"
#include "ImageFile.h"
#include <math.h> #include <math.h>
#include <stdlib.h> #include <stdlib.h>
@ -22,6 +23,11 @@ static void clamp(T &x, const T &minX, const T &maxX) {
x = max(min(maxX, x), minX); x = max(min(maxX, x), minX);
} }
template <typename T>
static inline T sad(const T &a, const T &b) {
return (a > b)? a - b : b - a;
}
SCompressionSettings:: SCompressionSettings() SCompressionSettings:: SCompressionSettings()
: format(eCompressionFormat_BPTC) : format(eCompressionFormat_BPTC)
, bUseSIMD(false) , bUseSIMD(false)
@ -58,7 +64,8 @@ static void ReportError(const char *msg) {
} }
static double CompressImageInSerial( static double CompressImageInSerial(
const ImageFile &img, const unsigned char *imgData,
const unsigned int imgDataSz,
const SCompressionSettings &settings, const SCompressionSettings &settings,
const CompressionFunc f, const CompressionFunc f,
unsigned char *outBuf unsigned char *outBuf
@ -71,7 +78,8 @@ static double CompressImageInSerial(
stopWatch.Reset(); stopWatch.Reset();
stopWatch.Start(); stopWatch.Start();
(*f)(img.GetPixels(), outBuf, img.GetWidth(), img.GetHeight()); // !FIXME! We're assuming that we have 4x4 blocks here...
(*f)(imgData, outBuf, imgDataSz / 16, 4);
stopWatch.Stop(); stopWatch.Stop();
@ -83,16 +91,17 @@ static double CompressImageInSerial(
} }
static double CompressImageWithThreads( static double CompressImageWithThreads(
const ImageFile &img, const unsigned char *imgData,
const unsigned int imgDataSz,
const SCompressionSettings &settings, const SCompressionSettings &settings,
const CompressionFunc f, const CompressionFunc f,
unsigned char *outBuf unsigned char *outBuf
) { ) {
ThreadGroup tgrp (settings.iNumThreads, img, f, outBuf); ThreadGroup tgrp (settings.iNumThreads, imgData, imgDataSz, f, outBuf);
if(!(tgrp.PrepareThreads())) { if(!(tgrp.PrepareThreads())) {
assert(!"Thread group failed to prepare threads?!"); assert(!"Thread group failed to prepare threads?!");
return NULL; return -1.0f;
} }
double cmpTimeTotal = 0.0; double cmpTimeTotal = 0.0;
@ -120,10 +129,13 @@ static double CompressImageWithWorkerQueue(
unsigned char *outBuf unsigned char *outBuf
) { ) {
return 0.0; return 0.0;
} }
CompressedImage * CompressImage( bool CompressImageData(
const ImageFile &img, const unsigned char *data,
const unsigned int dataSz,
unsigned char *cmpData,
const unsigned int cmpDataSz,
const SCompressionSettings &settings const SCompressionSettings &settings
) { ) {
@ -132,27 +144,31 @@ CompressedImage * CompressImage(
#ifndef HAS_SSE_41 #ifndef HAS_SSE_41
if(settings.bUseSIMD) { if(settings.bUseSIMD) {
ReportError("Platform does not support SIMD!\n"); ReportError("Platform does not support SIMD!\n");
return NULL; return false;
} }
#endif #endif
const unsigned int dataSz = img.GetWidth() * img.GetHeight() * 4; if(dataSz <= 0) {
ReportError("No data sent to compress!");
return false;
}
// Allocate data based on the compression method // Allocate data based on the compression method
int cmpDataSz = 0; int cmpDataSzNeeded = 0;
switch(settings.format) { switch(settings.format) {
case eCompressionFormat_DXT1: cmpDataSz = dataSz / 8; case eCompressionFormat_DXT1: cmpDataSzNeeded = dataSz / 8;
case eCompressionFormat_DXT5: cmpDataSz = dataSz / 4; case eCompressionFormat_DXT5: cmpDataSzNeeded = dataSz / 4;
case eCompressionFormat_BPTC: cmpDataSz = dataSz / 4; case eCompressionFormat_BPTC: cmpDataSzNeeded = dataSz / 4;
} }
if(cmpDataSz == 0) { if(cmpDataSzNeeded == 0) {
ReportError("Unknown compression format"); ReportError("Unknown compression format");
return NULL; return false;
}
else if(cmpDataSzNeeded > cmpDataSz) {
ReportError("Not enough space for compressed data!");
return false;
} }
CompressedImage *outImg = NULL;
unsigned char *cmpData = new unsigned char[cmpDataSz];
CompressionFunc f = ChooseFuncFromSettings(settings); CompressionFunc f = ChooseFuncFromSettings(settings);
if(f) { if(f) {
@ -160,32 +176,21 @@ CompressedImage * CompressImage(
double cmpMSTime = 0.0; double cmpMSTime = 0.0;
if(settings.iNumThreads > 1) { if(settings.iNumThreads > 1) {
cmpMSTime = CompressImageWithThreads(img, settings, f, cmpData); cmpMSTime = CompressImageWithThreads(data, dataSz, settings, f, cmpData);
} }
else { else {
cmpMSTime = CompressImageInSerial(img, settings, f, cmpData); cmpMSTime = CompressImageInSerial(data, dataSz, settings, f, cmpData);
} }
outImg = new CompressedImage(img.GetWidth(), img.GetHeight(), settings.format, cmpData);
// Report compression time // Report compression time
fprintf(stdout, "Compression time: %0.3f ms\n", cmpMSTime); fprintf(stdout, "Compression time: %0.3f ms\n", cmpMSTime);
} }
else { else {
ReportError("Could not find adequate compression function for specified settings"); ReportError("Could not find adequate compression function for specified settings");
delete [] cmpData; return false;
return NULL;
} }
// Cleanup return true;
delete [] cmpData;
return outImg;
}
template <typename T>
static inline T sad(const T &a, const T &b) {
return (a > b)? a - b : b - a;
} }
double ComputePSNR(const CompressedImage &ci, const ImageFile &file) { double ComputePSNR(const CompressedImage &ci, const ImageFile &file) {
@ -196,7 +201,7 @@ double ComputePSNR(const CompressedImage &ci, const ImageFile &file) {
return -1.0f; return -1.0f;
} }
const unsigned char *rawData = file.GetPixels(); const unsigned char *rawData = file.RawData();
const double wr = 1.0; const double wr = 1.0;
const double wg = 1.0; const double wg = 1.0;

View file

@ -52,14 +52,15 @@ void CmpThread::operator()() {
} }
ThreadGroup::ThreadGroup( int numThreads, const ImageFile &image, CompressionFunc func, unsigned char *outBuf ) ThreadGroup::ThreadGroup( int numThreads, const unsigned char *inBuf, unsigned int inBufSz, CompressionFunc func, unsigned char *outBuf )
: m_StartBarrier(new boost::barrier(numThreads + 1)) : m_StartBarrier(new boost::barrier(numThreads + 1))
, m_FinishMutex(new boost::mutex()) , m_FinishMutex(new boost::mutex())
, m_FinishCV(new boost::condition_variable()) , m_FinishCV(new boost::condition_variable())
, m_NumThreads(numThreads) , m_NumThreads(numThreads)
, m_ActiveThreads(0) , m_ActiveThreads(0)
, m_Func(func) , m_Func(func)
, m_Image(image) , m_ImageDataSz(inBufSz)
, m_ImageData(inBuf)
, m_OutBuf(outBuf) , m_OutBuf(outBuf)
, m_ThreadState(eThreadState_Done) , m_ThreadState(eThreadState_Done)
, m_ExitFlag(false) , m_ExitFlag(false)
@ -107,13 +108,9 @@ bool ThreadGroup::PrepareThreads() {
return true; return true;
} }
// Make sure that the image dimensions are multiples of 4
assert((m_Image.GetWidth() & 3) == 0);
assert((m_Image.GetHeight() & 3) == 0);
// We can assume that the image data is in block stream order // We can assume that the image data is in block stream order
// so, the size of the data given to each thread will be (nb*4)x4 // so, the size of the data given to each thread will be (nb*4)x4
int numBlocks = (m_Image.GetWidth() * m_Image.GetHeight()) / 16; int numBlocks = m_ImageDataSz / 64;
int blocksProcessed = 0; int blocksProcessed = 0;
int blocksPerThread = (numBlocks/m_NumThreads) + ((numBlocks % m_NumThreads)? 1 : 0); int blocksPerThread = (numBlocks/m_NumThreads) + ((numBlocks % m_NumThreads)? 1 : 0);
@ -135,7 +132,7 @@ bool ThreadGroup::PrepareThreads() {
t.m_Width = numBlocksThisThread * 4; t.m_Width = numBlocksThisThread * 4;
t.m_CmpFunc = m_Func; t.m_CmpFunc = m_Func;
t.m_OutBuf = m_OutBuf + (blocksProcessed * GetCompressedBlockSize()); t.m_OutBuf = m_OutBuf + (blocksProcessed * GetCompressedBlockSize());
t.m_InBuf = m_Image.GetPixels() + (blocksProcessed * GetUncompressedBlockSize()); t.m_InBuf = m_ImageData + (blocksProcessed * GetUncompressedBlockSize());
blocksProcessed += numBlocksThisThread; blocksProcessed += numBlocksThisThread;

View file

@ -42,7 +42,7 @@ public:
class ThreadGroup { class ThreadGroup {
public: public:
ThreadGroup( int numThreads, const ImageFile &, CompressionFunc func, unsigned char *outBuf ); ThreadGroup( int numThreads, const unsigned char *inBuf, unsigned int inBufSz, CompressionFunc func, unsigned char *outBuf );
~ThreadGroup(); ~ThreadGroup();
bool PrepareThreads(); bool PrepareThreads();
@ -74,7 +74,8 @@ class ThreadGroup {
boost::thread *m_ThreadHandles[kMaxNumThreads]; boost::thread *m_ThreadHandles[kMaxNumThreads];
// State variables. // State variables.
const ImageFile &m_Image; const unsigned int m_ImageDataSz;
const unsigned char *const m_ImageData;
const CompressionFunc m_Func; const CompressionFunc m_Func;
unsigned char *m_OutBuf; unsigned char *m_OutBuf;

View file

@ -3,6 +3,10 @@
#include "ImageFileFormat.h" #include "ImageFileFormat.h"
// Forward declare
class CompressedImage;
// Class definition
class ImageFile { class ImageFile {
public: public:
@ -13,7 +17,9 @@ public:
unsigned int GetWidth() const { return m_Width; } unsigned int GetWidth() const { return m_Width; }
unsigned int GetHeight() const { return m_Height; } unsigned int GetHeight() const { return m_Height; }
const unsigned char *GetPixels() const { return m_PixelData; } CompressedImage *Compress(const SCompressionSettings &) const;
const unsigned char *RawData() const { return m_PixelData; }
private: private:
unsigned int m_Handle; unsigned int m_Handle;

View file

@ -4,8 +4,10 @@
#include <limits.h> #include <limits.h>
#include <assert.h> #include <assert.h>
#include "TexComp.h"
#include "ImageFile.h" #include "ImageFile.h"
#include "ImageLoader.h" #include "ImageLoader.h"
#include "CompressedImage.h"
#ifdef PNG_FOUND #ifdef PNG_FOUND
# include "ImageLoaderPNG.h" # include "ImageLoaderPNG.h"
@ -302,3 +304,25 @@ unsigned char *ImageFile::ReadFileData(const char *filename) {
return rawData; return rawData;
} }
#endif #endif
CompressedImage *ImageFile::Compress(const SCompressionSettings &settings) const {
CompressedImage *outImg = NULL;
const unsigned int dataSz = GetWidth() * GetHeight() * 4;
assert(dataSz > 0);
// Allocate data based on the compression method
int cmpDataSz = 0;
switch(settings.format) {
case eCompressionFormat_DXT1: cmpDataSz = dataSz / 8;
case eCompressionFormat_DXT5: cmpDataSz = dataSz / 4;
case eCompressionFormat_BPTC: cmpDataSz = dataSz / 4;
}
unsigned char *cmpData = new unsigned char[cmpDataSz];
CompressImageData(m_PixelData, dataSz, cmpData, cmpDataSz, settings);
outImg = new CompressedImage(GetWidth(), GetHeight(), settings.format, cmpData);
return outImg;
}