Broke the coupling between ImageFile and Image. One is strictly for IO, the other can be platform and file format agnostic.

This commit is contained in:
Pavel Krajcevski 2012-09-21 16:42:15 -04:00
parent c1222d75f9
commit 2c22889533
10 changed files with 396 additions and 280 deletions

View File

@ -4,6 +4,7 @@
#include "TexComp.h"
#include "ImageFile.h"
#include "Image.h"
void PrintUsage() {
fprintf(stderr, "Usage: tc [-s|-t <num>] <imagefile>\n");
@ -76,6 +77,11 @@ int main(int argc, char **argv) {
}
ImageFile file (argv[fileArg]);
const Image *img = file.GetImage();
if(NULL == img) {
fprintf(stderr, "Error loading file: %s\n", argv[fileArg]);
return 1;
}
SCompressionSettings settings;
settings.bUseSIMD = bUseSIMD;
@ -83,13 +89,13 @@ int main(int argc, char **argv) {
settings.iQuality = quality;
settings.iNumCompressions = numCompressions;
CompressedImage *ci = file.Compress(settings);
CompressedImage *ci = img->Compress(settings);
if(NULL == ci) {
fprintf(stderr, "Error compressing image!\n");
return 1;
}
double PSNR = ComputePSNR(*ci, file);
double PSNR = img->ComputePSNR(*ci);
if(PSNR > 0.0) {
fprintf(stdout, "PSNR: %.3f\n", PSNR);
}

View File

@ -2,12 +2,14 @@
SET( SOURCES
"src/TexComp.cpp"
"src/CompressedImage.cpp"
"src/Image.cpp"
)
SET( HEADERS
"include/TexComp.h"
"include/CompressedImage.h"
"include/TexCompTypes.h"
"include/Image.h"
)
# Make sure to add the appropriate stopwatch files...

30
Core/include/Image.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef __TEXCOMP_IMAGE_H__
#define __TEXCOMP_IMAGE_H__
#include "TexCompTypes.h"
#include "TexComp.h"
// Forward declarations
class ImageLoader;
// Class definition
class Image {
public:
Image(const ImageLoader &);
const uint8 *RawData() const { return m_PixelData; }
CompressedImage *Compress(const SCompressionSettings &settings) const;
double ComputePSNR(const CompressedImage &ci) const;
uint32 GetWidth() const { return m_Width; }
uint32 GetHeight() const { return m_Height; }
private:
uint32 m_Width;
uint32 m_Height;
uint8 *m_PixelData;
};
#endif // __TEXCOMP_IMAGE_H__

94
Core/src/Image.cpp Normal file
View File

@ -0,0 +1,94 @@
#include "Image.h"
#include "ImageLoader.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <math.h>
template <typename T>
static inline T sad( const T &a, const T &b ) {
return (a > b)? a - b : b - a;
}
Image::Image(const ImageLoader &loader)
: m_PixelData(0)
, m_Width(loader.GetWidth())
, m_Height(loader.GetHeight())
{
if(loader.GetImageData()) {
m_PixelData = new uint8[ loader.GetImageDataSz() ];
if(!m_PixelData) { fprintf(stderr, "%s\n", "Out of memory!"); return; }
memcpy(m_PixelData, loader.GetImageData(), loader.GetImageDataSz());
}
else {
fprintf(stderr, "%s\n", "Failed to get data from image loader!");
}
}
CompressedImage *Image::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;
}
double Image::ComputePSNR(const CompressedImage &ci) const {
unsigned int imageSz = 4 * GetWidth() * GetHeight();
unsigned char *unCompData = new unsigned char[imageSz];
if(!(ci.DecompressImage(unCompData, imageSz))) {
fprintf(stderr, "%s\n", "Failed to decompress image.");
return -1.0f;
}
const double wr = 1.0;
const double wg = 1.0;
const double wb = 1.0;
double MSE = 0.0;
for(int i = 0; i < imageSz; i+=4) {
const unsigned char *pixelDataRaw = m_PixelData + i;
const unsigned char *pixelDataUncomp = unCompData + i;
double dr = double(sad(pixelDataRaw[0], pixelDataUncomp[0])) * wr;
double dg = double(sad(pixelDataRaw[1], pixelDataUncomp[1])) * wg;
double db = double(sad(pixelDataRaw[2], pixelDataUncomp[2])) * wb;
const double pixelMSE =
(double(dr) * double(dr)) +
(double(dg) * double(dg)) +
(double(db) * double(db));
//fprintf(stderr, "Pixel MSE: %f\n", pixelMSE);
MSE += pixelMSE;
}
MSE /= (double(GetWidth()) * double(GetHeight()));
double MAXI =
(255.0 * wr) * (255.0 * wr) +
(255.0 * wg) * (255.0 * wg) +
(255.0 * wb) * (255.0 * wb);
double PSNR = 10 * log10(MAXI/MSE);
// Cleanup
delete unCompData;
return PSNR;
}

View File

@ -1,13 +1,15 @@
#include "BC7Compressor.h"
#include "TexComp.h"
#include "ThreadGroup.h"
#include "ImageFile.h"
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include "BC7Compressor.h"
#include "ThreadGroup.h"
#include "ImageFile.h"
#include "Image.h"
template <typename T>
static T min(const T &a, const T &b) {
return (a < b)? a : b;
@ -192,51 +194,3 @@ bool CompressImageData(
return true;
}
double ComputePSNR(const CompressedImage &ci, const ImageFile &file) {
unsigned int imageSz = 4 * file.GetWidth() * file.GetHeight();
unsigned char *unCompData = new unsigned char[imageSz];
if(!(ci.DecompressImage(unCompData, imageSz))) {
ReportError("Failed to decompress image.");
return -1.0f;
}
const unsigned char *rawData = file.RawData();
const double wr = 1.0;
const double wg = 1.0;
const double wb = 1.0;
double MSE = 0.0;
for(int i = 0; i < imageSz; i+=4) {
const unsigned char *pixelDataRaw = rawData + i;
const unsigned char *pixelDataUncomp = unCompData + i;
double dr = double(sad(pixelDataRaw[0], pixelDataUncomp[0])) * wr;
double dg = double(sad(pixelDataRaw[1], pixelDataUncomp[1])) * wg;
double db = double(sad(pixelDataRaw[2], pixelDataUncomp[2])) * wb;
const double pixelMSE =
(double(dr) * double(dr)) +
(double(dg) * double(dg)) +
(double(db) * double(db));
//fprintf(stderr, "Pixel MSE: %f\n", pixelMSE);
MSE += pixelMSE;
}
MSE /= (double(file.GetWidth()) * double(file.GetHeight()));
double MAXI =
(255.0 * wr) * (255.0 * wr) +
(255.0 * wg) * (255.0 * wg) +
(255.0 * wb) * (255.0 * wb);
double PSNR = 10 * log10(MAXI/MSE);
// Cleanup
delete unCompData;
return PSNR;
}

View File

@ -1,6 +1,7 @@
SET( SOURCES
"src/ImageFile.cpp"
"src/ImageLoader.cpp"
)
SET( HEADERS

View File

@ -11,21 +11,24 @@ class ImageLoader {
uint32 m_Width;
uint32 m_Height;
unsigned int m_RedChannelPrecision;
unsigned char *m_RedData;
uint32 m_RedChannelPrecision;
uint8 *m_RedData;
unsigned int m_GreenChannelPrecision;
unsigned char *m_GreenData;
uint32 m_GreenChannelPrecision;
uint8 *m_GreenData;
unsigned int m_BlueChannelPrecision;
unsigned char *m_BlueData;
uint32 m_BlueChannelPrecision;
uint8 *m_BlueData;
unsigned int m_AlphaChannelPrecision;
unsigned char *m_AlphaData;
uint32 m_AlphaChannelPrecision;
uint8 *m_AlphaData;
const unsigned char *const m_RawData;
uint8 *m_PixelData;
const uint8 *const m_RawData;
ImageLoader(const unsigned char *rawData) : m_RawData(rawData)
ImageLoader(const uint8 *rawData)
: m_RawData(rawData)
, m_PixelData(0)
, m_Width(0), m_Height(0)
, m_RedChannelPrecision(0), m_RedData(0)
, m_GreenChannelPrecision(0), m_GreenData(0)
@ -33,6 +36,9 @@ class ImageLoader {
, m_AlphaChannelPrecision(0), m_AlphaData(0)
{ }
uint32 GetChannelForPixel(uint32 x, uint32 y, uint32 ch);
public:
virtual ~ImageLoader() {
if(m_RedData) {
delete [] m_RedData;
@ -55,23 +61,26 @@ class ImageLoader {
}
}
public:
virtual bool ReadData() = 0;
int GetRedChannelPrecision() const { return m_RedChannelPrecision; }
const unsigned char * GetRedPixelData() const { return m_RedData; }
uint32 GetRedChannelPrecision() const { return m_RedChannelPrecision; }
const uint8 * GetRedPixelData() const { return m_RedData; }
int GetGreenChannelPrecision() const { return m_GreenChannelPrecision; }
const unsigned char * GetGreenPixelData() const { return m_GreenData; }
uint32 GetGreenChannelPrecision() const { return m_GreenChannelPrecision; }
const uint8 * GetGreenPixelData() const { return m_GreenData; }
int GetBlueChannelPrecision() const { return m_BlueChannelPrecision; }
const unsigned char * GetBluePixelData() const { return m_BlueData; }
uint32 GetBlueChannelPrecision() const { return m_BlueChannelPrecision; }
const uint8 * GetBluePixelData() const { return m_BlueData; }
int GetAlphaChannelPrecision() const { return m_AlphaChannelPrecision; }
const unsigned char * GetAlphaPixelData() const { return m_AlphaData; }
uint32 GetAlphaChannelPrecision() const { return m_AlphaChannelPrecision; }
const uint8 * GetAlphaPixelData() const { return m_AlphaData; }
int GetWidth() const { return m_Width; }
int GetHeight() const { return m_Height; }
uint32 GetWidth() const { return m_Width; }
uint32 GetHeight() const { return m_Height; }
uint32 GetImageDataSz() const { return m_Width * m_Height * 4; }
bool LoadImage();
const uint8 *GetImageData() const { return m_PixelData; }
};
#cmakedefine PNG_FOUND

View File

@ -4,6 +4,7 @@
#include "ImageFileFormat.h"
// Forward declare
class Image;
class CompressedImage;
// Class definition
@ -18,19 +19,20 @@ public:
unsigned int GetWidth() const { return m_Width; }
unsigned int GetHeight() const { return m_Height; }
CompressedImage *Compress(const SCompressionSettings &) const;
const unsigned char *RawData() const { return m_PixelData; }
Image *GetImage() const { return m_Image; }
private:
unsigned int m_Handle;
unsigned int m_Width;
unsigned int m_Height;
unsigned char *m_PixelData;
Image *m_Image;
const EImageFileFormat m_FileFormat;
static unsigned char *ReadFileData(const char *filename);
static EImageFileFormat DetectFileFormat(const char *filename);
bool LoadImage(const unsigned char *rawImageData);
Image *LoadImage(const unsigned char *rawImageData) const;
};
#endif // _IMAGE_FILE_H_

View File

@ -8,6 +8,7 @@
#include "ImageFile.h"
#include "ImageLoader.h"
#include "CompressedImage.h"
#include "Image.h"
#ifdef PNG_FOUND
# include "ImageLoaderPNG.h"
@ -33,109 +34,74 @@ static inline T min(const T &a, const T &b) {
return (a < b)? a : b;
}
static unsigned int GetChannelForPixel(
const ImageLoader *loader,
unsigned int x, unsigned int y,
int ch
) {
unsigned int prec;
const unsigned char *data;
switch(ch) {
case 0:
prec = loader->GetRedChannelPrecision();
data = loader->GetRedPixelData();
break;
case 1:
prec = loader->GetGreenChannelPrecision();
data = loader->GetGreenPixelData();
break;
case 2:
prec = loader->GetBlueChannelPrecision();
data = loader->GetBluePixelData();
break;
case 3:
prec = loader->GetAlphaChannelPrecision();
data = loader->GetAlphaPixelData();
break;
default:
ReportError("Unspecified channel");
return INT_MAX;
}
if(0 == prec)
return 0;
assert(x < loader->GetWidth());
assert(y < loader->GetHeight());
int pixelIdx = y * loader->GetWidth() + x;
const unsigned int val = data[pixelIdx];
if(prec < 8) {
unsigned int ret = 0;
for(unsigned int precLeft = 8; precLeft > 0; precLeft -= min(prec, abs(prec - precLeft))) {
if(prec > precLeft) {
const int toShift = prec - precLeft;
ret = ret << precLeft;
ret |= val >> toShift;
}
else {
ret = ret << prec;
ret |= val;
}
}
return ret;
}
else if(prec > 8) {
const int toShift = prec - 8;
return val >> toShift;
}
return val;
}
//////////////////////////////////////////////////////////////////////////////////////////
//
// ImageFile implementation
//
//////////////////////////////////////////////////////////////////////////////////////////
ImageFile::ImageFile(const char *filename) :
m_PixelData(0),
m_FileFormat( DetectFileFormat(filename) )
ImageFile::ImageFile(const char *filename)
: m_FileFormat( DetectFileFormat(filename) )
, m_Image(NULL)
{
unsigned char *rawData = ReadFileData(filename);
if(rawData) {
LoadImage(rawData);
m_Image = LoadImage(rawData);
delete [] rawData;
}
}
ImageFile::ImageFile(const char *filename, EImageFileFormat format) :
m_FileFormat(format),
m_PixelData(0)
ImageFile::ImageFile(const char *filename, EImageFileFormat format)
: m_FileFormat(format)
, m_Image(NULL)
{
unsigned char *rawData = ReadFileData(filename);
if(rawData) {
LoadImage(rawData);
m_Image = LoadImage(rawData);
delete [] rawData;
}
}
ImageFile::~ImageFile() {
if(m_PixelData) {
delete [] m_PixelData;
ImageFile::~ImageFile() {
if(m_Image) {
delete m_Image;
m_Image = NULL;
}
}
Image *ImageFile::LoadImage(const unsigned char *rawImageData) const {
ImageLoader *loader = NULL;
switch(m_FileFormat) {
#ifdef PNG_FOUND
case eFileFormat_PNG:
{
loader = new ImageLoaderPNG(rawImageData);
}
break;
#endif // PNG_FOUND
default:
fprintf(stderr, "Unable to load image: unknown file format.\n");
return NULL;
}
if(!loader)
return NULL;
if(!(loader->LoadImage())) {
fprintf(stderr, "Unable to load image!\n");
delete loader;
return NULL;
}
Image *i = new Image(*loader);
delete loader;
return i;
}
EImageFileFormat ImageFile::DetectFileFormat(const char *filename) {
int len = strlen(filename);
@ -164,106 +130,6 @@ EImageFileFormat ImageFile::DetectFileFormat(const char *filename) {
return kNumImageFileFormats;
}
bool ImageFile::LoadImage(const unsigned char *rawImageData) {
ImageLoader *loader = NULL;
switch(m_FileFormat) {
#ifdef PNG_FOUND
case eFileFormat_PNG:
{
loader = new ImageLoaderPNG(rawImageData);
}
break;
#endif // PNG_FOUND
default:
fprintf(stderr, "Unable to load image: unknown file format.\n");
break;
}
// Read the image data!
if(!loader->ReadData())
return false;
m_Width = loader->GetWidth();
m_Height = loader->GetHeight();
// Create RGBA buffer
const unsigned int dataSz = 4 * m_Width * m_Height;
m_PixelData = new unsigned char[dataSz];
// Populate buffer in block stream order... make
// sure that width and height are aligned to multiples of four.
const unsigned int aw = ((m_Width + 3) >> 2) << 2;
const unsigned int ah = ((m_Height + 3) >> 2) << 2;
#ifndef NDEBUG
if(aw != m_Width || ah != m_Height)
fprintf(stderr, "Warning: Image dimension not multiple of four. Space will be filled with black.\n");
#endif
int byteIdx = 0;
for(int i = 0; i < ah; i+=4) {
for(int j = 0; j < aw; j+= 4) {
// For each block, visit the pixels in sequential order
for(int y = i; y < i+4; y++) {
for(int x = j; x < j+4; x++) {
if(y >= m_Height || x >= m_Width) {
m_PixelData[byteIdx++] = 0; // r
m_PixelData[byteIdx++] = 0; // g
m_PixelData[byteIdx++] = 0; // b
m_PixelData[byteIdx++] = 0; // a
continue;
}
unsigned int redVal = GetChannelForPixel(loader, x, y, 0);
if(redVal == INT_MAX)
return false;
unsigned int greenVal = redVal;
unsigned int blueVal = redVal;
if(loader->GetGreenChannelPrecision() > 0) {
greenVal = GetChannelForPixel(loader, x, y, 1);
if(greenVal == INT_MAX)
return false;
}
if(loader->GetBlueChannelPrecision() > 0) {
blueVal = GetChannelForPixel(loader, x, y, 2);
if(blueVal == INT_MAX)
return false;
}
unsigned int alphaVal = 0xFF;
if(loader->GetAlphaChannelPrecision() > 0) {
alphaVal = GetChannelForPixel(loader, x, y, 3);
if(alphaVal == INT_MAX)
return false;
}
// Red channel
m_PixelData[byteIdx++] = redVal & 0xFF;
// Green channel
m_PixelData[byteIdx++] = greenVal & 0xFF;
// Blue channel
m_PixelData[byteIdx++] = blueVal & 0xFF;
// Alpha channel
m_PixelData[byteIdx++] = alphaVal & 0xFF;
}
}
}
}
return true;
}
#ifdef _MSC_VER
unsigned char *ImageFile::ReadFileData(const char *filename) {
//!FIXME! - Actually, implement me
@ -304,25 +170,3 @@ unsigned char *ImageFile::ReadFileData(const char *filename) {
return rawData;
}
#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;
}

174
IO/src/ImageLoader.cpp Normal file
View File

@ -0,0 +1,174 @@
#include "ImageLoader.h"
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
#include <assert.h>
template <typename T>
static inline T min(const T &a, const T &b) {
return (a > b)? b : a;
}
template <typename T>
static inline T abs(const T &a) {
return (a > 0)? a : -a;
}
void ReportError(const char *str) {
fprintf(stderr, "ImageLoader.cpp -- ERROR: %s\n", str);
}
unsigned int ImageLoader::GetChannelForPixel(uint32 x, uint32 y, uint32 ch) {
uint32 prec;
const uint8 *data;
switch(ch) {
case 0:
prec = GetRedChannelPrecision();
data = GetRedPixelData();
break;
case 1:
prec = GetGreenChannelPrecision();
data = GetGreenPixelData();
break;
case 2:
prec = GetBlueChannelPrecision();
data = GetBluePixelData();
break;
case 3:
prec = GetAlphaChannelPrecision();
data = GetAlphaPixelData();
break;
default:
ReportError("Unspecified channel");
return INT_MAX;
}
if(0 == prec)
return 0;
assert(x < GetWidth());
assert(y < GetHeight());
uint32 pixelIdx = y * GetWidth() + x;
const uint32 val = data[pixelIdx];
if(prec < 8) {
uint32 ret = 0;
for(uint32 precLeft = 8; precLeft > 0; precLeft -= min(prec, abs(prec - precLeft))) {
if(prec > precLeft) {
const int toShift = prec - precLeft;
ret = ret << precLeft;
ret |= val >> toShift;
}
else {
ret = ret << prec;
ret |= val;
}
}
return ret;
}
else if(prec > 8) {
const int toShift = prec - 8;
return val >> toShift;
}
return val;
}
bool ImageLoader::LoadImage() {
// Do we already have pixel data?
if(m_PixelData)
return true;
// Read the image data!
if(!ReadData())
return false;
m_Width = GetWidth();
m_Height = GetHeight();
// Create RGBA buffer
const unsigned int dataSz = 4 * m_Width * m_Height;
m_PixelData = new unsigned char[dataSz];
// Populate buffer in block stream order... make
// sure that width and height are aligned to multiples of four.
const unsigned int aw = ((m_Width + 3) >> 2) << 2;
const unsigned int ah = ((m_Height + 3) >> 2) << 2;
#ifndef NDEBUG
if(aw != m_Width || ah != m_Height)
fprintf(stderr, "Warning: Image dimension not multiple of four. Space will be filled with black.\n");
#endif
int byteIdx = 0;
for(int i = 0; i < ah; i+=4) {
for(int j = 0; j < aw; j+= 4) {
// For each block, visit the pixels in sequential order
for(int y = i; y < i+4; y++) {
for(int x = j; x < j+4; x++) {
if(y >= m_Height || x >= m_Width) {
m_PixelData[byteIdx++] = 0; // r
m_PixelData[byteIdx++] = 0; // g
m_PixelData[byteIdx++] = 0; // b
m_PixelData[byteIdx++] = 0; // a
continue;
}
unsigned int redVal = GetChannelForPixel(x, y, 0);
if(redVal == INT_MAX)
return false;
unsigned int greenVal = redVal;
unsigned int blueVal = redVal;
if(GetGreenChannelPrecision() > 0) {
greenVal = GetChannelForPixel(x, y, 1);
if(greenVal == INT_MAX)
return false;
}
if(GetBlueChannelPrecision() > 0) {
blueVal = GetChannelForPixel(x, y, 2);
if(blueVal == INT_MAX)
return false;
}
unsigned int alphaVal = 0xFF;
if(GetAlphaChannelPrecision() > 0) {
alphaVal = GetChannelForPixel(x, y, 3);
if(alphaVal == INT_MAX)
return false;
}
// Red channel
m_PixelData[byteIdx++] = redVal & 0xFF;
// Green channel
m_PixelData[byteIdx++] = greenVal & 0xFF;
// Blue channel
m_PixelData[byteIdx++] = blueVal & 0xFF;
// Alpha channel
m_PixelData[byteIdx++] = alphaVal & 0xFF;
}
}
}
}
return true;
}