Initial source commit
This commit is contained in:
commit
f1384c11ee
335 changed files with 52715 additions and 0 deletions
251
minorGems/graphics/converters/BMPImageConverter.h
Normal file
251
minorGems/graphics/converters/BMPImageConverter.h
Normal file
|
@ -0,0 +1,251 @@
|
|||
/*
|
||||
* Modification History
|
||||
*
|
||||
* 2001-February-19 Jason Rohrer
|
||||
* Created.
|
||||
*
|
||||
* 2001-February-19 Jason Rohrer
|
||||
* Fixed some bugs in the raster formatting code.
|
||||
*
|
||||
* 2001-May-19 Jason Rohrer
|
||||
* Fixed a bug in zero padding for each line.
|
||||
*
|
||||
* 2001-May-21 Jason Rohrer
|
||||
* Fixed another bug in zero padding for each line.
|
||||
*
|
||||
* 2001-September-22 Jason Rohrer
|
||||
* Changed to subclass LittleEndianImageConverter.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef BMP_IMAGE_CONVERTER_INCLUDED
|
||||
#define BMP_IMAGE_CONVERTER_INCLUDED
|
||||
|
||||
|
||||
#include "LittleEndianImageConverter.h"
|
||||
|
||||
|
||||
/**
|
||||
* Windows BMP implementation of the image conversion interface.
|
||||
*
|
||||
* Note that it only supports 24-bit BMP files (and thus only 3-channel
|
||||
* Images).
|
||||
*
|
||||
* BMP format information taken from:
|
||||
* http://www.daubnet.com/formats/BMP.html
|
||||
* Some information from this site is incorrect:
|
||||
* 24-bit pixels are _not_ stored in 32-bit blocks.
|
||||
*
|
||||
* @author Jason Rohrer
|
||||
*/
|
||||
class BMPImageConverter : public LittleEndianImageConverter {
|
||||
|
||||
public:
|
||||
|
||||
// implement the ImageConverter interface
|
||||
virtual void formatImage( Image *inImage,
|
||||
OutputStream *inStream );
|
||||
|
||||
virtual Image *deformatImage( InputStream *inStream );
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
inline void BMPImageConverter::formatImage( Image *inImage,
|
||||
OutputStream *inStream ) {
|
||||
|
||||
// make sure the image is in the right format
|
||||
if( inImage->getNumChannels() != 3 ) {
|
||||
printf(
|
||||
"Only 3-channel images can be converted to the BMP format.\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
long width = inImage->getWidth();
|
||||
long height = inImage->getHeight();
|
||||
|
||||
long numPixels = width * height;
|
||||
|
||||
// each line should be padded with zeros to
|
||||
// end on a 4-byte boundary
|
||||
int numZeroPaddingBytes = ( 4 - ( width * 3 ) % 4 ) % 4;
|
||||
|
||||
short bitCount = 24;
|
||||
long rasterSize = numPixels * 3;
|
||||
// zero padding bytes for each row
|
||||
rasterSize += numZeroPaddingBytes * height;
|
||||
|
||||
// offset past two headers
|
||||
long offsetToRaster = 14 + 40;
|
||||
|
||||
long fileSize = offsetToRaster + rasterSize;
|
||||
|
||||
long compressionType = 0;
|
||||
|
||||
|
||||
long pixelsPerMeter = 2834;
|
||||
|
||||
// both are 0 since we have no color map
|
||||
long colorsUsed = 0;
|
||||
long colorsImportant = 0;
|
||||
|
||||
// write the header
|
||||
unsigned char *signature = new unsigned char[2];
|
||||
signature[0] = 'B';
|
||||
signature[1] = 'M';
|
||||
inStream->write( signature, 2 );
|
||||
delete [] signature;
|
||||
writeLittleEndianLong( fileSize, inStream );
|
||||
writeLittleEndianLong( 0, inStream );
|
||||
writeLittleEndianLong( offsetToRaster, inStream );
|
||||
|
||||
// write the info header
|
||||
// header size
|
||||
writeLittleEndianLong( 40, inStream );
|
||||
writeLittleEndianLong( width, inStream );
|
||||
writeLittleEndianLong( height, inStream );
|
||||
// numPlanes
|
||||
writeLittleEndianShort( 1, inStream );
|
||||
writeLittleEndianShort( bitCount, inStream );
|
||||
writeLittleEndianLong( compressionType, inStream );
|
||||
writeLittleEndianLong( rasterSize, inStream );
|
||||
writeLittleEndianLong( pixelsPerMeter, inStream );
|
||||
writeLittleEndianLong( pixelsPerMeter, inStream );
|
||||
writeLittleEndianLong( colorsUsed, inStream );
|
||||
writeLittleEndianLong( colorsImportant, inStream );
|
||||
|
||||
// no color table...
|
||||
|
||||
// now write the raster
|
||||
|
||||
unsigned char *raster = new unsigned char[ rasterSize ];
|
||||
double *red = inImage->getChannel( 0 );
|
||||
double *green = inImage->getChannel( 1 );
|
||||
double *blue = inImage->getChannel( 2 );
|
||||
|
||||
// pixels are stored bottom-up, left to right
|
||||
// (row major order)
|
||||
|
||||
long rasterIndex = 0;
|
||||
for( int y=height-1; y>=0; y-- ) {
|
||||
for( int x=0; x<width; x++ ) {
|
||||
int imageIndex = y * width + x;
|
||||
|
||||
raster[rasterIndex] =
|
||||
(unsigned char)( 255 * blue[imageIndex] );
|
||||
raster[rasterIndex + 1] =
|
||||
(unsigned char)( 255 * green[imageIndex] );
|
||||
raster[rasterIndex + 2] =
|
||||
(unsigned char)( 255 * red[imageIndex] );
|
||||
|
||||
rasterIndex += 3;
|
||||
}
|
||||
|
||||
for( int p=0; p<numZeroPaddingBytes; p++ ) {
|
||||
// insert at the end of this line
|
||||
raster[ rasterIndex ] = 0;
|
||||
rasterIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
inStream->write( raster, rasterSize );
|
||||
|
||||
delete [] raster;
|
||||
}
|
||||
|
||||
|
||||
|
||||
inline Image *BMPImageConverter::deformatImage( InputStream *inStream ) {
|
||||
// temp buffer used to skip data in the stream
|
||||
unsigned char *temp = new unsigned char[ 100 ];
|
||||
|
||||
// skip signature
|
||||
inStream->read( temp, 2 );
|
||||
|
||||
long fileSize = readLittleEndianLong( inStream );
|
||||
|
||||
// skip unused
|
||||
inStream->read( temp, 4 );
|
||||
|
||||
long rasterOffset = readLittleEndianLong( inStream );
|
||||
long rasterSize = fileSize - rasterOffset;
|
||||
|
||||
// skip size of header
|
||||
inStream->read( temp, 4 );
|
||||
|
||||
long width = readLittleEndianLong( inStream );
|
||||
long height = readLittleEndianLong( inStream );
|
||||
|
||||
// skip planes
|
||||
inStream->read( temp, 2 );
|
||||
|
||||
short bitCount = readLittleEndianShort( inStream );
|
||||
|
||||
char failing = false;
|
||||
if( bitCount != 24 ) {
|
||||
printf( "Only 24-bit BMP file formats supported.\n" );
|
||||
failing = true;
|
||||
}
|
||||
long compression = readLittleEndianLong( inStream );
|
||||
if( compression != 0 ) {
|
||||
printf( "Only uncompressed BMP file formats supported.\n" );
|
||||
failing = true;
|
||||
}
|
||||
|
||||
// skip imageSize, resolution, and color usage information
|
||||
inStream->read( temp, 20 );
|
||||
|
||||
// now we're at the raster.
|
||||
|
||||
// each line should be padded with zeros to
|
||||
// end on a 4-byte boundary
|
||||
int numZeroPaddingBytes = ( 4 - ( width * 3 ) % 4 ) % 4;
|
||||
|
||||
unsigned char *raster = new unsigned char[ rasterSize ];
|
||||
inStream->read( raster, rasterSize );
|
||||
Image *returnImage;
|
||||
if( failing ) {
|
||||
return NULL;
|
||||
}
|
||||
else {
|
||||
|
||||
returnImage = new Image( width, height, 3 );
|
||||
|
||||
double *red = returnImage->getChannel( 0 );
|
||||
double *green = returnImage->getChannel( 1 );
|
||||
double *blue = returnImage->getChannel( 2 );
|
||||
|
||||
// pixels are stored bottom-up, left to right
|
||||
// (row major order)
|
||||
|
||||
long rasterIndex = 0;
|
||||
for( int y=height-1; y>=0; y-- ) {
|
||||
for( int x=0; x<width; x++ ) {
|
||||
int imageIndex = y * width + x;
|
||||
|
||||
blue[imageIndex] =
|
||||
(double)( raster[rasterIndex] ) / 255.0;
|
||||
green[imageIndex] =
|
||||
(double)( raster[rasterIndex + 1] ) / 255.0;
|
||||
red[imageIndex] =
|
||||
(double)( raster[rasterIndex + 2] ) / 255.0;
|
||||
|
||||
rasterIndex += 3;
|
||||
}
|
||||
|
||||
// skip the zero padding bytes at the end of this line
|
||||
rasterIndex += numZeroPaddingBytes;
|
||||
}
|
||||
}
|
||||
|
||||
delete [] raster;
|
||||
delete [] temp;
|
||||
|
||||
return returnImage;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
142
minorGems/graphics/converters/BigEndianImageConverter.h
Normal file
142
minorGems/graphics/converters/BigEndianImageConverter.h
Normal file
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* Modification History
|
||||
*
|
||||
* 2006-November-21 Jason Rohrer
|
||||
* Created. Copied from LittleEndian version.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef BIG_ENDIAN_IMAGE_CONVERTER_INCLUDED
|
||||
#define BIG_ENDIAN_IMAGE_CONVERTER_INCLUDED
|
||||
|
||||
|
||||
#include "minorGems/graphics/ImageConverter.h"
|
||||
|
||||
|
||||
/**
|
||||
* A base class for converters that have big endian file formats.
|
||||
* Basically includes big endian reading and writing functions.
|
||||
*
|
||||
* @author Jason Rohrer
|
||||
*/
|
||||
class BigEndianImageConverter : public ImageConverter {
|
||||
|
||||
public:
|
||||
|
||||
// does not implement the ImageConverter interface,
|
||||
// which makes this class abstract.
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* Writes a long value in big endian format.
|
||||
*
|
||||
* @param inLong the long value to write.
|
||||
* @param inStream the stream to write inLong to.
|
||||
*/
|
||||
void writeBigEndianLong( long inLong,
|
||||
OutputStream *inStream );
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Writes a short value in big endian format.
|
||||
*
|
||||
* @param inShort the short value to write.
|
||||
* @param inStream the stream to write inShort to.
|
||||
*/
|
||||
void writeBigEndianShort( short inShort,
|
||||
OutputStream *inStream );
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Reads a long value in big endian format.
|
||||
*
|
||||
* @param inStream the stream to read the long value from.
|
||||
*
|
||||
* @return the long value.
|
||||
*/
|
||||
long readBigEndianLong( InputStream *inStream );
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Reads a short value in big endian format.
|
||||
*
|
||||
* @param inStream the stream to read the short value from.
|
||||
*
|
||||
* @return the short value.
|
||||
*/
|
||||
short readBigEndianShort( InputStream *inStream );
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
inline void BigEndianImageConverter::writeBigEndianLong( long inLong,
|
||||
OutputStream *inStream ) {
|
||||
|
||||
unsigned char buffer[4];
|
||||
|
||||
buffer[0] = (unsigned char)( ( inLong >> 24 ) & 0xFF );
|
||||
buffer[1] = (unsigned char)( ( inLong >> 16 ) & 0xFF );
|
||||
buffer[2] = (unsigned char)( ( inLong >> 8 ) & 0xFF );
|
||||
buffer[3] = (unsigned char)( inLong & 0xFF );
|
||||
|
||||
inStream->write( buffer, 4 );
|
||||
}
|
||||
|
||||
|
||||
|
||||
inline long BigEndianImageConverter::readBigEndianLong(
|
||||
InputStream *inStream ) {
|
||||
|
||||
unsigned char buffer[4];
|
||||
|
||||
inStream->read( buffer, 4 );
|
||||
|
||||
long outLong =
|
||||
( buffer[0] << 24 ) |
|
||||
( buffer[1] << 16 ) |
|
||||
( buffer[2] << 8 ) |
|
||||
buffer[3];
|
||||
|
||||
return outLong;
|
||||
}
|
||||
|
||||
|
||||
|
||||
inline void BigEndianImageConverter::writeBigEndianShort( short inShort,
|
||||
OutputStream *inStream ) {
|
||||
|
||||
unsigned char buffer[2];
|
||||
|
||||
buffer[0] = (unsigned char)( ( inShort >> 8 ) & 0xFF );
|
||||
buffer[1] = (unsigned char)( inShort & 0xFF );
|
||||
|
||||
inStream->write( buffer, 2 );
|
||||
}
|
||||
|
||||
|
||||
|
||||
inline short BigEndianImageConverter::readBigEndianShort(
|
||||
InputStream *inStream ) {
|
||||
|
||||
unsigned char buffer[2];
|
||||
|
||||
inStream->read( buffer, 2 );
|
||||
|
||||
long outShort =
|
||||
( buffer[0] << 8 ) |
|
||||
buffer[1];
|
||||
|
||||
return outShort;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
95
minorGems/graphics/converters/JPEGImageConverter.h
Normal file
95
minorGems/graphics/converters/JPEGImageConverter.h
Normal file
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Modification History
|
||||
*
|
||||
* 2001-April-27 Jason Rohrer
|
||||
* Created.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef JPEG_IMAGE_CONVERTER_INCLUDED
|
||||
#define JPEG_IMAGE_CONVERTER_INCLUDED
|
||||
|
||||
|
||||
#include "minorGems/graphics/ImageConverter.h"
|
||||
|
||||
|
||||
/**
|
||||
* JPEG implementation of the image conversion interface.
|
||||
*
|
||||
* Implementations are platform dependent.
|
||||
*
|
||||
* @author Jason Rohrer
|
||||
*/
|
||||
class JPEGImageConverter : public ImageConverter {
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructs a JPEGImageConverter.
|
||||
*
|
||||
* @param inQuality a quality value in [0,100] for compression.
|
||||
* 100 specifies highest quality.
|
||||
*/
|
||||
JPEGImageConverter( int inQuality );
|
||||
|
||||
|
||||
/**
|
||||
* Sets the compression quality.
|
||||
*
|
||||
* @param inQuality a quality value in [0,100]. 100
|
||||
* specifies highest quality.
|
||||
*/
|
||||
void setQuality( int inQuality );
|
||||
|
||||
|
||||
/**
|
||||
* Gets the compression quality.
|
||||
*
|
||||
* @return a quality value in [0,100]. 100
|
||||
* indicates highest quality.
|
||||
*/
|
||||
int getQuality();
|
||||
|
||||
|
||||
// implement the ImageConverter interface
|
||||
virtual void formatImage( Image *inImage,
|
||||
OutputStream *inStream );
|
||||
|
||||
virtual Image *deformatImage( InputStream *inStream );
|
||||
|
||||
|
||||
private:
|
||||
int mQuality;
|
||||
};
|
||||
|
||||
|
||||
|
||||
inline JPEGImageConverter::JPEGImageConverter( int inQuality )
|
||||
: mQuality( inQuality ) {
|
||||
|
||||
if( mQuality > 100 || mQuality < 0 ) {
|
||||
printf( "JPEG quality must be in [0,100]\n" );
|
||||
mQuality = 50;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
inline void JPEGImageConverter::setQuality( int inQuality ) {
|
||||
mQuality = inQuality;
|
||||
|
||||
if( mQuality > 100 || mQuality < 0 ) {
|
||||
printf( "JPEG quality must be in [0,100]\n" );
|
||||
mQuality = 50;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
inline int JPEGImageConverter::getQuality() {
|
||||
return mQuality;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
137
minorGems/graphics/converters/LittleEndianImageConverter.h
Normal file
137
minorGems/graphics/converters/LittleEndianImageConverter.h
Normal file
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* Modification History
|
||||
*
|
||||
* 2001-September-22 Jason Rohrer
|
||||
* Created.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef LITTLE_ENDIAN_IMAGE_CONVERTER_INCLUDED
|
||||
#define LITTLE_ENDIAN_IMAGE_CONVERTER_INCLUDED
|
||||
|
||||
|
||||
#include "minorGems/graphics/ImageConverter.h"
|
||||
|
||||
|
||||
/**
|
||||
* A base class for converters that have little endian file formats.
|
||||
* Basically includes little endian reading and writing functions.
|
||||
*
|
||||
* @author Jason Rohrer
|
||||
*/
|
||||
class LittleEndianImageConverter : public ImageConverter {
|
||||
|
||||
public:
|
||||
|
||||
// does not implement the ImageConverter interface,
|
||||
// which makes this class abstract.
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* Writes a long value in little endian format.
|
||||
*
|
||||
* @param inLong the long value to write.
|
||||
* @param inStream the stream to write inLong to.
|
||||
*/
|
||||
void writeLittleEndianLong( long inLong,
|
||||
OutputStream *inStream );
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Writes a short value in little endian format.
|
||||
*
|
||||
* @param inShort the short value to write.
|
||||
* @param inStream the stream to write inShort to.
|
||||
*/
|
||||
void writeLittleEndianShort( short inShort,
|
||||
OutputStream *inStream );
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Reads a long value in little endian format.
|
||||
*
|
||||
* @param inStream the stream to read the long value from.
|
||||
*
|
||||
* @return the long value.
|
||||
*/
|
||||
long readLittleEndianLong( InputStream *inStream );
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Reads a short value in little endian format.
|
||||
*
|
||||
* @param inStream the stream to read the short value from.
|
||||
*
|
||||
* @return the short value.
|
||||
*/
|
||||
short readLittleEndianShort( InputStream *inStream );
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
inline void LittleEndianImageConverter::writeLittleEndianLong( long inLong,
|
||||
OutputStream *inStream ) {
|
||||
|
||||
unsigned char buffer[4];
|
||||
|
||||
buffer[0] = (unsigned char)( inLong & 0xFF );
|
||||
buffer[1] = (unsigned char)( ( inLong >> 8 ) & 0xFF );
|
||||
buffer[2] = (unsigned char)( ( inLong >> 16 ) & 0xFF );
|
||||
buffer[3] = (unsigned char)( ( inLong >> 24 ) & 0xFF );
|
||||
|
||||
inStream->write( buffer, 4 );
|
||||
}
|
||||
|
||||
|
||||
|
||||
inline long LittleEndianImageConverter::readLittleEndianLong(
|
||||
InputStream *inStream ) {
|
||||
|
||||
unsigned char buffer[4];
|
||||
|
||||
inStream->read( buffer, 4 );
|
||||
|
||||
long outLong = buffer[0] | ( buffer[1] << 8 ) | ( buffer[2] << 16 )
|
||||
| ( buffer[3] << 24 );
|
||||
|
||||
return outLong;
|
||||
}
|
||||
|
||||
|
||||
|
||||
inline void LittleEndianImageConverter::writeLittleEndianShort( short inShort,
|
||||
OutputStream *inStream ) {
|
||||
|
||||
unsigned char buffer[2];
|
||||
|
||||
buffer[0] = (unsigned char)( inShort & 0xFF );
|
||||
buffer[1] = (unsigned char)( ( inShort >> 8 ) & 0xFF );
|
||||
|
||||
inStream->write( buffer, 2 );
|
||||
}
|
||||
|
||||
|
||||
|
||||
inline short LittleEndianImageConverter::readLittleEndianShort(
|
||||
InputStream *inStream ) {
|
||||
|
||||
unsigned char buffer[2];
|
||||
|
||||
inStream->read( buffer, 2 );
|
||||
|
||||
long outShort = buffer[0] | ( buffer[1] << 8 );
|
||||
|
||||
return outShort;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
643
minorGems/graphics/converters/PNGImageConverter.cpp
Normal file
643
minorGems/graphics/converters/PNGImageConverter.cpp
Normal file
|
@ -0,0 +1,643 @@
|
|||
/*
|
||||
* Modification History
|
||||
*
|
||||
* 2006-November-21 Jason Rohrer
|
||||
* Created.
|
||||
*
|
||||
* 2010-May-18 Jason Rohrer
|
||||
* String parameters as const to fix warnings.
|
||||
*/
|
||||
|
||||
|
||||
#include "PNGImageConverter.h"
|
||||
|
||||
#include "minorGems/util/SimpleVector.h"
|
||||
#include "minorGems/graphics/RGBAImage.h"
|
||||
|
||||
//#include "lodepng.h"
|
||||
|
||||
//#include <zlib.h>
|
||||
|
||||
#include <png.h>
|
||||
|
||||
|
||||
|
||||
PNGImageConverter::PNGImageConverter( int inCompressionLevel )
|
||||
: mCompressionLevel( inCompressionLevel ) {
|
||||
|
||||
// set up the CRC table
|
||||
|
||||
// code taken from the PNG spec:
|
||||
// http://www.w3.org/TR/2003/REC-PNG-20031110/#D-CRCAppendix
|
||||
unsigned long c;
|
||||
int n, k;
|
||||
|
||||
for( n=0; n<256; n++ ) {
|
||||
c = (unsigned long)n;
|
||||
for( k=0; k<8; k++ ) {
|
||||
if( c & 1 ) {
|
||||
c = 0xedb88320L ^ (c >> 1);
|
||||
}
|
||||
else {
|
||||
c = c >> 1;
|
||||
}
|
||||
}
|
||||
mCRCTable[n] = c;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
unsigned long PNGImageConverter::updateCRC(
|
||||
unsigned long inCRC, unsigned char *inData, int inLength ) {
|
||||
|
||||
// code taken from the PNG spec:
|
||||
// http://www.w3.org/TR/2003/REC-PNG-20031110/#D-CRCAppendix
|
||||
|
||||
unsigned long c = inCRC;
|
||||
int n;
|
||||
|
||||
for( n=0; n<inLength; n++ ) {
|
||||
c = mCRCTable[ (c ^ inData[n]) & 0xff ]
|
||||
^
|
||||
(c >> 8);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define ADLER_BASE 65521 /* largest prime smaller than 65536 */
|
||||
|
||||
/**
|
||||
* Updates an adler32 checksum.
|
||||
* code found here http://www.ietf.org/rfc/rfc1950.txt
|
||||
*
|
||||
* New adlers should start with inAdler set to 1.
|
||||
*
|
||||
* @param inAdler the current state of the checksum.
|
||||
* @param inData the data to add. Destroyed by caller.
|
||||
* @param inLength the length of the data in bytes.
|
||||
*
|
||||
* @return the new checksum.
|
||||
*/
|
||||
unsigned long updateAdler32( unsigned long inAdler,
|
||||
unsigned char *inData, int inLength ) {
|
||||
unsigned long s1 = inAdler & 0xffff;
|
||||
unsigned long s2 = (inAdler >> 16) & 0xffff;
|
||||
int n;
|
||||
|
||||
for (n = 0; n < inLength; n++) {
|
||||
s1 = (s1 + inData[n]) % ADLER_BASE;
|
||||
s2 = (s2 + s1) % ADLER_BASE;
|
||||
}
|
||||
return (s2 << 16) + s1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void PNGImageConverter::writeChunk(
|
||||
const char inChunkType[4], unsigned char *inData,
|
||||
unsigned long inNumBytes, OutputStream *inStream ) {
|
||||
|
||||
// chunk layout:
|
||||
|
||||
// 4-byte length
|
||||
// 4-char type
|
||||
// data
|
||||
// 4-byte CRC (applied to type and data parts)
|
||||
|
||||
|
||||
// write the length
|
||||
writeBigEndianLong( inNumBytes, inStream );
|
||||
|
||||
inStream->write( (unsigned char *)inChunkType, 4 );
|
||||
|
||||
// start the crc
|
||||
unsigned long crc = updateCRC( mStartCRC,
|
||||
(unsigned char *)inChunkType, 4 );
|
||||
|
||||
if( inData != NULL ) {
|
||||
// chunk has data
|
||||
|
||||
inStream->write( inData, inNumBytes );
|
||||
|
||||
crc = updateCRC( crc, inData, inNumBytes );
|
||||
}
|
||||
|
||||
// final step: invert the CRC
|
||||
crc = crc ^ 0xffffffffL;
|
||||
|
||||
// now write the CRC
|
||||
writeBigEndianLong( crc, inStream );
|
||||
}
|
||||
|
||||
|
||||
|
||||
// callbacks for libpng io
|
||||
void libpngWriteCallback( png_structp png_ptr,
|
||||
png_bytep data, png_size_t length ) {
|
||||
|
||||
// unpack our extra parameter
|
||||
void *write_io_ptr = png_get_io_ptr( png_ptr );
|
||||
|
||||
OutputStream *inStream = (OutputStream *)write_io_ptr;
|
||||
|
||||
inStream->write( data, length );
|
||||
}
|
||||
|
||||
|
||||
|
||||
void libpngFlushCallback( png_structp png_ptr ) {
|
||||
// do nothing?
|
||||
}
|
||||
|
||||
|
||||
|
||||
void PNGImageConverter::formatImage( Image *inImage,
|
||||
OutputStream *inStream ) {
|
||||
|
||||
int numChannels = inImage->getNumChannels();
|
||||
|
||||
// make sure the image is in the right format
|
||||
if( numChannels != 3 &&
|
||||
numChannels != 4 ) {
|
||||
printf( "Only 3- and 4-channel images can be converted to " );
|
||||
printf( "the PNG format.\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
int w = inImage->getWidth();
|
||||
int h = inImage->getHeight();
|
||||
|
||||
//RGBAImage rgbaImage( inImage );
|
||||
|
||||
unsigned char *imageBytes = RGBAImage::getRGBABytes( inImage );
|
||||
|
||||
|
||||
|
||||
// libpng implementation
|
||||
|
||||
// adapted from this:
|
||||
// http://zarb.org/~gc/html/libpng.html
|
||||
|
||||
|
||||
// get pointers to rows
|
||||
unsigned char **rows = new unsigned char *[h];
|
||||
|
||||
for( int y=0; y<h; y++ ) {
|
||||
rows[y] = &( imageBytes[ y * (w * 4) ] );
|
||||
}
|
||||
|
||||
|
||||
png_structp png_ptr;
|
||||
png_infop info_ptr;
|
||||
|
||||
// initialize structures
|
||||
png_ptr = png_create_write_struct( PNG_LIBPNG_VER_STRING,
|
||||
NULL, NULL, NULL );
|
||||
if( !png_ptr ) {
|
||||
delete [] imageBytes;
|
||||
delete [] rows;
|
||||
printf( "PNG Writing: png_create_write_struct failed\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
png_set_compression_level( png_ptr, mCompressionLevel );
|
||||
|
||||
|
||||
info_ptr = png_create_info_struct( png_ptr );
|
||||
|
||||
if( !info_ptr ) {
|
||||
delete [] imageBytes;
|
||||
delete [] rows;
|
||||
png_destroy_write_struct( &png_ptr, NULL );
|
||||
|
||||
printf( "PNG Writing: png_create_info_struct failed\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
// weird way that libpng handles errors with a jump
|
||||
if( setjmp( png_jmpbuf( png_ptr ) ) ) {
|
||||
delete [] imageBytes;
|
||||
delete [] rows;
|
||||
png_destroy_write_struct( &png_ptr, &info_ptr );
|
||||
|
||||
printf( "PNG Writing: error when setting writing funciton\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//png_init_io(png_ptr, fp);
|
||||
|
||||
|
||||
// set our write callback (since we're not writing directly to file)
|
||||
// pass the stream as the extra void* parameter
|
||||
png_set_write_fn( png_ptr,
|
||||
(void*)inStream,
|
||||
libpngWriteCallback,
|
||||
libpngFlushCallback );
|
||||
|
||||
|
||||
// write header
|
||||
if( setjmp( png_jmpbuf( png_ptr ) ) ) {
|
||||
delete [] imageBytes;
|
||||
delete [] rows;
|
||||
png_destroy_write_struct( &png_ptr, &info_ptr );
|
||||
|
||||
printf( "PNG Writing: error writing header\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
png_set_IHDR( png_ptr, info_ptr, w, h,
|
||||
8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
|
||||
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE );
|
||||
|
||||
png_write_info( png_ptr, info_ptr );
|
||||
|
||||
|
||||
// write bytes
|
||||
// write header
|
||||
if( setjmp( png_jmpbuf( png_ptr ) ) ) {
|
||||
delete [] imageBytes;
|
||||
delete [] rows;
|
||||
png_destroy_write_struct( &png_ptr, &info_ptr );
|
||||
|
||||
printf( "PNG Writing: error writing image bytes\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
png_write_image( png_ptr, rows );
|
||||
|
||||
|
||||
// end write
|
||||
if( setjmp( png_jmpbuf( png_ptr ) ) ) {
|
||||
delete [] imageBytes;
|
||||
delete [] rows;
|
||||
png_destroy_write_struct( &png_ptr, &info_ptr );
|
||||
|
||||
printf( "PNG Writing: error ending write\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
png_write_end( png_ptr, NULL );
|
||||
|
||||
|
||||
png_destroy_write_struct( &png_ptr, &info_ptr );
|
||||
|
||||
delete [] imageBytes;
|
||||
delete [] rows;
|
||||
|
||||
|
||||
if( true ) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
|
||||
// LodePNG implementation
|
||||
|
||||
unsigned char *encodedBytes;
|
||||
size_t encodedSize;
|
||||
|
||||
unsigned int error = LodePNG_encode32( &encodedBytes, &encodedSize,
|
||||
imageBytes, w, h );
|
||||
|
||||
delete [] imageBytes;
|
||||
|
||||
if( error ) {
|
||||
delete [] encodedBytes;
|
||||
|
||||
printf( "Error encoding PNG image\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
inStream->write( encodedBytes, encodedSize );
|
||||
|
||||
delete [] encodedBytes;
|
||||
|
||||
if( true ) {
|
||||
return;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
// same for all PNG images
|
||||
// used to check for basic transmission errors, such as line-end flipping
|
||||
unsigned char pngSignature[8] = { 0x89, 0x50, 0x4E, 0x47,
|
||||
0x0D, 0x0A, 0x1A, 0x0A };
|
||||
|
||||
inStream->write( pngSignature, 8 );
|
||||
|
||||
|
||||
// data for IHDR chunk
|
||||
unsigned char headerData[13];
|
||||
|
||||
// width
|
||||
headerData[0] = (w >> 24) & 0xff;
|
||||
headerData[1] = (w >> 16) & 0xff;
|
||||
headerData[2] = (w >> 8) & 0xff;
|
||||
headerData[3] = w & 0xff;
|
||||
|
||||
// height
|
||||
headerData[4] = (h >> 24) & 0xff;
|
||||
headerData[5] = (h >> 16) & 0xff;
|
||||
headerData[6] = (h >> 8) & 0xff;
|
||||
headerData[7] = h & 0xff;
|
||||
|
||||
// bit depth
|
||||
headerData[8] = 8;
|
||||
|
||||
// color type
|
||||
// 2 = truecolor (RGB)
|
||||
headerData[9] = 2;
|
||||
|
||||
// compression method
|
||||
// method 0 (deflate)
|
||||
headerData[10] = 0;
|
||||
|
||||
// filter method
|
||||
// method 0 supports 5 filter types
|
||||
headerData[11] = 0;
|
||||
|
||||
// no interlace
|
||||
headerData[12] = 0;
|
||||
|
||||
writeChunk( "IHDR", headerData, 13, inStream );
|
||||
|
||||
|
||||
int numRawBytes = w * h * 3;
|
||||
// extra byte per scanline for filter type
|
||||
numRawBytes += h;
|
||||
|
||||
unsigned char *rawScanlineBytes = new unsigned char[ numRawBytes ];
|
||||
|
||||
// ignore alpha channel
|
||||
double *channels[3];
|
||||
int i;
|
||||
for( i=0; i<3; i++ ) {
|
||||
channels[i] = inImage->getChannel( i );
|
||||
}
|
||||
|
||||
int pixelNumber = 0;
|
||||
for( int y=0; y<h; y++ ) {
|
||||
// each scanline starts with filter type byte
|
||||
// using filter type 0 (no filtering)
|
||||
|
||||
int index = y * ( w * 3 + 1 );
|
||||
|
||||
// filter type
|
||||
rawScanlineBytes[ index ] = 0;
|
||||
|
||||
index++;
|
||||
|
||||
for( int x=0; x<w; x++ ) {
|
||||
|
||||
for( i=0; i<3; i++ ) {
|
||||
rawScanlineBytes[ index ] =
|
||||
(unsigned char)( 255 * channels[i][pixelNumber] );
|
||||
|
||||
index ++;
|
||||
}
|
||||
|
||||
pixelNumber++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// zlib block contains
|
||||
// zlib compression method (1 byte)
|
||||
// flags (1 byte)
|
||||
// compressed data blocks
|
||||
// check value (4 bytes) (not CRC)
|
||||
|
||||
SimpleVector<unsigned char> zlibBlock;
|
||||
|
||||
// compression method 8 (deflate)
|
||||
// with a LZ77 window size parameter of w=7
|
||||
// LZ77 window size is then 2^( w + 8 ), or in this case 32768
|
||||
zlibBlock.push_back( 0x78 );
|
||||
|
||||
// flags
|
||||
// compression level 0 (2 bytes = 00b)
|
||||
// no preset dictionary (1 byte = 0b)
|
||||
// check bits for compression method (5 bits)
|
||||
// Should be such that if the 8-bit compression method, followed
|
||||
// by the 8-bit flags field, is viewed as a 16-bit number,
|
||||
// it is an even multiple of 31
|
||||
// For our settings, check bits of 00001 works
|
||||
//zlibBlock.push_back( 0x01 );
|
||||
// hack: mimic zlib here
|
||||
zlibBlock.push_back( 0xda );
|
||||
|
||||
|
||||
// now ready for compressed data blocks
|
||||
|
||||
int rawDataIndex = 0;
|
||||
|
||||
// length field is 16 bits
|
||||
int maxUncompressedBlockLength = 65535;
|
||||
|
||||
while( rawDataIndex < numRawBytes ) {
|
||||
|
||||
// push another deflate block
|
||||
|
||||
// first bit BFINAL
|
||||
// only 1 for final block
|
||||
// next two bits BTYPE
|
||||
// BTYPE=00 is an uncompressed block
|
||||
// remaining 5 bits ignored
|
||||
// Thus, we put 0x00 for uncompressed blocks that are not final
|
||||
// and 0x80 for final uncompressed block
|
||||
|
||||
|
||||
int bytesLeft = numRawBytes - rawDataIndex;
|
||||
|
||||
int bytesInBlock;
|
||||
|
||||
if( bytesLeft <= maxUncompressedBlockLength ) {
|
||||
// final
|
||||
|
||||
// hack: when comparing output with zlib, noticed that it doesn't
|
||||
// set 0x80 for the final block, instead it uses 0x01
|
||||
// For some reason, this was making the PNG unreadable
|
||||
// zlibBlock.push_back( 0x80 );
|
||||
zlibBlock.push_back( 0x01 );
|
||||
|
||||
bytesInBlock = bytesLeft;
|
||||
}
|
||||
else {
|
||||
// not final
|
||||
zlibBlock.push_back( 0x00 );
|
||||
|
||||
bytesInBlock = maxUncompressedBlockLength;
|
||||
}
|
||||
|
||||
// length in least-significant-byte-first order
|
||||
unsigned char firstLengthByte = bytesInBlock & 0xff;
|
||||
unsigned char secondLengthByte = (bytesInBlock >> 8) & 0xff;
|
||||
zlibBlock.push_back( firstLengthByte );
|
||||
zlibBlock.push_back( secondLengthByte );
|
||||
|
||||
// those same length bytes inverted
|
||||
// (called "one's compliment" in the spec
|
||||
zlibBlock.push_back( firstLengthByte ^ 0xff);
|
||||
zlibBlock.push_back( secondLengthByte ^ 0xff );
|
||||
|
||||
// now the uncompressed data
|
||||
|
||||
for( int b=0; b< bytesInBlock; b++ ) {
|
||||
zlibBlock.push_back( rawScanlineBytes[ rawDataIndex ] );
|
||||
|
||||
rawDataIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// finally, adler32 of original data
|
||||
unsigned long adler = updateAdler32( 1L, rawScanlineBytes, numRawBytes );
|
||||
|
||||
zlibBlock.push_back( (adler >> 24) & 0xff );
|
||||
zlibBlock.push_back( (adler >> 16) & 0xff );
|
||||
zlibBlock.push_back( (adler >> 8) & 0xff );
|
||||
zlibBlock.push_back( adler & 0xff );
|
||||
|
||||
|
||||
// the zlib block is now complete
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
// check against real zlib implementation
|
||||
z_stream zStream;
|
||||
|
||||
zStream.next_in = rawScanlineBytes;
|
||||
zStream.avail_in = numRawBytes;
|
||||
zStream.total_in = 0;
|
||||
|
||||
int outSize = 2 * numRawBytes + 100;
|
||||
unsigned char *zlibOutBuffer = new unsigned char[ outSize ];
|
||||
zStream.next_out = zlibOutBuffer;
|
||||
zStream.avail_out = outSize;
|
||||
zStream.total_out = 0;
|
||||
|
||||
zStream.data_type = Z_BINARY;
|
||||
|
||||
zStream.zalloc = Z_NULL;
|
||||
zStream.zfree = Z_NULL;
|
||||
|
||||
// init the stream
|
||||
// no compression
|
||||
int result;
|
||||
//result = deflateInit( &zStream, Z_DEFAULT_COMPRESSION);
|
||||
result = deflateInit( &zStream, Z_NO_COMPRESSION);
|
||||
|
||||
if( result != Z_OK ) {
|
||||
printf( "zlib deflateInit error: %s\n", zStream.msg );
|
||||
}
|
||||
|
||||
|
||||
// deflate and flush
|
||||
result = deflate( &zStream, Z_FINISH );
|
||||
|
||||
if( result != Z_STREAM_END ) {
|
||||
printf( "zlib deflate error (%d): %s\n", result, zStream.msg );
|
||||
}
|
||||
printf( "Total in = %d, total out = %d\n",
|
||||
zStream.total_in, zStream.total_out );
|
||||
|
||||
|
||||
|
||||
|
||||
printf( "Our raw bytes (%d):\n", numRawBytes );
|
||||
int b;
|
||||
for( b=0; b<numRawBytes; b++ ) {
|
||||
printf( "%02x ", rawScanlineBytes[ b ] );
|
||||
}
|
||||
|
||||
printf( "\n\nOur zlib bytes (%d) (adler = %d) :\n",
|
||||
zlibBlock.size(), adler );
|
||||
|
||||
for( b=0; b<zlibBlock.size(); b++ ) {
|
||||
printf( "%02x ", *( zlibBlock.getElement( b ) ) );
|
||||
}
|
||||
|
||||
|
||||
printf( "\n\nTheir zlib bytes (%d) (adler = %d) :\n",
|
||||
(int)( zStream.total_out ), zStream.adler );
|
||||
for( b=0; b<zStream.total_out; b++ ) {
|
||||
printf( "%02x ", zlibOutBuffer[ b ] );
|
||||
}
|
||||
|
||||
int minBytes = zStream.total_out;
|
||||
if( minBytes > zlibBlock.size() ) {
|
||||
minBytes = zlibBlock.size();
|
||||
}
|
||||
|
||||
for( b=0; b<minBytes; b++ ) {
|
||||
if( zlibOutBuffer[b] != *( zlibBlock.getElement( b ) ) ) {
|
||||
printf( "mismatch at byte %d\n", b );
|
||||
printf( "Theirs: %02x, ours: %02x\n", zlibOutBuffer[b],
|
||||
*( zlibBlock.getElement( b ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
printf( "\n\n" );
|
||||
|
||||
|
||||
|
||||
|
||||
delete [] zlibOutBuffer;
|
||||
result = deflateEnd( &zStream );
|
||||
|
||||
if( result != Z_OK ) {
|
||||
printf( "zlib deflateEnd error (%d): %s\n", result, zStream.msg );
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
// the zlib block is the data of an IDAT chunk
|
||||
|
||||
unsigned char *zlibBytes = zlibBlock.getElementArray();
|
||||
|
||||
writeChunk( "IDAT", zlibBytes, zlibBlock.size(), inStream );
|
||||
|
||||
|
||||
delete [] zlibBytes;
|
||||
|
||||
// no data in end chunk
|
||||
writeChunk( "IEND", NULL, 0, inStream );
|
||||
|
||||
|
||||
delete [] rawScanlineBytes;
|
||||
|
||||
|
||||
// done
|
||||
}
|
||||
|
||||
|
||||
|
||||
Image *PNGImageConverter::deformatImage( InputStream *inStream ) {
|
||||
|
||||
printf( "ERROR: reading PNG images is not supported by "
|
||||
"PNGImageConverter\n" );
|
||||
|
||||
return NULL;
|
||||
}
|
100
minorGems/graphics/converters/PNGImageConverter.h
Normal file
100
minorGems/graphics/converters/PNGImageConverter.h
Normal file
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Modification History
|
||||
*
|
||||
* 2006-November-21 Jason Rohrer
|
||||
* Created.
|
||||
*
|
||||
* 2010-May-18 Jason Rohrer
|
||||
* String parameters as const to fix warnings.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef PNG_IMAGE_CONVERTER_INCLUDED
|
||||
#define PNG_IMAGE_CONVERTER_INCLUDED
|
||||
|
||||
|
||||
#include "BigEndianImageConverter.h"
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* PNG implementation of the image conversion interface.
|
||||
*
|
||||
* Note that it only supports 32-bit PNG files
|
||||
* (3-channel Images are given a solid alpha channel).
|
||||
*
|
||||
* @author Jason Rohrer
|
||||
*/
|
||||
class PNGImageConverter : public BigEndianImageConverter {
|
||||
|
||||
public:
|
||||
|
||||
|
||||
// compression level can be from 0 (none, raw data) to
|
||||
// 9 (most, slowest).
|
||||
// 1 is supposedly the "fastest" setting that actually does some
|
||||
// compression
|
||||
// 3 to 6 are supposed to be "nearly as good" as 7-9, but "much faster"
|
||||
//
|
||||
// Defaults to 5.
|
||||
PNGImageConverter( int inCompressionLevel=5 );
|
||||
|
||||
|
||||
|
||||
// implement the ImageConverter interface
|
||||
virtual void formatImage( Image *inImage,
|
||||
OutputStream *inStream );
|
||||
|
||||
virtual Image *deformatImage( InputStream *inStream );
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
int mCompressionLevel;
|
||||
|
||||
|
||||
/**
|
||||
* Writes a chunk to a stream.
|
||||
*
|
||||
* @param inChunkType the 4-char type of the chunk.
|
||||
* @param inData the data for the chunk. Can be NULL if no data
|
||||
* in chunk. Destroyed by caller.
|
||||
* @param inNumBytes the length of inData, or 0 if inData is NULL.
|
||||
* @param inStream the stream to write the chunk to. Destroyed by
|
||||
* caller.
|
||||
*/
|
||||
void writeChunk( const char inChunkType[4], unsigned char *inData,
|
||||
unsigned long inNumBytes, OutputStream *inStream );
|
||||
|
||||
|
||||
|
||||
// precomputed CRCs for all 8-bit messages
|
||||
unsigned long mCRCTable[256];
|
||||
|
||||
|
||||
const static unsigned long mStartCRC = 0xffffffffL;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Updates a crc with new data.
|
||||
*
|
||||
* Note that starting state for a CRC (before it is updated with data)
|
||||
* must be mStartCRC.
|
||||
* After all data has been added to the CRC, the resulting value
|
||||
* must be inverted (crc ^ 0xffffffffL).
|
||||
*
|
||||
* @param inCRC the current crc value.
|
||||
* @param inData the data. Destroyed by caller.
|
||||
* @param inLength the length of the data.
|
||||
*
|
||||
* @return the new CRC.
|
||||
*/
|
||||
unsigned long updateCRC( unsigned long inCRC, unsigned char *inData,
|
||||
int inLength );
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
376
minorGems/graphics/converters/TGAImageConverter.h
Normal file
376
minorGems/graphics/converters/TGAImageConverter.h
Normal file
|
@ -0,0 +1,376 @@
|
|||
/*
|
||||
* Modification History
|
||||
*
|
||||
* 2001-September-22 Jason Rohrer
|
||||
* Created.
|
||||
*
|
||||
* 2001-October-13 Jason Rohrer
|
||||
* Added support for 4-channel images.
|
||||
*
|
||||
* 2006-November-18 Jason Rohrer
|
||||
* Fixed two unused variable warnings.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef TGA_IMAGE_CONVERTER_INCLUDED
|
||||
#define TGA_IMAGE_CONVERTER_INCLUDED
|
||||
|
||||
|
||||
#include "LittleEndianImageConverter.h"
|
||||
|
||||
|
||||
/**
|
||||
* TGA (Targa) implementation of the image conversion interface.
|
||||
*
|
||||
* Note that it only supports 24- and 32-bit TGA files
|
||||
* (and thus only 3- and 4-channel Images).
|
||||
*
|
||||
* TGA format information taken from:
|
||||
* http://www.cubic.org/source/archive/fileform/graphic/tga/targa.txt
|
||||
*
|
||||
* @author Jason Rohrer
|
||||
*/
|
||||
class TGAImageConverter : public LittleEndianImageConverter {
|
||||
|
||||
public:
|
||||
|
||||
// implement the ImageConverter interface
|
||||
virtual void formatImage( Image *inImage,
|
||||
OutputStream *inStream );
|
||||
|
||||
virtual Image *deformatImage( InputStream *inStream );
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
inline void TGAImageConverter::formatImage( Image *inImage,
|
||||
OutputStream *inStream ) {
|
||||
|
||||
int numChannels = inImage->getNumChannels();
|
||||
|
||||
// make sure the image is in the right format
|
||||
if( numChannels != 3 &&
|
||||
numChannels != 4 ) {
|
||||
printf( "Only 3- and 4-channel images can be converted to " );
|
||||
printf( "the TGA format.\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
long width = inImage->getWidth();
|
||||
long height = inImage->getHeight();
|
||||
|
||||
long numPixels = width * height;
|
||||
|
||||
|
||||
// a buffer for writing single bytes
|
||||
unsigned char *byteBuffer = new unsigned char[1];
|
||||
|
||||
// write the identification field size
|
||||
// (an empty identification field)
|
||||
byteBuffer[0] = 0;
|
||||
inStream->write( byteBuffer, 1 );
|
||||
|
||||
// write the color map type
|
||||
// (no color map)
|
||||
byteBuffer[0] = 0;
|
||||
inStream->write( byteBuffer, 1 );
|
||||
|
||||
// write the image type code
|
||||
// (type 2: unmapped RGB image)
|
||||
byteBuffer[0] = 2;
|
||||
inStream->write( byteBuffer, 1 );
|
||||
|
||||
// no color map spec
|
||||
// (set to 0, though it will be ignored)
|
||||
unsigned char *colorMapSpec = new unsigned char[5];
|
||||
int i;
|
||||
for( i=0; i<5; i++ ) {
|
||||
colorMapSpec[i] = 0;
|
||||
}
|
||||
inStream->write( colorMapSpec, 5 );
|
||||
delete [] colorMapSpec;
|
||||
|
||||
// now for the image specification
|
||||
|
||||
// x origin coordinate
|
||||
writeLittleEndianShort( 0, inStream );
|
||||
// y origin coordinate
|
||||
writeLittleEndianShort( 0, inStream );
|
||||
|
||||
writeLittleEndianShort( width, inStream );
|
||||
writeLittleEndianShort( height, inStream );
|
||||
|
||||
// number of bits in pixels
|
||||
if( numChannels == 3 ) {
|
||||
byteBuffer[0] = 24;
|
||||
}
|
||||
else {
|
||||
byteBuffer[0] = 32;
|
||||
}
|
||||
inStream->write( byteBuffer, 1 );
|
||||
|
||||
|
||||
// image descriptor byte
|
||||
if( numChannels == 3 ) {
|
||||
// setting to 0 specifies:
|
||||
// -- no attributes per pixel (for 24-bit)
|
||||
// -- screen origin in lower left corner
|
||||
// -- non-interleaved data storage
|
||||
byteBuffer[0] = 0;
|
||||
}
|
||||
else {
|
||||
// setting to 8 specifies:
|
||||
// -- 8 attributes per pixel (for 32-bit) (attributes are alpha bits)
|
||||
// -- screen origin in lower left corner
|
||||
// -- non-interleaved data storage
|
||||
byteBuffer[0] = 8;
|
||||
}
|
||||
|
||||
// set bit 5 to 1 to specify screen origin in upper left corner
|
||||
byteBuffer[0] = byteBuffer[0] | ( 1 << 5 );
|
||||
inStream->write( byteBuffer, 1 );
|
||||
|
||||
|
||||
// We skip the image identification field,
|
||||
// since we set its length to 0 above.
|
||||
|
||||
// We also skip the color map data,
|
||||
// since we have none (as specified above).
|
||||
|
||||
|
||||
// now we write the pixels, in BGR(A) order
|
||||
unsigned char *raster = new unsigned char[ numPixels * numChannels ];
|
||||
double *red = inImage->getChannel( 0 );
|
||||
double *green = inImage->getChannel( 1 );
|
||||
double *blue = inImage->getChannel( 2 );
|
||||
|
||||
long rasterIndex = 0;
|
||||
|
||||
if( numChannels == 3 ) {
|
||||
for( int i=0; i<numPixels; i++ ) {
|
||||
raster[rasterIndex] =
|
||||
(unsigned char)( 255 * blue[i] );
|
||||
raster[rasterIndex + 1] =
|
||||
(unsigned char)( 255 * green[i] );
|
||||
raster[rasterIndex + 2] =
|
||||
(unsigned char)( 255 * red[i] );
|
||||
|
||||
rasterIndex += 3;
|
||||
}
|
||||
inStream->write( raster, numPixels * 3 );
|
||||
}
|
||||
else { // numChannels == 4
|
||||
double *alpha = inImage->getChannel( 3 );
|
||||
|
||||
for( int i=0; i<numPixels; i++ ) {
|
||||
raster[rasterIndex] =
|
||||
(unsigned char)( 255 * blue[i] );
|
||||
raster[rasterIndex + 1] =
|
||||
(unsigned char)( 255 * green[i] );
|
||||
raster[rasterIndex + 2] =
|
||||
(unsigned char)( 255 * red[i] );
|
||||
raster[rasterIndex + 3] =
|
||||
(unsigned char)( 255 * alpha[i] );
|
||||
|
||||
rasterIndex += 4;
|
||||
}
|
||||
}
|
||||
|
||||
inStream->write( raster, numPixels * numChannels );
|
||||
|
||||
delete [] raster;
|
||||
delete [] byteBuffer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
inline Image *TGAImageConverter::deformatImage( InputStream *inStream ) {
|
||||
|
||||
// a buffer for reading single bytes
|
||||
unsigned char *byteBuffer = new unsigned char[1];
|
||||
|
||||
// read the identification field size
|
||||
inStream->read( byteBuffer, 1 );
|
||||
|
||||
int identificationFieldSize = byteBuffer[0];
|
||||
|
||||
// read the color map type
|
||||
// (only 0, or no color map, is supported)
|
||||
inStream->read( byteBuffer, 1 );
|
||||
if( byteBuffer[0] != 0 ) {
|
||||
printf( "Only TGA files without colormaps can be read.\n" );
|
||||
delete [] byteBuffer;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// read the image type code
|
||||
// (only type 2, unmapped RGB image, is supported)
|
||||
inStream->read( byteBuffer, 1 );
|
||||
if( byteBuffer[0] != 2 ) {
|
||||
printf(
|
||||
"Only TGA files containing unmapped RGB images can be read.\n" );
|
||||
delete [] byteBuffer;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// ignore color map spec
|
||||
// (skip all 5 bytes of it)
|
||||
unsigned char *colorMapSpec = new unsigned char[5];
|
||||
inStream->read( colorMapSpec, 5 );
|
||||
delete [] colorMapSpec;
|
||||
|
||||
|
||||
// now for the image specification
|
||||
|
||||
// don't need either of these
|
||||
// don't set to a variable for now to avoid unused variable warnings
|
||||
// x origin coordinate
|
||||
readLittleEndianShort( inStream );
|
||||
// y origin coordinate
|
||||
readLittleEndianShort( inStream );
|
||||
|
||||
long width = readLittleEndianShort( inStream );
|
||||
long height = readLittleEndianShort( inStream );
|
||||
|
||||
long numPixels = width * height;
|
||||
|
||||
|
||||
// number of bits in pixels
|
||||
// only 24 bits per pixel supported
|
||||
|
||||
inStream->read( byteBuffer, 1 );
|
||||
if( byteBuffer[0] != 24 && byteBuffer[0] != 32 ) {
|
||||
printf( "Only 24- and 32-bit TGA files can be read.\n" );
|
||||
delete [] byteBuffer;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int numChannels = 0;
|
||||
if( byteBuffer[0] == 24 ) {
|
||||
numChannels = 3;
|
||||
}
|
||||
else {
|
||||
numChannels = 4;
|
||||
}
|
||||
|
||||
|
||||
// image descriptor byte
|
||||
// setting to 0 specifies:
|
||||
// -- no attributes per pixel (for 24-bit)
|
||||
// -- screen origin in lower left corner
|
||||
// -- non-interleaved data storage
|
||||
// set bit 5 to 1 to specify screen origin in upper left corner
|
||||
inStream->read( byteBuffer, 1 );
|
||||
char originAtTop = byteBuffer[0] & ( 1 << 5 );
|
||||
|
||||
|
||||
if( identificationFieldSize > 0 ) {
|
||||
// We skip the image identification field
|
||||
unsigned char *identificationField =
|
||||
new unsigned char[ identificationFieldSize ];
|
||||
inStream->read( identificationField, identificationFieldSize );
|
||||
delete [] identificationField;
|
||||
}
|
||||
|
||||
|
||||
// We also skip the color map data,
|
||||
// since we have none (as specified above).
|
||||
|
||||
|
||||
|
||||
|
||||
// now we read the pixels, in BGR(A) order
|
||||
unsigned char *raster = new unsigned char[ numPixels * numChannels ];
|
||||
inStream->read( raster, numPixels * numChannels );
|
||||
|
||||
// optimization: don't init channels to black (found with profiler)
|
||||
Image *image = new Image( width, height, numChannels, false );
|
||||
|
||||
double *red = image->getChannel( 0 );
|
||||
double *green = image->getChannel( 1 );
|
||||
double *blue = image->getChannel( 2 );
|
||||
|
||||
long rasterIndex = 0;
|
||||
double inv255 = 1.0 / 255.0;
|
||||
|
||||
if( numChannels == 3 ) {
|
||||
if( originAtTop ) {
|
||||
for( int i=0; i<numPixels; i++ ) {
|
||||
blue[i] = inv255 * raster[ rasterIndex ];
|
||||
green[i] = inv255 * raster[ rasterIndex + 1 ];
|
||||
red[i] = inv255 * raster[ rasterIndex + 2 ];
|
||||
|
||||
rasterIndex += 3;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// we need to flip the raster vertically as we
|
||||
// copy it into our return image
|
||||
for( int y=height-1; y>=0; y-- ) {
|
||||
for( int x=0; x<width; x++ ) {
|
||||
int imageIndex = y * width + x;
|
||||
|
||||
blue[ imageIndex ] = inv255 * raster[ rasterIndex ];
|
||||
green[imageIndex] = inv255 * raster[ rasterIndex + 1 ];
|
||||
red[imageIndex] = inv255 * raster[ rasterIndex + 2 ];
|
||||
|
||||
rasterIndex += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else { // numChannels == 4
|
||||
double *alpha = image->getChannel( 3 );
|
||||
|
||||
if( originAtTop ) {
|
||||
for( int i=0; i<numPixels; i++ ) {
|
||||
// optimization: use postfix increment operators in
|
||||
// array index
|
||||
// (found with profiler)
|
||||
blue[i] = inv255 * raster[ rasterIndex ++ ];
|
||||
green[i] = inv255 * raster[ rasterIndex ++ ];
|
||||
red[i] = inv255 * raster[ rasterIndex ++ ];
|
||||
alpha[i] = inv255 * raster[ rasterIndex ++ ];
|
||||
|
||||
// optimization
|
||||
// rasterIndex += 4;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// we need to flip the raster vertically as we
|
||||
// copy it into our return image
|
||||
for( int y=height-1; y>=0; y-- ) {
|
||||
int yOffset = y * width;
|
||||
|
||||
for( int x=0; x<width; x++ ) {
|
||||
int imageIndex = yOffset + x;
|
||||
|
||||
// optimization: use postfix increment operators in
|
||||
// array index
|
||||
// (found with profiler)
|
||||
|
||||
blue[ imageIndex ] = inv255 * raster[ rasterIndex ++ ];
|
||||
green[imageIndex] = inv255 * raster[ rasterIndex ++ ];
|
||||
red[imageIndex] = inv255 * raster[ rasterIndex ++ ];
|
||||
alpha[imageIndex] = inv255 * raster[ rasterIndex ++ ];
|
||||
|
||||
// optimization
|
||||
// rasterIndex += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
delete [] raster;
|
||||
delete [] byteBuffer;
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
90
minorGems/graphics/converters/bmpConverterTest.cpp
Normal file
90
minorGems/graphics/converters/bmpConverterTest.cpp
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Modification History
|
||||
*
|
||||
* 2001-February-19 Jason Rohrer
|
||||
* Created.
|
||||
*
|
||||
* 2001-February-24 Jason Rohrer
|
||||
* Fixed incorrect delete usage.
|
||||
*
|
||||
* 2001-April-12 Jason Rohrer
|
||||
* Changed to comply with new FileInput/OutputStream interface
|
||||
* (underlying File must be destroyed explicitly).
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "minorGems/graphics/Image.h"
|
||||
#include "BMPImageConverter.h"
|
||||
|
||||
#include "minorGems/io/file/File.h"
|
||||
#include "minorGems/io/file/FileOutputStream.h"
|
||||
#include "minorGems/io/file/FileInputStream.h"
|
||||
|
||||
// test function for the BMPImageConverter class
|
||||
int main( char inNumArgs, char**inArgs ) {
|
||||
if( inNumArgs != 2 ) {
|
||||
printf( "must pass in a file name to write to\n" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
int length = 0;
|
||||
while( inArgs[1][length] != '\0' ) {
|
||||
length++;
|
||||
}
|
||||
|
||||
File *file = new File( NULL, inArgs[1], length );
|
||||
|
||||
|
||||
// read image in
|
||||
FileInputStream *stream = new FileInputStream( file );
|
||||
BMPImageConverter *converter = new BMPImageConverter();
|
||||
Image *image = converter->deformatImage( stream );
|
||||
|
||||
if( image != NULL ) {
|
||||
// write image back out
|
||||
File *fileOut = new File( NULL, "testOut.bmp", 11 );
|
||||
FileOutputStream *outStream = new FileOutputStream( fileOut );
|
||||
|
||||
converter->formatImage( image, outStream );
|
||||
|
||||
delete outStream;
|
||||
delete image;
|
||||
}
|
||||
|
||||
delete stream;
|
||||
delete file;
|
||||
delete converter;
|
||||
|
||||
|
||||
/*
|
||||
FileOutputStream *stream = new FileOutputStream( file );
|
||||
|
||||
BMPImageConverter *converter = new BMPImageConverter();
|
||||
|
||||
Image *image = new Image( 256, 256, 3 );
|
||||
|
||||
double *red = image->getChannel( 0 );
|
||||
double *green = image->getChannel( 1 );
|
||||
|
||||
for( int y=0; y<image->getHeight(); y++ ) {
|
||||
for( int x=0; x<image->getWidth(); x++ ) {
|
||||
long index = y * image->getWidth() + x;
|
||||
|
||||
red[index] = (double)y / (double)( image->getHeight() );
|
||||
green[index] = (double)x / (double)( image->getWidth() );
|
||||
//red[index] = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
converter->formatImage( image, stream );
|
||||
|
||||
|
||||
delete stream;
|
||||
// delete file explicitly
|
||||
delete file;
|
||||
delete converter;
|
||||
delete image;
|
||||
*/
|
||||
return 0;
|
||||
}
|
87
minorGems/graphics/converters/bmpformat.txt
Normal file
87
minorGems/graphics/converters/bmpformat.txt
Normal file
|
@ -0,0 +1,87 @@
|
|||
|
||||
Size
|
||||
Description
|
||||
Header
|
||||
14 bytes
|
||||
Windows Structure: BITMAPFILEHEADER
|
||||
|
||||
Signature
|
||||
2 bytes
|
||||
'BM'
|
||||
FileSize
|
||||
4 bytes
|
||||
File size in bytes
|
||||
reserved
|
||||
4 bytes
|
||||
unused (=0)
|
||||
DataOffset
|
||||
4 bytes
|
||||
File offset to Raster Data
|
||||
InfoHeader
|
||||
40 bytes
|
||||
Windows Structure: BITMAPINFOHEADER
|
||||
|
||||
Size
|
||||
4 bytes
|
||||
Size of InfoHeader =40
|
||||
Width
|
||||
4 bytes
|
||||
Bitmap Width
|
||||
Height
|
||||
4 bytes
|
||||
Bitmap Height
|
||||
Planes
|
||||
2 bytes
|
||||
Number of Planes (=1)
|
||||
BitCount
|
||||
2 bytes
|
||||
Bits per Pixel
|
||||
1 = monochrome palette. NumColors = 1
|
||||
4 = 4bit palletized. NumColors = 16
|
||||
8 = 8bit palletized. NumColors = 256
|
||||
16 = 16bit RGB. NumColors = 65536 (?)
|
||||
24 = 24bit RGB. NumColors = 16M
|
||||
Compression
|
||||
4 bytes
|
||||
Type of Compression
|
||||
0 = BI_RGB no compression
|
||||
1 = BI_RLE8 8bit RLE encoding
|
||||
2 = BI_RLE4 4bit RLE encoding
|
||||
ImageSize
|
||||
4 bytes
|
||||
(compressed) Size of Image
|
||||
It is valid to set this =0 if Compression = 0
|
||||
XpixelsPerM
|
||||
4 bytes
|
||||
horizontal resolution: Pixels/meter
|
||||
YpixelsPerM
|
||||
4 bytes
|
||||
vertical resolution: Pixels/meter
|
||||
ColorsUsed
|
||||
4 bytes
|
||||
Number of actually used colors
|
||||
ColorsImportant
|
||||
4 bytes
|
||||
Number of important colors
|
||||
0 = all
|
||||
ColorTable
|
||||
4 * NumColors bytes
|
||||
present only if Info.BitsPerPixel <= 8
|
||||
colors should be ordered by importance
|
||||
|
||||
Red
|
||||
1 byte
|
||||
Red intensity
|
||||
Green
|
||||
1 byte
|
||||
Green intensity
|
||||
Blue
|
||||
1 byte
|
||||
Blue intensity
|
||||
reserved
|
||||
1 byte
|
||||
unused (=0)
|
||||
repeated NumColors times
|
||||
Raster Data
|
||||
Info.ImageSize bytes
|
||||
The pixel data
|
1
minorGems/graphics/converters/compileTestPNG
Executable file
1
minorGems/graphics/converters/compileTestPNG
Executable file
|
@ -0,0 +1 @@
|
|||
g++ -g -Wall -o testPNG -I../../.. testPNG.cpp PNGImageConverter.cpp ../../io/file/linux/PathLinux.cpp ../../system/unix/TimeUnix.cpp -lz -lpng
|
90
minorGems/graphics/converters/jpegConverterTest.cpp
Normal file
90
minorGems/graphics/converters/jpegConverterTest.cpp
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Modification History
|
||||
*
|
||||
* 2001-April-27 Jason Rohrer
|
||||
* Created.
|
||||
*
|
||||
* 2001-April-29 Jason Rohrer
|
||||
* Completed initial version and used to test JPEGImageConverter
|
||||
* successfully.
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "minorGems/graphics/Image.h"
|
||||
#include "JPEGImageConverter.h"
|
||||
|
||||
#include "minorGems/io/file/File.h"
|
||||
#include "minorGems/io/file/FileOutputStream.h"
|
||||
#include "minorGems/io/file/FileInputStream.h"
|
||||
|
||||
// test function for the BMPImageConverter class
|
||||
int main( char inNumArgs, char**inArgs ) {
|
||||
if( inNumArgs != 2 ) {
|
||||
printf( "must pass in a file name to write to\n" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
int length = 0;
|
||||
while( inArgs[1][length] != '\0' ) {
|
||||
length++;
|
||||
}
|
||||
|
||||
File *file = new File( NULL, inArgs[1], length );
|
||||
|
||||
|
||||
// read image in
|
||||
FileInputStream *stream = new FileInputStream( file );
|
||||
JPEGImageConverter *converter = new JPEGImageConverter( 50 );
|
||||
Image *image = converter->deformatImage( stream );
|
||||
|
||||
if( image != NULL ) {
|
||||
// write image back out
|
||||
File *fileOut = new File( NULL, "testOut.jpg", 11 );
|
||||
FileOutputStream *outStream = new FileOutputStream( fileOut );
|
||||
|
||||
converter->formatImage( image, outStream );
|
||||
|
||||
delete outStream;
|
||||
delete fileOut;
|
||||
delete image;
|
||||
}
|
||||
|
||||
|
||||
delete stream;
|
||||
delete converter;
|
||||
delete file;
|
||||
|
||||
/*
|
||||
FileOutputStream *stream = new FileOutputStream( file );
|
||||
|
||||
JPEGImageConverter *converter = new JPEGImageConverteonverter( 50 );
|
||||
|
||||
Image *image = new Image( 256, 256, 3 );
|
||||
|
||||
double *red = image->getChannel( 0 );
|
||||
double *green = image->getChannel( 1 );
|
||||
|
||||
for( int y=0; y<image->getHeight(); y++ ) {
|
||||
for( int x=0; x<image->getWidth(); x++ ) {
|
||||
long index = y * image->getWidth() + x;
|
||||
|
||||
red[index] = (double)y / (double)( image->getHeight() );
|
||||
green[index] = (double)x / (double)( image->getWidth() );
|
||||
//red[index] = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
converter->formatImage( image, stream );
|
||||
|
||||
|
||||
delete stream;
|
||||
// delete file explicitly
|
||||
delete file;
|
||||
delete converter;
|
||||
delete image;
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
1
minorGems/graphics/converters/jpegConverterTestCompile
Executable file
1
minorGems/graphics/converters/jpegConverterTestCompile
Executable file
|
@ -0,0 +1 @@
|
|||
g++ -I../../.. -ljpeg -o jpegConverterTest jpegConverterTest.cpp unix/JPEGImageConverterUnix.cpp ../../io/file/linux/PathLinux.cpp
|
108
minorGems/graphics/converters/libpngSample.cpp
Normal file
108
minorGems/graphics/converters/libpngSample.cpp
Normal file
|
@ -0,0 +1,108 @@
|
|||
|
||||
|
||||
|
||||
|
||||
#include <png.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
|
||||
void abort_(const char * s, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, s);
|
||||
vfprintf(stderr, s, args);
|
||||
fprintf(stderr, "\n");
|
||||
va_end(args);
|
||||
abort();
|
||||
}
|
||||
|
||||
int main() {
|
||||
|
||||
int w = 100;
|
||||
int h = 100;
|
||||
|
||||
unsigned int *data = new unsigned int[ w * h ];
|
||||
|
||||
// red fades toward bottom
|
||||
// green fades toward right
|
||||
|
||||
unsigned int **rows = new unsigned int *[ h ];
|
||||
|
||||
for( int y=0; y<h; y++ ) {
|
||||
for( int x=0; x<w; x++ ) {
|
||||
unsigned char red = 255 - ( 255 * y / h );
|
||||
unsigned char green = 255 - ( 255 * x / h );
|
||||
|
||||
data[ y * w + x ] = red | green << 8 | 255 << 24;
|
||||
}
|
||||
|
||||
rows[y] = &( data[ y * w ] );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* create file */
|
||||
FILE *fp = fopen( "test.png", "wb");
|
||||
if (!fp)
|
||||
abort_("[write_png_file] File %s could not be opened for writing",
|
||||
"test.png" );
|
||||
|
||||
png_structp png_ptr;
|
||||
png_infop info_ptr;
|
||||
|
||||
/* initialize stuff */
|
||||
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
||||
|
||||
if (!png_ptr)
|
||||
abort_("[write_png_file] png_create_write_struct failed");
|
||||
|
||||
info_ptr = png_create_info_struct(png_ptr);
|
||||
if (!info_ptr)
|
||||
abort_("[write_png_file] png_create_info_struct failed");
|
||||
|
||||
if (setjmp(png_jmpbuf(png_ptr)))
|
||||
abort_("[write_png_file] Error during init_io");
|
||||
|
||||
png_init_io(png_ptr, fp);
|
||||
|
||||
|
||||
/* write header */
|
||||
if (setjmp(png_jmpbuf(png_ptr)))
|
||||
abort_("[write_png_file] Error during writing header");
|
||||
|
||||
png_set_IHDR(png_ptr, info_ptr, w, h,
|
||||
8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
|
||||
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
|
||||
|
||||
png_write_info(png_ptr, info_ptr);
|
||||
|
||||
|
||||
/* write bytes */
|
||||
if (setjmp(png_jmpbuf(png_ptr)))
|
||||
|
||||
abort_("[write_png_file] Error during writing bytes");
|
||||
|
||||
png_write_image(png_ptr, (png_byte**)rows);
|
||||
|
||||
|
||||
/* end write */
|
||||
if (setjmp(png_jmpbuf(png_ptr)))
|
||||
abort_("[write_png_file] Error during end of write");
|
||||
|
||||
png_write_end(png_ptr, NULL);
|
||||
|
||||
|
||||
/* cleanup heap allocation */
|
||||
/*for (y=0; y<height; y++)
|
||||
free(row_pointers[y]);
|
||||
free(row_pointers);
|
||||
*/
|
||||
fclose(fp);
|
||||
}
|
4319
minorGems/graphics/converters/lodepng.cpp
Normal file
4319
minorGems/graphics/converters/lodepng.cpp
Normal file
File diff suppressed because it is too large
Load diff
1708
minorGems/graphics/converters/lodepng.h
Normal file
1708
minorGems/graphics/converters/lodepng.h
Normal file
File diff suppressed because it is too large
Load diff
44
minorGems/graphics/converters/testPNG.cpp
Normal file
44
minorGems/graphics/converters/testPNG.cpp
Normal file
|
@ -0,0 +1,44 @@
|
|||
#include "PNGImageConverter.h"
|
||||
|
||||
#include "minorGems/graphics/Image.h"
|
||||
|
||||
#include "minorGems/io/file/FileOutputStream.h"
|
||||
|
||||
#include "minorGems/system/Time.h"
|
||||
|
||||
|
||||
int main() {
|
||||
|
||||
int imageSize = 640;
|
||||
Image testImage( imageSize, imageSize, 3, false );
|
||||
|
||||
|
||||
// red fades toward bottom
|
||||
// green fades toward right
|
||||
double *red = testImage.getChannel( 0 );
|
||||
double *green = testImage.getChannel( 1 );
|
||||
|
||||
for( int y=0; y<imageSize; y++ ) {
|
||||
for( int x=0; x<imageSize; x++ ) {
|
||||
red[y*imageSize+x] = 1.0 - ( y / (double)imageSize );
|
||||
green[y*imageSize+x] = 1.0 - ( x / (double)imageSize );
|
||||
green[y*imageSize+x] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
PNGImageConverter png;
|
||||
|
||||
|
||||
File outFileB( NULL, "test.png" );
|
||||
FileOutputStream outStreamB( &outFileB );
|
||||
|
||||
|
||||
double t = Time::getCurrentTime();
|
||||
|
||||
png.formatImage( &testImage, &outStreamB );
|
||||
|
||||
printf( "Converter took %f seconds\n",
|
||||
Time::getCurrentTime() - t );
|
||||
|
||||
return 0;
|
||||
}
|
563
minorGems/graphics/converters/unix/JPEGImageConverterUnix.cpp
Normal file
563
minorGems/graphics/converters/unix/JPEGImageConverterUnix.cpp
Normal file
|
@ -0,0 +1,563 @@
|
|||
/*
|
||||
* Modification History
|
||||
*
|
||||
* 2001-April-27 Jason Rohrer
|
||||
* Created.
|
||||
*
|
||||
* 2001-April-29 Jason Rohrer
|
||||
* Finished implementation.
|
||||
* Added an optimization to formatImage, but it did not improve
|
||||
* performance, so it has been commented out.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Unix-specific JPEGImageConverter implementation
|
||||
*
|
||||
* Code for compression and decompression modeled after IJG's
|
||||
* libjpeg example code.
|
||||
*
|
||||
* For now, it use libjpeg to write converted data out to
|
||||
* file, and then reads it back in.
|
||||
*/
|
||||
|
||||
|
||||
#include "minorGems/graphics/converters/JPEGImageConverter.h"
|
||||
#include "minorGems/io/file/File.h"
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
// include the jpeg library as a C file.
|
||||
// (yuk... spent way too much time trying to figure this one out!)
|
||||
extern "C" {
|
||||
#include<jpeglib.h>
|
||||
}
|
||||
|
||||
/*
|
||||
* <setjmp.h> is used for the decompression
|
||||
* error recovery mechanism.
|
||||
*/
|
||||
|
||||
#include <setjmp.h>
|
||||
|
||||
|
||||
|
||||
void JPEGImageConverter::formatImage( Image *inImage,
|
||||
OutputStream *inStream ) {
|
||||
|
||||
if( inImage->getNumChannels() != 3 ) {
|
||||
printf( "JPEGImageConverter only works on 3-channel images.\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
// most of this code was copied without modification from
|
||||
// IJG's example.c
|
||||
|
||||
// This struct contains the JPEG compression parameters and pointers to
|
||||
// working space (which is allocated as needed by the JPEG library).
|
||||
// It is possible to have several such structures, representing multiple
|
||||
// compression/decompression processes, in existence at once. We refer
|
||||
// to any one struct (and its associated working data) as a "JPEG object".
|
||||
|
||||
struct jpeg_compress_struct cinfo;
|
||||
|
||||
// This struct represents a JPEG error handler. It is declared separately
|
||||
// because applications often want to supply a specialized error handler
|
||||
// (see the second half of this file for an example). But here we just
|
||||
// take the easy way out and use the standard error handler, which will
|
||||
// print a message on stderr and call exit() if compression fails.
|
||||
// Note that this struct must live as long as the main JPEG parameter
|
||||
// struct, to avoid dangling-pointer problems.
|
||||
|
||||
struct jpeg_error_mgr jerr;
|
||||
|
||||
// More stuff
|
||||
FILE * outfile; // target file
|
||||
JSAMPROW row_pointer[1]; // pointer to JSAMPLE row[s]
|
||||
int row_stride; // physical row width in image buffer
|
||||
|
||||
// Step 1: allocate and initialize JPEG compression object
|
||||
|
||||
// We have to set up the error handler first, in case the initialization
|
||||
// step fails. (Unlikely, but it could happen if you are out of memory.)
|
||||
// This routine fills in the contents of struct jerr, and returns jerr's
|
||||
// address which we place into the link field in cinfo.
|
||||
|
||||
cinfo.err = jpeg_std_error( &jerr );
|
||||
// Now we can initialize the JPEG compression object.
|
||||
jpeg_create_compress( &cinfo );
|
||||
|
||||
// Step 2: specify data destination (eg, a file)
|
||||
// Note: steps 2 and 3 can be done in either order.
|
||||
|
||||
// use a temp file with a random name to make this more
|
||||
// thread-safe
|
||||
char *fileName = new char[99];
|
||||
sprintf( fileName, "temp%d.dat", rand() );
|
||||
|
||||
// Here we use the library-supplied code to send compressed data to a
|
||||
// stdio stream. You can also write your own code to do something else.
|
||||
// VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
|
||||
// requires it in order to write binary files.
|
||||
|
||||
if( ( outfile = fopen( fileName, "wb" ) ) == NULL ) {
|
||||
printf( "can't open jpeg conversion temp file %s\n", fileName );
|
||||
return;
|
||||
}
|
||||
|
||||
jpeg_stdio_dest( &cinfo, outfile );
|
||||
|
||||
|
||||
// Step 3: set parameters for compression
|
||||
|
||||
// First we supply a description of the input image.
|
||||
// Four fields of the cinfo struct must be filled in:
|
||||
|
||||
|
||||
// image width and height, in pixels
|
||||
cinfo.image_width = inImage->getWidth();
|
||||
cinfo.image_height = inImage->getHeight();
|
||||
cinfo.input_components = 3; // # of color components per pixel
|
||||
cinfo.in_color_space = JCS_RGB; // colorspace of input image
|
||||
// Now use the library's routine to set default compression parameters.
|
||||
// (You must set at least cinfo.in_color_space before calling this,
|
||||
// since the defaults depend on the source color space.)
|
||||
|
||||
jpeg_set_defaults( &cinfo );
|
||||
// Now you can set any non-default parameters you wish to.
|
||||
// Here we just illustrate the use of
|
||||
// quality (quantization table) scaling:
|
||||
|
||||
jpeg_set_quality( &cinfo, mQuality,
|
||||
TRUE ); // limit to baseline-JPEG values
|
||||
|
||||
// Step 4: Start compressor
|
||||
|
||||
// TRUE ensures that we will write a complete interchange-JPEG file.
|
||||
// Pass TRUE unless you are very sure of what you're doing.
|
||||
|
||||
jpeg_start_compress( &cinfo, TRUE );
|
||||
|
||||
// Step 5: while (scan lines remain to be written)
|
||||
// jpeg_write_scanlines(...);
|
||||
|
||||
// Here we use the library's state variable cinfo.next_scanline as the
|
||||
// loop counter, so that we don't have to keep track ourselves.
|
||||
// To keep things simple, we pass one scanline per call; you can pass
|
||||
// more if you wish, though.
|
||||
|
||||
// JSAMPLEs per row in image_buffer
|
||||
row_stride = cinfo.image_width * 3;
|
||||
|
||||
// channels of inImage, which we will need to pull pixel values out of
|
||||
double *redChannel = inImage->getChannel(0);
|
||||
double *greenChannel = inImage->getChannel(1);
|
||||
double *blueChannel = inImage->getChannel(2);
|
||||
|
||||
// array that we will copy inImage pixels into
|
||||
// one scanline at a time
|
||||
row_pointer[0] = new JSAMPLE[ row_stride ];
|
||||
|
||||
//int rowNumber = 0;
|
||||
|
||||
while( cinfo.next_scanline < cinfo.image_height ) {
|
||||
// jpeg_write_scanlines expects an array of pointers to scanlines.
|
||||
// Here the array is only one element long, but you could pass
|
||||
// more than one scanline at a time if that's more convenient.
|
||||
|
||||
|
||||
// make a scanline
|
||||
|
||||
|
||||
int yOffset = cinfo.next_scanline * cinfo.image_width;
|
||||
|
||||
// for each pixel in the row
|
||||
for( int p=0; p<cinfo.image_width; p++ ) {
|
||||
// index into inImage
|
||||
int pixelIndex = p + yOffset;
|
||||
|
||||
// index into this row
|
||||
int startRowIndex = p * 3;
|
||||
|
||||
// red
|
||||
row_pointer[0][ startRowIndex ] =
|
||||
(JSAMPLE)( redChannel[ pixelIndex ] * 255 );
|
||||
// green
|
||||
row_pointer[0][ startRowIndex + 1 ] =
|
||||
(JSAMPLE)( greenChannel[ pixelIndex ] * 255 );
|
||||
// blue
|
||||
row_pointer[0][ startRowIndex + 2 ] =
|
||||
(JSAMPLE)( blueChannel[ pixelIndex ] * 255 );
|
||||
}
|
||||
|
||||
|
||||
// now pass the scanline into libjpeg
|
||||
(void) jpeg_write_scanlines( &cinfo, row_pointer, 1 );
|
||||
|
||||
//rowNumber++;
|
||||
}
|
||||
|
||||
delete [] ( row_pointer[0] );
|
||||
|
||||
|
||||
// Step 6: Finish compression
|
||||
|
||||
jpeg_finish_compress( &cinfo );
|
||||
// After finish_compress, we can close the output file.
|
||||
fclose( outfile );
|
||||
|
||||
// Step 7: release JPEG compression object
|
||||
|
||||
// This is an important step since it
|
||||
// will release a good deal of memory.
|
||||
|
||||
jpeg_destroy_compress( &cinfo );
|
||||
|
||||
|
||||
// now read the compressed data back in from file
|
||||
File *file = new File( NULL, fileName, strlen( fileName ) );
|
||||
|
||||
FILE *inFile = fopen( fileName, "rb" );
|
||||
|
||||
if( inFile == NULL ) {
|
||||
printf( "can't open jpeg conversion temp file %s\n", fileName );
|
||||
return;
|
||||
}
|
||||
|
||||
// read entire file into memory
|
||||
int fileLength = file->getLength();
|
||||
unsigned char *fileBuffer = new unsigned char[ fileLength ];
|
||||
fread( fileBuffer, 1, fileLength, inFile );
|
||||
|
||||
// now write the entire buffer to our output stream
|
||||
inStream->write( fileBuffer,
|
||||
fileLength );
|
||||
|
||||
delete [] fileBuffer;
|
||||
|
||||
delete file;
|
||||
|
||||
fclose( inFile );
|
||||
|
||||
// delete this temporary file
|
||||
remove( fileName );
|
||||
|
||||
delete [] fileName;
|
||||
|
||||
|
||||
// And we're done!
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// copied this directly from IJG's example.c
|
||||
//extern "C" {
|
||||
|
||||
/*
|
||||
* ERROR HANDLING:
|
||||
*
|
||||
* The JPEG library's standard error handler (jerror.c) is divided into
|
||||
* several "methods" which you can override individually. This lets you
|
||||
* adjust the behavior without duplicating a lot of code, which you might
|
||||
* have to update with each future release.
|
||||
*
|
||||
* Our example here shows how to override the "error_exit" method so that
|
||||
* control is returned to the library's caller when a fatal error occurs,
|
||||
* rather than calling exit() as the standard error_exit method does.
|
||||
*
|
||||
* We use C's setjmp/longjmp facility to return
|
||||
* control. This means that the
|
||||
* routine which calls the JPEG library must
|
||||
* first execute a setjmp() call to
|
||||
* establish the return point. We want the replacement error_exit to do a
|
||||
* longjmp(). But we need to make the setjmp buffer accessible to the
|
||||
* error_exit routine. To do this, we make a private extension of the
|
||||
* standard JPEG error handler object. (If we were using C++, we'd say we
|
||||
* were making a subclass of the regular error handler.)
|
||||
*
|
||||
* Here's the extended error handler struct:
|
||||
*/
|
||||
|
||||
struct my_error_mgr {
|
||||
struct jpeg_error_mgr pub; /* "public" fields */
|
||||
|
||||
jmp_buf setjmp_buffer; /* for return to caller */
|
||||
};
|
||||
|
||||
typedef struct my_error_mgr * my_error_ptr;
|
||||
|
||||
|
||||
/*
|
||||
* Here's the routine that will replace the standard error_exit method:
|
||||
*/
|
||||
|
||||
METHODDEF(void) my_error_exit( j_common_ptr cinfo ) {
|
||||
/* cinfo->err really points to a my_error_mgr struct,
|
||||
so coerce pointer
|
||||
*/
|
||||
my_error_ptr myerr = (my_error_ptr)( cinfo->err );
|
||||
|
||||
/* Always display the message. */
|
||||
/* We could postpone this until after returning, if we chose. */
|
||||
(*cinfo->err->output_message)( cinfo );
|
||||
|
||||
/* Return control to the setjmp point */
|
||||
longjmp( myerr->setjmp_buffer, 1 );
|
||||
}
|
||||
|
||||
// }
|
||||
|
||||
|
||||
|
||||
Image *JPEGImageConverter::deformatImage( InputStream *inStream ) {
|
||||
|
||||
// use a temp file with a random name to make this more
|
||||
// thread-safe
|
||||
char *fileName = new char[99];
|
||||
sprintf( fileName, "temp%d.dat", rand() );
|
||||
|
||||
FILE *tempFile = fopen( fileName, "wb" );
|
||||
|
||||
if( tempFile == NULL ) {
|
||||
printf( "can't open jpeg conversion temp file %s\n", fileName );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// buffer for dumping stream to temp file
|
||||
unsigned char *tempBuffer = new unsigned char[1];
|
||||
unsigned char previousByte = 0;
|
||||
|
||||
// dump the JPEG stream from the input stream into tempFile
|
||||
// so that we can pass this file to libjpeg
|
||||
|
||||
/*
|
||||
// optimization: use a buffer to prevent too many fwrite calls
|
||||
int bufferLength = 5000;
|
||||
unsigned char *fileBuffer = new unsigned char[ bufferLength ];
|
||||
int currentBufferPosition = 0;
|
||||
|
||||
while( !( tempBuffer[0] == 0xD9 && previousByte == 0xFF ) ) {
|
||||
previousByte = tempBuffer[0];
|
||||
|
||||
inStream->read( tempBuffer, 1 );
|
||||
|
||||
fileBuffer[currentBufferPosition] = tempBuffer[0];
|
||||
|
||||
if( currentBufferPosition == bufferLength - 1 ) {
|
||||
// at the end of the file buffer
|
||||
fwrite( fileBuffer, 1, bufferLength, tempFile );
|
||||
currentBufferPosition = 0;
|
||||
}
|
||||
else {
|
||||
// keep filling the fileBuffer
|
||||
currentBufferPosition++;
|
||||
}
|
||||
}
|
||||
// now write remaining fileBuffer data to file
|
||||
fwrite( fileBuffer, 1, currentBufferPosition + 1, tempFile );
|
||||
|
||||
delete [] fileBuffer;
|
||||
*/
|
||||
|
||||
// write until EOI sequence seen (0xFFD9)
|
||||
while( !( tempBuffer[0] == 0xD9 && previousByte == 0xFF ) ) {
|
||||
previousByte = tempBuffer[0];
|
||||
|
||||
inStream->read( tempBuffer, 1 );
|
||||
|
||||
fwrite( tempBuffer, 1, 1, tempFile );
|
||||
}
|
||||
|
||||
// end of jpeg stream reached.
|
||||
fclose( tempFile );
|
||||
delete [] tempBuffer;
|
||||
|
||||
|
||||
// the remainder of this method was mostly copied from
|
||||
// IJG's example.c
|
||||
|
||||
|
||||
|
||||
/* This struct contains the JPEG decompression parameters and pointers to
|
||||
* working space (which is allocated as needed by the JPEG library).
|
||||
*/
|
||||
struct jpeg_decompress_struct cinfo;
|
||||
/* We use our private extension JPEG error handler.
|
||||
* Note that this struct must live as long as the main JPEG parameter
|
||||
* struct, to avoid dangling-pointer problems.
|
||||
*/
|
||||
struct my_error_mgr jerr;
|
||||
/* More stuff */
|
||||
FILE * infile; /* source file */
|
||||
JSAMPARRAY buffer; /* Output row buffer */
|
||||
int row_stride; /* physical row width in output buffer */
|
||||
|
||||
/* In this example we want to open the input
|
||||
* file before doing anything else,
|
||||
* so that the setjmp() error recovery below can assume the file is open.
|
||||
* VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
|
||||
* requires it in order to read binary files.
|
||||
*/
|
||||
|
||||
if( ( infile = fopen( fileName, "rb" ) ) == NULL ) {
|
||||
printf( "can't open jpeg conversion temp file %s\n", fileName );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Step 1: allocate and initialize JPEG decompression object */
|
||||
|
||||
/* We set up the normal JPEG error routines, then override error_exit. */
|
||||
cinfo.err = jpeg_std_error(&jerr.pub);
|
||||
jerr.pub.error_exit = my_error_exit;
|
||||
/* Establish the setjmp return context for my_error_exit to use. */
|
||||
if( setjmp( jerr.setjmp_buffer ) ) {
|
||||
/* If we get here, the JPEG code has signaled an error.
|
||||
* We need to clean up the JPEG object,
|
||||
* close the input file, and return.
|
||||
*/
|
||||
jpeg_destroy_decompress( &cinfo );
|
||||
fclose( infile );
|
||||
printf( "error in decompressing jpeg from stream.\n" );
|
||||
return NULL;
|
||||
}
|
||||
/* Now we can initialize the JPEG decompression object. */
|
||||
jpeg_create_decompress( &cinfo );
|
||||
|
||||
/* Step 2: specify data source (eg, a file) */
|
||||
|
||||
jpeg_stdio_src( &cinfo, infile );
|
||||
|
||||
/* Step 3: read file parameters with jpeg_read_header() */
|
||||
|
||||
(void) jpeg_read_header( &cinfo, TRUE );
|
||||
/* We can ignore the return value from jpeg_read_header since
|
||||
* (a) suspension is not possible with the stdio data source, and
|
||||
* (b) we passed TRUE to reject a tables-only JPEG file as an error.
|
||||
* See libjpeg.doc for more info.
|
||||
*/
|
||||
|
||||
/* Step 4: set parameters for decompression */
|
||||
|
||||
/* In this example, we don't need to change any of the defaults set by
|
||||
* jpeg_read_header(), so we do nothing here.
|
||||
*/
|
||||
|
||||
/* Step 5: Start decompressor */
|
||||
|
||||
(void) jpeg_start_decompress( &cinfo );
|
||||
/* We can ignore the return value since suspension is not possible
|
||||
* with the stdio data source.
|
||||
*/
|
||||
|
||||
/* We may need to do some setup of our own at this point before reading
|
||||
* the data. After jpeg_start_decompress() we have the correct scaled
|
||||
* output image dimensions available, as well as the output colormap
|
||||
* if we asked for color quantization.
|
||||
* In this example, we need to make an output work buffer of the right size.
|
||||
*/
|
||||
/* JSAMPLEs per row in output buffer */
|
||||
|
||||
int imageWidth = cinfo.output_width;
|
||||
int imageHeight = cinfo.output_height;
|
||||
|
||||
// the return image with 3 channels
|
||||
Image *returnImage = new Image( imageWidth, imageHeight, 3, false );
|
||||
|
||||
// channels of returnImage,
|
||||
// which we will need to put pixel values into of
|
||||
double *redChannel = returnImage->getChannel(0);
|
||||
double *greenChannel = returnImage->getChannel(1);
|
||||
double *blueChannel = returnImage->getChannel(2);
|
||||
int currentIndex = 0;
|
||||
|
||||
|
||||
row_stride = cinfo.output_width * cinfo.output_components;
|
||||
/* Make a one-row-high sample array that
|
||||
* will go away when done with image
|
||||
*/
|
||||
buffer = ( *cinfo.mem->alloc_sarray )
|
||||
((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1 );
|
||||
|
||||
/* Step 6: while (scan lines remain to be read) */
|
||||
/* jpeg_read_scanlines(...); */
|
||||
|
||||
/* Here we use the library's state variable cinfo.output_scanline as the
|
||||
* loop counter, so that we don't have to keep track ourselves.
|
||||
*/
|
||||
int rowNumber = 0;
|
||||
double inv255 = 1.0 / 255.0;
|
||||
|
||||
while( cinfo.output_scanline < cinfo.output_height ) {
|
||||
/* jpeg_read_scanlines expects an array of pointers to scanlines.
|
||||
* Here the array is only one element long, but you could ask for
|
||||
* more than one scanline at a time if that's more convenient.
|
||||
*/
|
||||
(void) jpeg_read_scanlines( &cinfo, buffer, 1 );
|
||||
|
||||
// write the scanline into returnImage
|
||||
|
||||
int yOffset = rowNumber * cinfo.output_width;
|
||||
|
||||
// for each pixel in the row
|
||||
// copy it into the return image channels
|
||||
for( int p=0; p<cinfo.output_width; p++ ) {
|
||||
|
||||
// index into inImage
|
||||
int pixelIndex = p + yOffset;
|
||||
|
||||
// index into this row
|
||||
int startRowIndex = p * 3;
|
||||
|
||||
// red
|
||||
redChannel[ pixelIndex ] =
|
||||
buffer[0][ startRowIndex ] * inv255;
|
||||
// green
|
||||
greenChannel[ pixelIndex ] =
|
||||
buffer[0][ startRowIndex + 1 ] * inv255;
|
||||
// blue
|
||||
blueChannel[ pixelIndex ] =
|
||||
buffer[0][ startRowIndex + 2 ] * inv255;
|
||||
}
|
||||
|
||||
rowNumber++;
|
||||
}
|
||||
|
||||
/* Step 7: Finish decompression */
|
||||
|
||||
(void) jpeg_finish_decompress( &cinfo );
|
||||
/* We can ignore the return value since suspension is not possible
|
||||
* with the stdio data source.
|
||||
*/
|
||||
|
||||
/* Step 8: Release JPEG decompression object */
|
||||
|
||||
/* This is an important step since it will
|
||||
* release a good deal of memory.
|
||||
*/
|
||||
jpeg_destroy_decompress( &cinfo );
|
||||
|
||||
/* After finish_decompress, we can close the input file.
|
||||
* Here we postpone it until after no more JPEG errors are possible,
|
||||
* so as to simplify the setjmp error logic above. (Actually, I don't
|
||||
* think that jpeg_destroy can do an error exit,
|
||||
* but why assume anything...)
|
||||
*/
|
||||
fclose( infile );
|
||||
|
||||
/* At this point you may want to check to see whether any corrupt-data
|
||||
* warnings occurred (test whether jerr.pub.num_warnings is nonzero).
|
||||
*/
|
||||
|
||||
// delete this temporary file
|
||||
remove( fileName );
|
||||
|
||||
delete [] fileName;
|
||||
|
||||
/* And we're done! */
|
||||
return returnImage;
|
||||
}
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue