Archived

This topic is now archived and is closed to further replies.

Damocles

Any generous PNG users out there?

Recommended Posts

I''ve been messing around with PNG file loading and setting as an OpenGL texture and it''s driving me insane (well, more insane). I mnaaged to load the data using DevIL, but it won''t use the alpha data. The only way I managed to get alpha data ot load in correctly was when the image came out as only 2/3 of the whole image :/ I''m currently playing around with libpng but it''s beginning to do my nut in I was wondering if anyone would be kind enough to share a PNG load routine they have developed, before I go on a small killing spree.

Share this post


Link to post
Share on other sites
I wrote this a while ago. It still relies on the png file's width and height being multiples powers of two.

GLuint loadPngAsTexture(const std::string& filename)
{
std::string errmsg = "Couldn't load texture: " + filename;

std::FILE *f = std::fopen(filename.c_str(), "rb");
if (!f) throw std::runtime_error(errmsg);
png_structp pread = 0;
png_infop pinfo = 0;
png_byte* imgbytes = 0;
png_byte** rows = 0;

png_byte signature[8];
fread(signature, 1, 8, f);
if (png_sig_cmp(signature, 0, 8))
{
std::fclose(f);
throw std::runtime_error(errmsg);
}

pread = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
if (!pread)
{
std::fclose(f);
throw std::runtime_error(errmsg);
}

jmp_buf errbuf;
if (setjmp(errbuf) || setjmp(png_jmpbuf(pread)))
{
std::fclose(f);
png_destroy_read_struct(&pread, &pinfo, 0);
if (rows) delete[] rows;
if (imgbytes) delete[] imgbytes;
throw std::runtime_error(errmsg);
}

pinfo = png_create_info_struct(pread);
if (!pinfo) longjmp(errbuf, 1);

png_init_io(pread, f);
png_set_sig_bytes(pread, 8);
png_read_info(pread, pinfo);

int depth, color_type;
unsigned long width, height;
png_get_IHDR(pread, pinfo, &width, &height, &depth, &color_type, 0, 0, 0);

if (depth > 16) png_set_strip_16(pread);
if (!(color_type & PNG_COLOR_MASK_COLOR))
{
if (depth < 8) png_set_gray_1_2_4_to_8(pread);
png_set_gray_to_rgb(pread);
}
if (png_get_valid(pread, pinfo, PNG_INFO_tRNS))
png_set_tRNS_to_alpha(pread);
if (color_type & PNG_COLOR_MASK_ALPHA) png_set_strip_alpha(pread);

png_read_update_info(pread, pinfo);
png_get_IHDR(pread, pinfo, &width, &height, &depth, &color_type, 0, 0, 0);

png_uint_32 row_bytes = png_get_rowbytes(pread, pinfo);
int img_size = row_bytes * height;

imgbytes = new (std::nothrow) png_byte[img_size];
rows = new (std::nothrow) png_byte*[height];

if (imgbytes == 0 || rows == 0) longjmp(errbuf, 1);

for (unsigned int i = 0; i < height; ++i)
rows[i] = imgbytes + (height - i - 1) * row_bytes;

png_read_image(pread, rows);
png_read_end(pread, 0);

GLuint texId;
glGenTextures(1, &texId);
glBindTexture(GL_TEXTURE_2D, texId);

glTexImage2D(GL_TEXTURE_2D, 0, 3, width, height, 0, GL_RGB,
GL_UNSIGNED_BYTE, imgbytes );

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

std::fclose(f);
png_destroy_read_struct(&pread, &pinfo, 0);
delete[] rows;
delete[] imgbytes;
return texId;
}


[edited by - Beer Hunter on August 14, 2003 7:53:19 PM]

Share this post


Link to post
Share on other sites
Thanks for the code, but it''s falling down at the same place I was having trouble before. It dies at the line:

png_read_info(pread, pinfo);

Using your code given, the only modifications I made was removing the namespace (as I''m not using them in this particular project) and the string class usage (I''m funny like that, I like the old character arrays).

GLuint LoadPNG(char* filename)
{
FILE *f = fopen(filename, "rb");
if (!f)
{
Log<<"ERROR: unable to open png file "<<filename<<endl;
return -1;
}
png_structp pread = 0;
png_infop pinfo = 0;
png_byte* imgbytes = 0;
png_byte** rows = 0;
png_byte signature[8];
fread(signature, 1, 8, f);
if (png_sig_cmp(signature, 0, 8))
{
fclose(f);
return -1;
}
pread = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
if (!pread)
{
fclose(f);
return -1;
}
jmp_buf errbuf;
if (setjmp(errbuf) || setjmp(png_jmpbuf(pread)))
{
fclose(f);
png_destroy_read_struct(&pread, &pinfo, 0);
if (rows) delete[] rows;
if (imgbytes) delete[] imgbytes;
return -1;
}
pinfo = png_create_info_struct(pread);
if (!pinfo)
longjmp(errbuf, 1);
png_init_io(pread, f);
png_set_sig_bytes(pread, 8);
png_read_info(pread, pinfo);
int depth, color_type;
unsigned long width, height;
png_get_IHDR(pread, pinfo, &width, &height, &depth, &color_type, 0, 0, 0);
if (depth > 16) png_set_strip_16(pread);
if (!(color_type & PNG_COLOR_MASK_COLOR))
{
if (depth < 8) png_set_gray_1_2_4_to_8(pread);
png_set_gray_to_rgb(pread);
}
if (png_get_valid(pread, pinfo, PNG_INFO_tRNS))
png_set_tRNS_to_alpha(pread);
if (color_type & PNG_COLOR_MASK_ALPHA) png_set_strip_alpha(pread);
png_read_update_info(pread, pinfo);
png_get_IHDR(pread, pinfo, &width, &height, &depth, &color_type, 0, 0, 0);
png_uint_32 row_bytes = png_get_rowbytes(pread, pinfo);
int img_size = row_bytes * height;
imgbytes = new png_byte[img_size];
rows = new png_byte*[height];
if (imgbytes == 0 || rows == 0) longjmp(errbuf, 1);
for (unsigned int i = 0; i < height; ++i)
rows[i] = imgbytes + (height - i - 1) * row_bytes;
png_read_image(pread, rows);
png_read_end(pread, 0); GLuint texId;
glGenTextures(1, &texId);
glBindTexture(GL_TEXTURE_2D, texId);
glTexImage2D(GL_TEXTURE_2D, 0, 3, width, height, 0, GL_RGB,
GL_UNSIGNED_BYTE, imgbytes);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
fclose(f);
png_destroy_read_struct(&pread, &pinfo, 0);
delete[] rows;
delete[] imgbytes;
return texId;
}


The file opens fine and all other operations work fine until that one. I''ve tried creating the png file in photoshop and paint shop pro, so I''m going to assume it''s not the file. Anyone care to hazard a guess as to what the problem is?

Share this post


Link to post
Share on other sites
I''ve tested your modified code, and it loaded this without complaint. The only other thing I can think of is that libpng might not be set up properly on your computer.

Share this post


Link to post
Share on other sites
It dies on that file too. So are there any special setup requirements for libpng? I''m trying to run it in ms vc6. I built the project and moved libpng13.lib, libpng13.dll and zlib.dll into the program directory. Added libpng13.lib to my project and compiled.

Are there are any special requirements not mentioned in the libgpng setup docs? It does say that most users will have to modify the code to get it to work, but doesn''t give any clue as to what needs modifying :/

Share this post


Link to post
Share on other sites
OK, you asked for it!

Disclaimer : I''ve just ripped this code from a project in progress. I know it''s not a complete PNG loader, I know it supports 24bit RGBA images, I don''t remember if it supports anything else. If you need a different format png you may well be able to modify the code to support it (if you can understand the code). This code has not been optimised and I''m pretty sure there''s some legacy debugging stuff in there (i.e. if statements that can never be true). Hope this helps!

Note - I haven''t included all the classes needed to get this to compile. Replace String with your preferred String class. Bitmap is just a class that holds an array of image data and some information

HuffmanCode.h:

class HuffmanCode{

private:

HuffmanCode* childTree1;
HuffmanCode* childTree2;
GLuint childValue1;
GLuint childValue2;

public:

HuffmanCode();
HuffmanCode(GLubyte* lengths, GLuint numberOfCodes);
GLboolean child1IsTree();
GLboolean child2IsTree();
HuffmanCode* getChildTree1();
HuffmanCode* getChildTree2();
GLuint getChildValue1();
GLuint getChildValue2();
GLvoid setChild1(HuffmanCode* childTree);
GLvoid setChild1(GLuint childValue);
GLvoid setChild2(HuffmanCode* childTree);
GLvoid setChild2(GLuint childValue);

};


HuffmanCode.cpp:

HuffmanCode::HuffmanCode(){
childTree1 = null;
childTree2 = null;
}

HuffmanCode::HuffmanCode(GLubyte* lengths, GLuint numberOfCodes){
childTree1 = null;
childTree2 = null;
GLuint* frequencies = new GLuint[16];
GLuint* offsets = new GLuint[16];
GLuint n = 0;
while (n < 16){
frequencies[n] = 0;
offsets[n] = 0;
n++;
}
GLuint codeNumber = 0;
while (codeNumber < numberOfCodes){
frequencies[lengths[codeNumber]]++;
codeNumber++;
}
GLuint code = 0;
frequencies[0] = 0;
codeNumber = 1;
while (codeNumber < 16){
code = (code + frequencies[codeNumber - 1]) << 1;
offsets[codeNumber] = code;
codeNumber++;
}
codeNumber = 0;
while (codeNumber < numberOfCodes){
GLuint length = lengths[codeNumber];
if (length != 0){
GLuint value = offsets[lengths[codeNumber]];
HuffmanCode* tree = this;
while (length > 1){
if (((value >> (length - 1)) & 0x1) == 0){
if (tree->child1IsTree()){
tree = tree->getChildTree1();
}
else {
tree->setChild1(new HuffmanCode());
tree = tree->getChildTree1();
}
}
else {
if (tree->child2IsTree()){
tree = tree->getChildTree2();
}
else {
tree->setChild2(new HuffmanCode());
tree = tree->getChildTree2();
}
}
length--;
}
if ((value & 0x1) == 0){
tree->setChild1(codeNumber);
}
else {
tree->setChild2(codeNumber);
}
offsets[lengths[codeNumber]]++;
}
codeNumber++;
}
delete[] frequencies;
delete[] offsets;
}

GLboolean HuffmanCode::child1IsTree(){
return childTree1 != null;
}

GLboolean HuffmanCode::child2IsTree(){
return childTree2 != null;
}

HuffmanCode* HuffmanCode::getChildTree1(){
return childTree1;
}

HuffmanCode* HuffmanCode::getChildTree2(){
return childTree2;
}

GLuint HuffmanCode::getChildValue1(){
return childValue1;
}

GLuint HuffmanCode::getChildValue2(){
return childValue2;
}

GLvoid HuffmanCode::setChild1(HuffmanCode* childTree){
childTree1 = childTree;
}

GLvoid HuffmanCode::setChild1(GLuint childValue){
childValue1 = childValue;
}

GLvoid HuffmanCode::setChild2(HuffmanCode* childTree){
childTree2 = childTree;
}

GLvoid HuffmanCode::setChild2(GLuint childValue){
childValue2 = childValue;
}


TextureLoaderPNG.h:

class TextureLoaderPNG{

private:

class PngIDATData{

private:

static const GLubyte codeLengthCodeOrder[];
static const GLubyte defaultDistanceDictionaryLengths[];
static const GLuint defaultDistanceDictionarySize;
static const HuffmanCode* defaultDistanceDictionary;
static const GLubyte defaultLiteralOrLengthDictionaryLengths[];
static const GLuint defaultLiteralOrLengthDictionarySize;
static const HuffmanCode* defaultLiteralOrLengthDictionary;

static const GLubyte deflateCompressedDataBlockState;
static const GLubyte deflateFinishedState;
static const GLubyte deflateStartOfHuffmanCodeBlockState;
static const GLubyte deflateStartOfBlockState;
static const GLubyte deflateUncompressedDataBlockDataState;

static const GLubyte deflateDynamicHuffmanCompressionValue;
static const GLubyte deflateHuffmanCompressionValue;
static const GLubyte deflateNoCompressionValue;
static const GLubyte deflateReservedCompressionValue;

static const GLuint zlibAdler32Length;
static const GLuint zlibCMFByteOffset;
static const GLuint zlibFLGByteOffset;
static const GLuint zlibHeaderLength;

std::ifstream& sourceStream;
Bitmap* destinationStream;
GLuint byteOffset;
GLubyte bitOffset;
GLuint totalBytes;
GLubyte* data;
GLuint windowSize;
HuffmanCode* literalOrLengthDictionary;
HuffmanCode* distanceDictionary;
GLuint outputOffset;
GLubyte* filters;
GLuint state;
GLboolean isLastBlock;
GLuint blockLength;
GLboolean readZlibHeader;
GLboolean shouldProcessToEnd;
GLubyte bytesPerPixel;

GLvoid applyAverageFilter(GLuint scanline);
GLvoid applyPaethFilter(GLuint scanline);
GLvoid applySubFilter(GLuint scanline);
GLvoid applyUpFilter(GLuint scanline);
GLubyte paethPredictor(GLint prior1, GLint prior2, GLint prior3);
GLuint readBitsLeftToRight(GLuint numberOfBits);
GLuint readBitsRightToLeft(GLuint numberOfBits);
GLuint readHuffmanCode(HuffmanCode* dictionary);
GLvoid readZlibFlags();

public:

static const GLubyte pngFilterAverage;
static const GLubyte pngFilterNone;
static const GLubyte pngFilterPaeth;
static const GLubyte pngFilterSub;
static const GLubyte pngFilterUp;

PngIDATData(std::ifstream& textureFile, Bitmap* bitmap);
GLvoid applyFilters();
GLboolean finished();
GLvoid ignore(GLuint bytesToIgnore);
GLvoid ignoreToByteBoundary();
GLvoid process();
GLvoid processToEnd();
GLubyte readBit();
GLuint readDistanceCode();
GLvoid readFile(GLuint bytesToRead);
GLboolean readHuffmanCodes();
GLuint readLengthCode(GLuint initialCode);
GLuint readLiteralOrLengthHuffmanCode();
GLubyte readTwoBitNumberLeftToRight();
GLubyte readOneByteNumberLeftToRight();
GLuint readTwoByteNumber();
GLuint remainingBits();
GLuint remainingBytes();
GLvoid setWindowSize(GLuint newWindowSize);
GLvoid writeByteToBitmap(GLubyte value);
GLuint writeStreamToBitmap(GLuint length);
GLvoid writeStreamToBitmap(GLuint distance, GLuint length);

};

static const GLuint deflateBlockLengthHeaderLength;

static const GLuint pngChunkHeaderLength;
static const GLuint pngChunkHeaderNameLength;
static const GLuint pngColourDepthByteOffset;
static const GLuint pngColourTypeByteOffset;
static const GLuint pngCompressionMethodByteOffset;
static const GLuint pngFilterMethodByteOffset;
static const GLuint pngHeaderLength;
static const GLuint pngHeightByteOffset;
static const GLuint pngInterlaceMethodByteOffset;
static const GLuint pngWidthByteOffset;

static const GLubyte pngIDATChunkHeader[];
static const GLubyte pngIENDChunkHeader[];
static const GLubyte pngIHDRChunkHeader[];
static const GLubyte pngPLTEChunkHeader[];
static const GLubyte pngRequiredHeader[];


static GLvoid checkPngColourTypeAndBitDepth(GLubyte colourType, GLubyte colourDepth);
static GLuint extractUnsignedInt(GLubyte* sizeBytes, GLuint offset);
static GLuint extractUnsignedIntLittleEndian(GLubyte* sizeBytes, GLuint offset);
static Bitmap* loadPNG(char* filename);
static GLvoid readPngPLTEChunk(std::ifstream& textureFile, GLuint chunkLength, GLubyte colourDepth, GLubyte colourType);
static GLvoid readPngIDATChunk(GLuint chunkLength, PngIDATData* dataStream);

public:

static Bitmap* loadImage(char* filename);

};


TextureLoaderPNG.cpp:

const GLuint TextureLoaderPNG::deflateBlockLengthHeaderLength = 4;

const GLuint TextureLoaderPNG::pngChunkHeaderLength = 8;
const GLuint TextureLoaderPNG::pngChunkHeaderNameLength = 4;
const GLuint TextureLoaderPNG::pngColourDepthByteOffset = 8;
const GLuint TextureLoaderPNG::pngColourTypeByteOffset = 9;
const GLuint TextureLoaderPNG::pngCompressionMethodByteOffset = 10;
const GLuint TextureLoaderPNG::pngFilterMethodByteOffset = 11;
const GLuint TextureLoaderPNG::pngHeaderLength = 8;
const GLuint TextureLoaderPNG::pngHeightByteOffset = 4;
const GLuint TextureLoaderPNG::pngInterlaceMethodByteOffset = 12;
const GLuint TextureLoaderPNG::pngWidthByteOffset = 0;

const GLubyte TextureLoaderPNG::pngIDATChunkHeader[] = {''I'', ''D'', ''A'', ''T''};
const GLubyte TextureLoaderPNG::pngIENDChunkHeader[] = {''I'', ''E'', ''N'', ''D''};
const GLubyte TextureLoaderPNG::pngIHDRChunkHeader[] = {''I'', ''H'', ''D'', ''R''};
const GLubyte TextureLoaderPNG::pngPLTEChunkHeader[] = {''P'', ''L'', ''T'', ''E''};
const GLubyte TextureLoaderPNG::pngRequiredHeader[] = {''\211'', ''P'', ''N'', ''G'', ''\r'', ''\n'', ''\032'', ''\n''};

const GLubyte TextureLoaderPNG::PngIDATData::codeLengthCodeOrder[] = {
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
};
const GLubyte TextureLoaderPNG::PngIDATData::defaultDistanceDictionaryLengths[] = {
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
};
const GLuint TextureLoaderPNG::PngIDATData::defaultDistanceDictionarySize = 30;
const HuffmanCode* TextureLoaderPNG::PngIDATData::defaultDistanceDictionary = null;
const GLubyte TextureLoaderPNG::PngIDATData::defaultLiteralOrLengthDictionaryLengths[] = {
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8
};
const GLuint TextureLoaderPNG::PngIDATData::defaultLiteralOrLengthDictionarySize = 288;
const HuffmanCode* TextureLoaderPNG::PngIDATData::defaultLiteralOrLengthDictionary = null;

const GLubyte TextureLoaderPNG::PngIDATData::deflateCompressedDataBlockState = 0x6;
const GLubyte TextureLoaderPNG::PngIDATData::deflateFinishedState = 0xA;
const GLubyte TextureLoaderPNG::PngIDATData::deflateStartOfHuffmanCodeBlockState = 0x9;
const GLubyte TextureLoaderPNG::PngIDATData::deflateStartOfBlockState = 0x1;
const GLubyte TextureLoaderPNG::PngIDATData::deflateUncompressedDataBlockDataState = 0x5;

const GLubyte TextureLoaderPNG::PngIDATData::deflateDynamicHuffmanCompressionValue = 0x2;
const GLubyte TextureLoaderPNG::PngIDATData::deflateHuffmanCompressionValue = 0x1;
const GLubyte TextureLoaderPNG::PngIDATData::deflateNoCompressionValue = 0x0;
const GLubyte TextureLoaderPNG::PngIDATData::deflateReservedCompressionValue = 0x3;

const GLuint TextureLoaderPNG::PngIDATData::zlibAdler32Length = 4;
const GLuint TextureLoaderPNG::PngIDATData::zlibCMFByteOffset = 0;
const GLuint TextureLoaderPNG::PngIDATData::zlibFLGByteOffset = 1;
const GLuint TextureLoaderPNG::PngIDATData::zlibHeaderLength = 2;

GLvoid TextureLoaderPNG::PngIDATData::applyAverageFilter(GLuint scanline){
GLuint pixelNumber = 0;
GLuint sum;
GLubyte* imageData = destinationStream->getData();
GLuint offset1 = ((scanline - 1) * destinationStream->getWidth() * bytesPerPixel);
GLuint offset2 = (scanline * destinationStream->getWidth() * bytesPerPixel);
while (pixelNumber < destinationStream->getWidth() * bytesPerPixel){
if (scanline > 0){
sum = imageData[offset1 + pixelNumber];
}
else {
sum = 0;
}
if (pixelNumber >= bytesPerPixel){
sum += imageData[offset2 + pixelNumber - bytesPerPixel];
}
imageData[offset2 + pixelNumber] += (sum >> 1);
pixelNumber++;
}
}

GLvoid TextureLoaderPNG::PngIDATData::applyPaethFilter(GLuint scanline){
GLuint pixelNumber = 0;
GLint prior1;
GLint prior2;
GLint prior3;
GLubyte* imageData = destinationStream->getData();
GLuint offset1 = ((scanline - 1) * destinationStream->getWidth() * bytesPerPixel);
GLuint offset2 = (scanline * destinationStream->getWidth() * bytesPerPixel);
while (pixelNumber < destinationStream->getWidth() * bytesPerPixel){
if (scanline > 0){
prior2 = imageData[offset1 + pixelNumber];
if (pixelNumber >= bytesPerPixel){
prior3 = imageData[offset1 + pixelNumber - bytesPerPixel];
}
else {
prior3 = 0;
}
}
else {
prior2 = 0;
prior3 = 0;
}
if (pixelNumber >= bytesPerPixel){
prior1 = imageData[offset2 + pixelNumber - bytesPerPixel];
}
else {
prior1 = 0;
}
imageData[offset2 + pixelNumber] += paethPredictor(prior1, prior2, prior3);
pixelNumber++;
}
}

GLvoid TextureLoaderPNG::PngIDATData::applySubFilter(GLuint scanline){
GLuint pixelNumber = bytesPerPixel;
GLubyte* imageData = destinationStream->getData();
GLuint offset = (scanline * destinationStream->getWidth() * bytesPerPixel);
while (pixelNumber < destinationStream->getWidth() * bytesPerPixel){
imageData[offset + pixelNumber] += imageData[offset + pixelNumber - bytesPerPixel];
pixelNumber++;
}
}

GLvoid TextureLoaderPNG::PngIDATData::applyUpFilter(GLuint scanline){
if (scanline == 0){
return;
}
GLuint pixelNumber = 0;
GLubyte* imageData = destinationStream->getData();
GLuint offset1 = ((scanline - 1) * destinationStream->getWidth() * bytesPerPixel);
GLuint offset2 = (scanline * destinationStream->getWidth() * bytesPerPixel);
while (pixelNumber < destinationStream->getWidth() * bytesPerPixel){
imageData[offset2 + pixelNumber] += imageData[offset1 + pixelNumber];
pixelNumber++;
}
}

GLubyte TextureLoaderPNG::PngIDATData::paethPredictor(GLint prior1, GLint prior2, GLint prior3){
GLint predict1 = prior1 + prior2 - prior3;
GLint predict2 = abs(predict1 - prior1);
GLint predict3 = abs(predict1 - prior2);
GLint predict4 = abs(predict1 - prior3);
if (predict2 <= predict3 && predict2 <= predict4){
return prior1;
}
else if (predict3 <= predict4){
return prior2;
}
else {
return prior3;
}
}

GLuint TextureLoaderPNG::PngIDATData::readBitsLeftToRight(GLuint numberOfBits){
GLuint value = 0;
GLuint bitNumber = numberOfBits;
while (bitNumber > 0){
if (byteOffset == totalBytes){
readFile(1);
}
value >>= 1;
value += ((data[byteOffset] >> bitOffset) & 0x1) << (numberOfBits - 1);
bitOffset++;
if (bitOffset == 8){
bitOffset = 0;
byteOffset++;
}
bitNumber--;
}
return value;
}

GLuint TextureLoaderPNG::PngIDATData::readBitsRightToLeft(GLuint numberOfBits){
GLuint value = 0;
while (numberOfBits > 0){
if (byteOffset == totalBytes){
readFile(1);
}
value <<= 1;
value += ((data[byteOffset] >> bitOffset) & 0x1);
bitOffset++;
if (bitOffset == 8){
bitOffset = 0;
byteOffset++;
}
numberOfBits--;
}
return value;
}

GLuint TextureLoaderPNG::PngIDATData::readHuffmanCode(HuffmanCode* dictionary){
while (true){
if (readBit() == 0){
if (dictionary->child1IsTree()){
dictionary = dictionary->getChildTree1();
}
else {
return dictionary->getChildValue1();
}
}
else {
if (dictionary->child2IsTree()){
dictionary = dictionary->getChildTree2();
}
else {
return dictionary->getChildValue2();
}
}
}
}

GLvoid TextureLoaderPNG::PngIDATData::readZlibFlags(){
GLubyte* zlibFlags = new GLubyte[zlibHeaderLength];
zlibFlags[zlibCMFByteOffset] = sourceStream.get();
zlibFlags[zlibFLGByteOffset] = sourceStream.get();
if ((zlibFlags[zlibCMFByteOffset] & 0x0F) != 0x08){
MessageBox(null, "invalid png image - zlib compression method must be 8", "failed", MB_OK | MB_ICONEXCLAMATION);
throw 7;
}
if (((zlibFlags[zlibCMFByteOffset] & 0xF0) >> 4) > 7){
MessageBox(null, "invalid png image - zlib window size must be smaller than 8", "failed", MB_OK | MB_ICONEXCLAMATION);
throw 7;
}
setWindowSize(0x1 << (((zlibFlags[zlibCMFByteOffset] & 0xF0) >> 4) + 8));
if ((((GLuint)zlibFlags[zlibCMFByteOffset] * 256) + zlibFlags[zlibFLGByteOffset]) % 31 != 0){
MessageBox(null, "invalid png image - zlib FCHECK failed", "failed", MB_OK | MB_ICONEXCLAMATION);
throw 7;
}
if ((zlibFlags[zlibFLGByteOffset] & 0x20) == 0x20){
MessageBox(null, "invalid png image - preset dictionary not allowed in zlib data", "failed", MB_OK | MB_ICONEXCLAMATION);
throw 7;
}
delete[] zlibFlags;
readZlibHeader = true;
}

const GLubyte TextureLoaderPNG::PngIDATData::pngFilterAverage = 0x3;
const GLubyte TextureLoaderPNG::PngIDATData::pngFilterNone = 0x0;
const GLubyte TextureLoaderPNG::PngIDATData::pngFilterPaeth = 0x4;
const GLubyte TextureLoaderPNG::PngIDATData::pngFilterSub = 0x1;
const GLubyte TextureLoaderPNG::PngIDATData::pngFilterUp = 0x2;

TextureLoaderPNG::PngIDATData::PngIDATData(std::ifstream& textureFile, Bitmap* bitmap) : sourceStream(textureFile){
destinationStream = bitmap;
byteOffset = 0;
bitOffset = 0;
totalBytes = 0;
data = null;
windowSize = 0;
if (defaultDistanceDictionary == null){
const_cast<HuffmanCode*>(defaultDistanceDictionary) = new HuffmanCode(const_cast<GLubyte*>(defaultDistanceDictionaryLengths), defaultDistanceDictionarySize);
const_cast<HuffmanCode*>(defaultLiteralOrLengthDictionary) = new HuffmanCode(const_cast<GLubyte*>(defaultLiteralOrLengthDictionaryLengths), defaultLiteralOrLengthDictionarySize);
}
distanceDictionary = const_cast<HuffmanCode*>(defaultDistanceDictionary);
literalOrLengthDictionary = const_cast<HuffmanCode*>(defaultLiteralOrLengthDictionary);
filters = new GLubyte[bitmap->getHeight()];
outputOffset = 0;
state = deflateStartOfBlockState;
isLastBlock = false;
readZlibHeader = false;
shouldProcessToEnd = false;
bytesPerPixel = bitmap->getBPP() / 8;
}

GLvoid TextureLoaderPNG::PngIDATData::applyFilters(){
GLuint scanLine = 0;
while (scanLine < destinationStream->getHeight()){
if (filters[scanLine] == pngFilterSub){
applySubFilter(scanLine);
}
else if (filters[scanLine] == pngFilterUp){
applyUpFilter(scanLine);
}
else if (filters[scanLine] == pngFilterAverage){
applyAverageFilter(scanLine);
}
else if (filters[scanLine] == pngFilterPaeth){
applyPaethFilter(scanLine);
}
else if (filters[scanLine] != pngFilterNone){
MessageBox(null, "invalid png image - unrecognised filter", "failed", MB_OK | MB_ICONINFORMATION);
throw 7;
}
scanLine++;
}
}

GLboolean TextureLoaderPNG::PngIDATData::finished(){
return state == deflateFinishedState;
}

GLvoid TextureLoaderPNG::PngIDATData::ignore(GLuint bytesToIgnore){
byteOffset += bytesToIgnore;
}

GLvoid TextureLoaderPNG::PngIDATData::ignoreToByteBoundary(){
if (bitOffset > 0){
bitOffset = 0;
byteOffset++;
}
}

GLvoid TextureLoaderPNG::PngIDATData::process(){
GLboolean readData = true;
while ((readData && remainingBits() >= 50) || (shouldProcessToEnd && state != deflateFinishedState)){
if (state == deflateStartOfBlockState && !isLastBlock){
isLastBlock = readBit();
GLubyte compressionMethod = readTwoBitNumberLeftToRight();
if (compressionMethod == deflateNoCompressionValue){
ignoreToByteBoundary();
blockLength = readTwoByteNumber();
ignore(2);
state = deflateUncompressedDataBlockDataState;
}
else if (compressionMethod == deflateHuffmanCompressionValue){
if (literalOrLengthDictionary != defaultLiteralOrLengthDictionary){
delete literalOrLengthDictionary;
}
if (distanceDictionary != defaultDistanceDictionary){
delete distanceDictionary;
}
distanceDictionary = const_cast<HuffmanCode*>(defaultDistanceDictionary);
literalOrLengthDictionary = const_cast<HuffmanCode*>(defaultLiteralOrLengthDictionary);
state = deflateCompressedDataBlockState;
}
else if (compressionMethod == deflateDynamicHuffmanCompressionValue){
state = deflateStartOfHuffmanCodeBlockState;
}
else {
MessageBox(null, "invalid png image - invalid DEFLATE compression method", "failed", MB_OK | MB_ICONEXCLAMATION);
throw 7;
}
}
else if (state == deflateUncompressedDataBlockDataState){
blockLength -= writeStreamToBitmap(blockLength);
if (blockLength == 0){
state = deflateStartOfBlockState;
}
}
else if (state == deflateCompressedDataBlockState){
while (state == deflateCompressedDataBlockState && (remainingBits() >= 50 || shouldProcessToEnd)){
GLuint value = readLiteralOrLengthHuffmanCode();
if (value < 256){
writeByteToBitmap(value);
}
else if (value == 256){
state = deflateStartOfBlockState;
}
else {
value = readLengthCode(value);
GLuint distance = readDistanceCode();
writeStreamToBitmap(distance, value);
}
}
}
else if (state == deflateStartOfHuffmanCodeBlockState){
readData = readHuffmanCodes();
if (readData){
state = deflateCompressedDataBlockState;
}
}
else if (isLastBlock && state == deflateStartOfBlockState){
ignore(zlibAdler32Length);
state = deflateFinishedState;
}
}
}

GLvoid TextureLoaderPNG::PngIDATData::processToEnd(){
shouldProcessToEnd = true;
process();
}

GLubyte TextureLoaderPNG::PngIDATData::readBit(){
return readBitsLeftToRight(1);
}

GLuint TextureLoaderPNG::PngIDATData::readDistanceCode(){
GLuint code = readHuffmanCode(distanceDictionary);
GLuint distance = code;
if (code > 4){
distance += (code - 4);
}
if (code > 6){
distance += (code - 6) * 2;
}
if (code > 8){
distance += (code - 8) * 4;
}
if (code > 10){
distance += (code - 10) * 8;
}
if (code > 12){
distance += (code - 12) * 16;
}
if (code > 14){
distance += (code - 14) * 32;
}
if (code > 16){
distance += (code - 16) * 64;
}
if (code > 18){
distance += (code - 18) * 128;
}
if (code > 20){
distance += (code - 20) * 256;
}
if (code > 22){
distance += (code - 22) * 512;
}
if (code > 24){
distance += (code - 24) * 1024;
}
if (code > 26){
distance += (code - 26) * 2048;
}
if (code > 28){
distance += (code - 28) * 4096;
}
if (code < 2){
return distance + 1;
}
return distance + readBitsLeftToRight((code - 2) >> 1) + 1;
}

GLvoid TextureLoaderPNG::PngIDATData::readFile(GLuint bytesToRead){
if (!readZlibHeader){
if (bytesToRead < 2){
MessageBox(null, "error - Cannot read zlib header", "failed", MB_OK | MB_ICONEXCLAMATION);
throw 7;
}
readZlibFlags();
bytesToRead -= 2;
}
while (bytesToRead > 0){
if (bytesToRead < (2 * windowSize) - totalBytes){
sourceStream.read(data + totalBytes, bytesToRead);
totalBytes += bytesToRead;
bytesToRead = 0;
}
else {
sourceStream.read(data + totalBytes, (2 * windowSize) - totalBytes);
bytesToRead -= (2 * windowSize) - totalBytes;
totalBytes = 2 * windowSize;
process();
std::memcpy(data, data + windowSize, windowSize * sizeof(GLubyte));
byteOffset -= windowSize;
totalBytes = windowSize;
}
}
}

GLboolean TextureLoaderPNG::PngIDATData::readHuffmanCodes(){
if (remainingBytes() < 884 && !shouldProcessToEnd){
return false;
}
GLuint numberOfLiteralOrLengthCodes = 257 + readBitsLeftToRight(5);
GLuint numberOfDistanceCodes = 1 + readBitsLeftToRight(5);
GLuint numberOfCodeLengthCodes = 4 + readBitsLeftToRight(4);
GLuint codeLengthCodeNumber = 0;
GLubyte* codeLengthCodes = new GLubyte[19];
while (codeLengthCodeNumber < 19){
codeLengthCodes[codeLengthCodeNumber] = 0;
codeLengthCodeNumber++;
}
codeLengthCodeNumber = 0;
while (codeLengthCodeNumber < numberOfCodeLengthCodes){
codeLengthCodes[codeLengthCodeOrder[codeLengthCodeNumber]] = readBitsLeftToRight(3);
codeLengthCodeNumber++;
}
HuffmanCode* codeCode = new HuffmanCode(codeLengthCodes, 19);
GLuint codeNumber = 0;
GLuint code;
GLubyte* literalOrLengthCodeLengths = new GLubyte[numberOfLiteralOrLengthCodes];
GLubyte* distanceCodeLengths = new GLubyte[numberOfDistanceCodes];
while (codeNumber < numberOfLiteralOrLengthCodes + numberOfDistanceCodes){
code = readHuffmanCode(codeCode);
if (code < 16){
if (codeNumber < numberOfLiteralOrLengthCodes){
literalOrLengthCodeLengths[codeNumber] = code;
}
else {
distanceCodeLengths[codeNumber - numberOfLiteralOrLengthCodes] = code;
}
codeNumber++;
}
else {
GLuint repeats;
if (code == 16){
if (codeNumber < numberOfLiteralOrLengthCodes){
code = literalOrLengthCodeLengths[codeNumber - 1];
}
else {
code = distanceCodeLengths[(codeNumber - numberOfLiteralOrLengthCodes) - 1];
}
repeats = 3 + readBitsLeftToRight(2);
}
else if (code == 17){
code = 0;
repeats = 3 + readBitsLeftToRight(3);
}
else {
code = 0;
repeats = 11 + readBitsLeftToRight(7);
}
while (repeats > 0){
if (codeNumber < numberOfLiteralOrLengthCodes){
literalOrLengthCodeLengths[codeNumber] = code;
}
else {
distanceCodeLengths[codeNumber - numberOfLiteralOrLengthCodes] = code;
}
codeNumber++;
repeats--;
}
}
}
if (literalOrLengthDictionary != defaultLiteralOrLengthDictionary){
delete literalOrLengthDictionary;
}
if (distanceDictionary != defaultDistanceDictionary){
delete distanceDictionary;
}
literalOrLengthDictionary = new HuffmanCode(literalOrLengthCodeLengths, numberOfLiteralOrLengthCodes);
distanceDictionary = new HuffmanCode(distanceCodeLengths, numberOfDistanceCodes);
return true;
}

GLuint TextureLoaderPNG::PngIDATData::readLengthCode(GLuint initialCode){
if (initialCode == 285){
return 258;
}
GLuint length = initialCode;
if (initialCode > 265){
length += (initialCode - 265);
}
if (initialCode > 269){
length += (initialCode - 269) * 2;
}
if (initialCode > 273){
length += (initialCode - 273) * 4;
}
if (initialCode > 277){
length += (initialCode - 277) * 8;
}
if (initialCode > 281){
length += (initialCode - 281) * 16;
}
if (initialCode < 265){
return length - 254;
}
return length + readBitsLeftToRight((initialCode - 261) >> 2) - 254;
}

GLuint TextureLoaderPNG::PngIDATData::readLiteralOrLengthHuffmanCode(){
return readHuffmanCode(literalOrLengthDictionary);
}

GLubyte TextureLoaderPNG::PngIDATData::readOneByteNumberLeftToRight(){
return readBitsLeftToRight(8);
}

GLubyte TextureLoaderPNG::PngIDATData::readTwoBitNumberLeftToRight(){
return readBitsLeftToRight(2);
}

GLuint TextureLoaderPNG::PngIDATData::readTwoByteNumber(){
return readBitsLeftToRight(16);
}

GLuint TextureLoaderPNG::PngIDATData::remainingBits(){
return ((totalBytes - byteOffset) * 8) - bitOffset;
}

GLuint TextureLoaderPNG::PngIDATData::remainingBytes(){
if (bitOffset == 0){
return totalBytes - byteOffset;
}
else {
return totalBytes - (byteOffset + 1);
}
}

GLvoid TextureLoaderPNG::PngIDATData::setWindowSize(GLuint newWindowSize){
if (data != null){
MessageBox(null, "invalid png image - cannot set window size more than once", "failed", MB_OK | MB_ICONEXCLAMATION);
throw 7;
}
data = new GLubyte[newWindowSize * 2];
windowSize = newWindowSize;
totalBytes = 0;
byteOffset = 0;
bitOffset = 0;
}

GLvoid TextureLoaderPNG::PngIDATData::writeByteToBitmap(GLubyte value){
if (outputOffset % ((destinationStream->getWidth() * bytesPerPixel) + 1) == 0){
if (value > 4){
std::cout << "filter corrupt\n";
}
filters[outputOffset / ((destinationStream->getWidth() * bytesPerPixel) + 1)] = value;
}
else {
destinationStream->getData()[outputOffset - ((outputOffset / ((destinationStream->getWidth() * bytesPerPixel) + 1)) + 1)] = value;
}
outputOffset++;
}

GLuint TextureLoaderPNG::PngIDATData::writeStreamToBitmap(GLuint length){
GLuint bytesWritten = 0;
while (bytesWritten < length && remainingBytes() > 0){
writeByteToBitmap(data[byteOffset]);
byteOffset++;
bytesWritten++;
}
return bytesWritten;
}

GLvoid TextureLoaderPNG::PngIDATData::writeStreamToBitmap(GLuint distance, GLuint length){
if (distance > outputOffset){
MessageBox(null, "invalid png image - offset too large", "failed", MB_OK | MB_ICONEXCLAMATION);
throw 7;
}
GLuint byteNumber = 0;
while (byteNumber < length){
if ((outputOffset - distance) % ((destinationStream->getWidth() * bytesPerPixel) + 1) == 0){
writeByteToBitmap(filters[(outputOffset - distance) / ((destinationStream->getWidth() * bytesPerPixel) + 1)]);
}
else {
writeByteToBitmap(destinationStream->getData()[(outputOffset - distance) - (((outputOffset - distance) / ((destinationStream->getWidth() * bytesPerPixel) + 1)) + 1)]);
}
byteNumber++;
}
}

GLvoid TextureLoaderPNG::checkPngColourTypeAndBitDepth(GLubyte colourType, GLubyte colourDepth){
GLboolean valid = true;
valid &= colourType != 1 && colourType != 5 && colourType < 7;
valid &= colourDepth == 1 || colourDepth == 2 || colourDepth == 4 || colourDepth == 8 || colourDepth == 16;
if (colourType == 2 || colourType == 4 || colourType == 6){
valid &= colourDepth >= 8;
}
if (colourType == 3){
valid &= colourDepth != 16;
}
if (!valid){
MessageBox(null, "invalid png image - invalid colour type/depth combination", "failed", MB_OK | MB_ICONEXCLAMATION);
throw 7;
}
}

GLuint TextureLoaderPNG::extractUnsignedInt(GLubyte* sizeBytes, GLuint offset){
GLuint size = 0;
GLuint sizeByte = 0;
while (sizeByte < 4){
size += sizeBytes[offset + sizeByte] << (8 * sizeByte);
sizeByte++;
}
return size;
}

GLuint TextureLoaderPNG::extractUnsignedIntLittleEndian(GLubyte* sizeBytes, GLuint offset){
GLuint size = 0;
GLuint sizeByte = 0;
while (sizeByte < 4){
size += sizeBytes[offset + sizeByte] << (8 * (3 - sizeByte));
sizeByte++;
}
return size;
}

GLvoid TextureLoaderPNG::readPngPLTEChunk(std::ifstream& textureFile, GLuint chunkLength, GLubyte colourDepth, GLubyte colourType){
if (colourType == 0 || colourType == 4){
MessageBox(null, "invalid png image - cannot have PLTE chunk with this colour type", "failed", MB_OK | MB_ICONEXCLAMATION);
throw 7;
}
if (chunkLength % 3 != 0){
MessageBox(null, "invalid png image - PLTE chunk length must be multiple of three bytes", "failed", MB_OK | MB_ICONEXCLAMATION);
throw 7;
}
if (chunkLength > 3 * pow(2, colourDepth)){
MessageBox(null, "invalid png image - palette length exceeds colour depth", "failed", MB_OK | MB_ICONEXCLAMATION);
throw 7;
}
textureFile.ignore(chunkLength);
}

GLvoid TextureLoaderPNG::readPngIDATChunk(GLuint chunkLength, TextureLoaderPNG::PngIDATData* dataStream){
dataStream->readFile(chunkLength);
dataStream->process();
}

Bitmap* TextureLoaderPNG::loadImage(char* filename){
std::ifstream textureFile(filename, std::ios::in | std::ios::binary);
if (textureFile.fail()){
MessageBox(null, "image load", "failed", MB_OK | MB_ICONEXCLAMATION);
throw 7;
}
GLubyte* header = new GLubyte[pngHeaderLength];
textureFile.read(header, pngHeaderLength);
if (std::memcmp(header, pngRequiredHeader, pngHeaderLength * sizeof(GLubyte)) != 0){
MessageBox(null, "invalid png image", "failed", MB_OK | MB_ICONEXCLAMATION);
throw 7;
}
delete[] header;
GLuint width;
GLuint height;
GLubyte colourDepth;
GLubyte colourType;
GLubyte compressionMethod;
GLubyte filterMethod;
GLubyte interlaceMethod;
// read IHDR chunk

GLubyte* chunkHeader = new GLubyte[pngChunkHeaderLength];
GLuint chunkDataLength;
textureFile.read(chunkHeader, pngChunkHeaderLength);
chunkDataLength = extractUnsignedIntLittleEndian(chunkHeader, 0);
if(std::memcmp(chunkHeader + 4, pngIHDRChunkHeader, pngChunkHeaderNameLength * sizeof(GLubyte)) != 0){
MessageBox(null, "invalid png image - no IHDR chunk", "failed", MB_OK | MB_ICONEXCLAMATION);
throw 7;
}
if (chunkDataLength != 13){
MessageBox(null, "invalid png image - invalid IHDR chunk length", "failed", MB_OK | MB_ICONEXCLAMATION);
throw 7;
}
GLubyte* chunkData = new GLubyte[13];
textureFile.read(chunkData, chunkDataLength);
width = extractUnsignedIntLittleEndian(chunkData, pngWidthByteOffset);
height = extractUnsignedIntLittleEndian(chunkData, pngHeightByteOffset);
colourDepth = chunkData[pngColourDepthByteOffset];
colourType = chunkData[pngColourTypeByteOffset];
compressionMethod = chunkData[pngCompressionMethodByteOffset];
filterMethod = chunkData[pngFilterMethodByteOffset];
interlaceMethod = chunkData[pngInterlaceMethodByteOffset];
delete[] chunkData;
checkPngColourTypeAndBitDepth(colourType, colourDepth);
if (compressionMethod != 0){
MessageBox(null, "invalid png image - unknown compression method", "failed", MB_OK | MB_ICONEXCLAMATION);
throw 7;
}
if (filterMethod != 0){
MessageBox(null, "invalid png image - unknown filter method", "failed", MB_OK | MB_ICONEXCLAMATION);
throw 7;
}
if (interlaceMethod > 1){
MessageBox(null, "invalid png image - unknown interlace method", "failed", MB_OK | MB_ICONEXCLAMATION);
throw 7;
}
// ignore CRC

textureFile.ignore(4);
GLenum format;
if (colourType == 2){
format = GL_RGB;
}
else if (colourType == 6){
format = GL_RGBA;
}
else {
MessageBox(null, "not implemented - only supports RGB and RGBA images", "failed", MB_OK | MB_ICONEXCLAMATION);
throw 7;
}
GLenum type;
if (colourDepth == 1){
type = GL_BITMAP;
}
else if (colourDepth == 4){
type = GL_UNSIGNED_SHORT_4_4_4_4;
}
else if (colourDepth == 8){
type = GL_UNSIGNED_BYTE;
}
else if (colourDepth == 16){
type = GL_UNSIGNED_SHORT;
}
else {
MessageBox(null, "not implemented - unsupported image type", "failed", MB_OK | MB_ICONEXCLAMATION);
throw 7;
}
GLint desiredFormat;
if (format == GL_RGB){
if (type == GL_UNSIGNED_BYTE){
desiredFormat = GL_RGB8;
}
else {
MessageBox(null, "not implemented - unsupported image type", "failed", MB_OK | MB_ICONEXCLAMATION);
throw 7;
}
}
else {
if (type == GL_UNSIGNED_BYTE){
desiredFormat = GL_RGBA8;
}
else {
MessageBox(null, "not implemented - unsupported image type", "failed", MB_OK | MB_ICONEXCLAMATION);
throw 7;
}
}
GLubyte* bitmapData;
if (format == GL_RGB){
bitmapData = new GLubyte[width * height * 4];
}
else {
bitmapData = new GLubyte[width * height * 4];
}
Bitmap* bitmap = new Bitmap(bitmapData, width, height, format, type, desiredFormat);
TextureLoaderPNG::PngIDATData* dataStream = new PngIDATData(textureFile, bitmap);

// read remaining chunks

GLboolean hadIDATChunk = false;
GLboolean hadPLTEChunk = false;
GLboolean lastChunkReadWasIDAT = false;
textureFile.read(chunkHeader, pngChunkHeaderLength);
chunkDataLength = extractUnsignedIntLittleEndian(chunkHeader, 0);
while (std::memcmp(chunkHeader + 4, pngIENDChunkHeader, pngChunkHeaderNameLength * sizeof(GLubyte)) != 0){
if (std::memcmp(chunkHeader + 4, pngPLTEChunkHeader, pngChunkHeaderNameLength * sizeof(GLubyte)) == 0){
if (hadPLTEChunk){
MessageBox(null, "invalid png image - multiple PLTE chunks", "failed", MB_OK | MB_ICONEXCLAMATION);
throw 7;
}
readPngPLTEChunk(textureFile, chunkDataLength, colourDepth, colourType);
hadPLTEChunk = true;
}
else if (std::memcmp(chunkHeader + 4, pngIDATChunkHeader, pngChunkHeaderNameLength * sizeof(GLubyte)) == 0){
if (colourType == 3 && !hadPLTEChunk){
MessageBox(null, "invalid png image - first IDAT chunk preceedes PLTE chunk", "failed", MB_OK | MB_ICONEXCLAMATION);
throw 7;
}
if (hadIDATChunk && !lastChunkReadWasIDAT){
MessageBox(null, "invalid png image - IDAT chunks not consecutive", "failed", MB_OK | MB_ICONEXCLAMATION);
throw 7;
}
readPngIDATChunk(chunkDataLength, dataStream);
hadIDATChunk = true;
lastChunkReadWasIDAT = true;
}
else {
textureFile.ignore(chunkDataLength);
}
// ignore CRC

textureFile.ignore(4);
textureFile.read(chunkHeader, pngChunkHeaderLength);
chunkDataLength = extractUnsignedIntLittleEndian(chunkHeader, 0);
}
delete[] chunkHeader;
if (!hadIDATChunk){
MessageBox(null, "invalid png image - no IDAT chunk", "failed", MB_OK | MB_ICONEXCLAMATION);
throw 7;
}
textureFile.close();
dataStream->processToEnd();
dataStream->applyFilters();
return bitmap;
}


Enigma

Share this post


Link to post
Share on other sites
Thanks for the code enigma, but I think I''ll try and get my head around the libpng problem first.

Beer hunter - did you have to add any defines for png to work on yours or did you use it as is? And what platform is that for?

Share this post


Link to post
Share on other sites
quote:
Original post by Damocles
Beer hunter - did you have to add any defines for png to work on yours or did you use it as is? And what platform is that for?
I''m using g++ under windows, along with the precompiled libpng from here. No #defines were needed.

Share this post


Link to post
Share on other sites
I just use Corona.

(incomplete, but you should be able to get the idea)

corona::Image* img = corona::OpenImage(fileName, corona::FF_AUTODETECT, corona::PF_R8G8B8A8);
if (!img)
throw std::runtime_error(std::string("Unable to load image file ") + fname);

glGenTextures(1, &tex->handle);
glBindTexture(GL_TEXTURE_2D, tex->handle);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, img->getWidth(), img->getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, img->getPixels());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

delete img;


I''m hip because I say "M$" instead of "MS".

Share this post


Link to post
Share on other sites