mirror of
https://github.com/yuzu-emu/FasTC.git
synced 2024-11-28 03:24:16 +01:00
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:
parent
c1222d75f9
commit
2c22889533
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include "TexComp.h"
|
#include "TexComp.h"
|
||||||
#include "ImageFile.h"
|
#include "ImageFile.h"
|
||||||
|
#include "Image.h"
|
||||||
|
|
||||||
void PrintUsage() {
|
void PrintUsage() {
|
||||||
fprintf(stderr, "Usage: tc [-s|-t <num>] <imagefile>\n");
|
fprintf(stderr, "Usage: tc [-s|-t <num>] <imagefile>\n");
|
||||||
@ -76,6 +77,11 @@ int main(int argc, char **argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ImageFile file (argv[fileArg]);
|
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;
|
SCompressionSettings settings;
|
||||||
settings.bUseSIMD = bUseSIMD;
|
settings.bUseSIMD = bUseSIMD;
|
||||||
@ -83,13 +89,13 @@ int main(int argc, char **argv) {
|
|||||||
settings.iQuality = quality;
|
settings.iQuality = quality;
|
||||||
settings.iNumCompressions = numCompressions;
|
settings.iNumCompressions = numCompressions;
|
||||||
|
|
||||||
CompressedImage *ci = file.Compress(settings);
|
CompressedImage *ci = img->Compress(settings);
|
||||||
if(NULL == ci) {
|
if(NULL == ci) {
|
||||||
fprintf(stderr, "Error compressing image!\n");
|
fprintf(stderr, "Error compressing image!\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
double PSNR = ComputePSNR(*ci, file);
|
double PSNR = img->ComputePSNR(*ci);
|
||||||
if(PSNR > 0.0) {
|
if(PSNR > 0.0) {
|
||||||
fprintf(stdout, "PSNR: %.3f\n", PSNR);
|
fprintf(stdout, "PSNR: %.3f\n", PSNR);
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,14 @@
|
|||||||
SET( SOURCES
|
SET( SOURCES
|
||||||
"src/TexComp.cpp"
|
"src/TexComp.cpp"
|
||||||
"src/CompressedImage.cpp"
|
"src/CompressedImage.cpp"
|
||||||
|
"src/Image.cpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
SET( HEADERS
|
SET( HEADERS
|
||||||
"include/TexComp.h"
|
"include/TexComp.h"
|
||||||
"include/CompressedImage.h"
|
"include/CompressedImage.h"
|
||||||
"include/TexCompTypes.h"
|
"include/TexCompTypes.h"
|
||||||
|
"include/Image.h"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Make sure to add the appropriate stopwatch files...
|
# Make sure to add the appropriate stopwatch files...
|
||||||
|
30
Core/include/Image.h
Normal file
30
Core/include/Image.h
Normal 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
94
Core/src/Image.cpp
Normal 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;
|
||||||
|
}
|
@ -1,13 +1,15 @@
|
|||||||
#include "BC7Compressor.h"
|
|
||||||
#include "TexComp.h"
|
#include "TexComp.h"
|
||||||
#include "ThreadGroup.h"
|
|
||||||
#include "ImageFile.h"
|
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "BC7Compressor.h"
|
||||||
|
#include "ThreadGroup.h"
|
||||||
|
#include "ImageFile.h"
|
||||||
|
#include "Image.h"
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static T min(const T &a, const T &b) {
|
static T min(const T &a, const T &b) {
|
||||||
return (a < b)? a : b;
|
return (a < b)? a : b;
|
||||||
@ -192,51 +194,3 @@ bool CompressImageData(
|
|||||||
|
|
||||||
return true;
|
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;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
SET( SOURCES
|
SET( SOURCES
|
||||||
"src/ImageFile.cpp"
|
"src/ImageFile.cpp"
|
||||||
|
"src/ImageLoader.cpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
SET( HEADERS
|
SET( HEADERS
|
||||||
|
@ -11,21 +11,24 @@ class ImageLoader {
|
|||||||
uint32 m_Width;
|
uint32 m_Width;
|
||||||
uint32 m_Height;
|
uint32 m_Height;
|
||||||
|
|
||||||
unsigned int m_RedChannelPrecision;
|
uint32 m_RedChannelPrecision;
|
||||||
unsigned char *m_RedData;
|
uint8 *m_RedData;
|
||||||
|
|
||||||
unsigned int m_GreenChannelPrecision;
|
uint32 m_GreenChannelPrecision;
|
||||||
unsigned char *m_GreenData;
|
uint8 *m_GreenData;
|
||||||
|
|
||||||
unsigned int m_BlueChannelPrecision;
|
uint32 m_BlueChannelPrecision;
|
||||||
unsigned char *m_BlueData;
|
uint8 *m_BlueData;
|
||||||
|
|
||||||
unsigned int m_AlphaChannelPrecision;
|
uint32 m_AlphaChannelPrecision;
|
||||||
unsigned char *m_AlphaData;
|
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_Width(0), m_Height(0)
|
||||||
, m_RedChannelPrecision(0), m_RedData(0)
|
, m_RedChannelPrecision(0), m_RedData(0)
|
||||||
, m_GreenChannelPrecision(0), m_GreenData(0)
|
, m_GreenChannelPrecision(0), m_GreenData(0)
|
||||||
@ -33,6 +36,9 @@ class ImageLoader {
|
|||||||
, m_AlphaChannelPrecision(0), m_AlphaData(0)
|
, m_AlphaChannelPrecision(0), m_AlphaData(0)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
uint32 GetChannelForPixel(uint32 x, uint32 y, uint32 ch);
|
||||||
|
|
||||||
|
public:
|
||||||
virtual ~ImageLoader() {
|
virtual ~ImageLoader() {
|
||||||
if(m_RedData) {
|
if(m_RedData) {
|
||||||
delete [] m_RedData;
|
delete [] m_RedData;
|
||||||
@ -55,23 +61,26 @@ class ImageLoader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
|
||||||
virtual bool ReadData() = 0;
|
virtual bool ReadData() = 0;
|
||||||
|
|
||||||
int GetRedChannelPrecision() const { return m_RedChannelPrecision; }
|
uint32 GetRedChannelPrecision() const { return m_RedChannelPrecision; }
|
||||||
const unsigned char * GetRedPixelData() const { return m_RedData; }
|
const uint8 * GetRedPixelData() const { return m_RedData; }
|
||||||
|
|
||||||
int GetGreenChannelPrecision() const { return m_GreenChannelPrecision; }
|
uint32 GetGreenChannelPrecision() const { return m_GreenChannelPrecision; }
|
||||||
const unsigned char * GetGreenPixelData() const { return m_GreenData; }
|
const uint8 * GetGreenPixelData() const { return m_GreenData; }
|
||||||
|
|
||||||
int GetBlueChannelPrecision() const { return m_BlueChannelPrecision; }
|
uint32 GetBlueChannelPrecision() const { return m_BlueChannelPrecision; }
|
||||||
const unsigned char * GetBluePixelData() const { return m_BlueData; }
|
const uint8 * GetBluePixelData() const { return m_BlueData; }
|
||||||
|
|
||||||
int GetAlphaChannelPrecision() const { return m_AlphaChannelPrecision; }
|
uint32 GetAlphaChannelPrecision() const { return m_AlphaChannelPrecision; }
|
||||||
const unsigned char * GetAlphaPixelData() const { return m_AlphaData; }
|
const uint8 * GetAlphaPixelData() const { return m_AlphaData; }
|
||||||
|
|
||||||
int GetWidth() const { return m_Width; }
|
uint32 GetWidth() const { return m_Width; }
|
||||||
int GetHeight() const { return m_Height; }
|
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
|
#cmakedefine PNG_FOUND
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include "ImageFileFormat.h"
|
#include "ImageFileFormat.h"
|
||||||
|
|
||||||
// Forward declare
|
// Forward declare
|
||||||
|
class Image;
|
||||||
class CompressedImage;
|
class CompressedImage;
|
||||||
|
|
||||||
// Class definition
|
// Class definition
|
||||||
@ -18,19 +19,20 @@ 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; }
|
||||||
CompressedImage *Compress(const SCompressionSettings &) const;
|
CompressedImage *Compress(const SCompressionSettings &) const;
|
||||||
|
Image *GetImage() const { return m_Image; }
|
||||||
const unsigned char *RawData() const { return m_PixelData; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
unsigned int m_Handle;
|
unsigned int m_Handle;
|
||||||
unsigned int m_Width;
|
unsigned int m_Width;
|
||||||
unsigned int m_Height;
|
unsigned int m_Height;
|
||||||
unsigned char *m_PixelData;
|
|
||||||
|
Image *m_Image;
|
||||||
|
|
||||||
const EImageFileFormat m_FileFormat;
|
const EImageFileFormat m_FileFormat;
|
||||||
|
|
||||||
static unsigned char *ReadFileData(const char *filename);
|
static unsigned char *ReadFileData(const char *filename);
|
||||||
static EImageFileFormat DetectFileFormat(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_
|
#endif // _IMAGE_FILE_H_
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include "ImageFile.h"
|
#include "ImageFile.h"
|
||||||
#include "ImageLoader.h"
|
#include "ImageLoader.h"
|
||||||
#include "CompressedImage.h"
|
#include "CompressedImage.h"
|
||||||
|
#include "Image.h"
|
||||||
|
|
||||||
#ifdef PNG_FOUND
|
#ifdef PNG_FOUND
|
||||||
# include "ImageLoaderPNG.h"
|
# include "ImageLoaderPNG.h"
|
||||||
@ -33,109 +34,74 @@ static inline T min(const T &a, const T &b) {
|
|||||||
return (a < b)? a : 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 implementation
|
||||||
//
|
//
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
ImageFile::ImageFile(const char *filename) :
|
ImageFile::ImageFile(const char *filename)
|
||||||
m_PixelData(0),
|
: m_FileFormat( DetectFileFormat(filename) )
|
||||||
m_FileFormat( DetectFileFormat(filename) )
|
, m_Image(NULL)
|
||||||
{
|
{
|
||||||
unsigned char *rawData = ReadFileData(filename);
|
unsigned char *rawData = ReadFileData(filename);
|
||||||
if(rawData) {
|
if(rawData) {
|
||||||
LoadImage(rawData);
|
m_Image = LoadImage(rawData);
|
||||||
delete [] rawData;
|
delete [] rawData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageFile::ImageFile(const char *filename, EImageFileFormat format) :
|
ImageFile::ImageFile(const char *filename, EImageFileFormat format)
|
||||||
m_FileFormat(format),
|
: m_FileFormat(format)
|
||||||
m_PixelData(0)
|
, m_Image(NULL)
|
||||||
{
|
{
|
||||||
unsigned char *rawData = ReadFileData(filename);
|
unsigned char *rawData = ReadFileData(filename);
|
||||||
if(rawData) {
|
if(rawData) {
|
||||||
LoadImage(rawData);
|
m_Image = LoadImage(rawData);
|
||||||
delete [] rawData;
|
delete [] rawData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageFile::~ImageFile() {
|
ImageFile::~ImageFile() {
|
||||||
if(m_PixelData) {
|
if(m_Image) {
|
||||||
delete [] m_PixelData;
|
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) {
|
EImageFileFormat ImageFile::DetectFileFormat(const char *filename) {
|
||||||
|
|
||||||
int len = strlen(filename);
|
int len = strlen(filename);
|
||||||
@ -164,106 +130,6 @@ EImageFileFormat ImageFile::DetectFileFormat(const char *filename) {
|
|||||||
return kNumImageFileFormats;
|
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
|
#ifdef _MSC_VER
|
||||||
unsigned char *ImageFile::ReadFileData(const char *filename) {
|
unsigned char *ImageFile::ReadFileData(const char *filename) {
|
||||||
//!FIXME! - Actually, implement me
|
//!FIXME! - Actually, implement me
|
||||||
@ -304,25 +170,3 @@ 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
174
IO/src/ImageLoader.cpp
Normal file
174
IO/src/ImageLoader.cpp
Normal 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;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user