mirror of
https://github.com/glest/glest-source.git
synced 2025-02-26 20:52:32 +01:00
1536 lines
39 KiB
C++
1536 lines
39 KiB
C++
// ==============================================================
|
|
// This file is part of MegaGlest (www.glest.org)
|
|
//
|
|
// Image.cpp -- Copyright (c) 2006 David Henry
|
|
// changed for use with MegaGlest: Copyright (C) 2011- by Mark Vejvoda
|
|
//
|
|
// This code is licensed under the MIT license:
|
|
// http://www.opensource.org/licenses/mit-license.php
|
|
//
|
|
// Open Source Initiative OSI - The MIT License (MIT):Licensing
|
|
//
|
|
// The MIT License (MIT)
|
|
// Copyright (c) <year> <copyright holders>
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
// copy of this software and associated documentation files (the "Software"),
|
|
// to deal in the Software without restriction, including without limitation
|
|
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
// and/or sell copies of the Software, and to permit persons to whom the
|
|
// Software is furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.//
|
|
//
|
|
// Implementation of image loader classes for DDS, TGA, PCX, JPEG
|
|
// and PNG image formats.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "Image.h"
|
|
|
|
namespace Shared { namespace Graphics { namespace md5 {
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// class ImageBuffer implementation.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// --------------------------------------------------------------------------
|
|
// ImageBuffer::ImageBuffer
|
|
//
|
|
// Constructor. Load a file into memory buffer.
|
|
// --------------------------------------------------------------------------
|
|
ImageBuffer::ImageBuffer (const string &filename)
|
|
: _filename (filename), _data (NULL), _length (0) {
|
|
// Open file
|
|
std::ifstream ifs (filename.c_str(), std::ios::in | std::ios::binary);
|
|
|
|
if (ifs.fail ())
|
|
throw ImageException ("Couldn't open image file: " + filename, filename);
|
|
|
|
// Get file length
|
|
ifs.seekg (0, std::ios::end);
|
|
_length = ifs.tellg ();
|
|
ifs.seekg (0, std::ios::beg);
|
|
|
|
try {
|
|
// Allocate memory for holding file data
|
|
_data = new GLubyte[_length];
|
|
|
|
// Read whole file data
|
|
ifs.read (reinterpret_cast<char *> (_data), _length);
|
|
ifs.close ();
|
|
}
|
|
catch (...)
|
|
{
|
|
delete [] _data;
|
|
throw;
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// ImageBuffer::~ImageBuffer
|
|
//
|
|
// Destructor. Free memory buffer.
|
|
// --------------------------------------------------------------------------
|
|
ImageBuffer::~ImageBuffer () {
|
|
delete [] _data;
|
|
_data = NULL;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// class Image implementation.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Image::Image
|
|
//
|
|
// Constructors. Create an image from a pixel buffer. It allows to
|
|
// create an image from an other source of data than an ImageBuffer.
|
|
// This is also the copy constructor to use if you want pixel data
|
|
// to be copied, since the Image(const Image&) constructor is protected.
|
|
// --------------------------------------------------------------------------
|
|
|
|
Image::Image (const string &name, GLsizei w, GLsizei h, GLint numMipMaps,
|
|
GLenum format, GLint components, const GLubyte *pixels,
|
|
bool stdCoordSystem)
|
|
: _width (w), _height (h), _numMipmaps (numMipMaps),
|
|
_format (format), _components (components), _name (name),
|
|
_standardCoordSystem (stdCoordSystem) {
|
|
// NOTE: pixels should be a valid pointer. w, h and components
|
|
// have to be non-zero positive values.
|
|
|
|
long size = _width * _height * _components;
|
|
|
|
if (size <= 0)
|
|
throw std::invalid_argument
|
|
("Image::Image: Invalid width, height or component value");
|
|
|
|
if (!pixels)
|
|
throw std::invalid_argument
|
|
("Image::Image: Invalid pixel data source");
|
|
|
|
// allocate memory for pixel data
|
|
_pixels = new GLubyte[size];
|
|
|
|
// Copy pixel data from buffer
|
|
memcpy (_pixels, pixels, size);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Image::~Image
|
|
//
|
|
// Destructor. Delete all memory allocated for this object, i.e. pixel
|
|
// data.
|
|
// --------------------------------------------------------------------------
|
|
Image::~Image () {
|
|
delete [] _pixels;
|
|
_pixels = NULL;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Image::isCompressed
|
|
//
|
|
// Check if the image is using S3TC compression (this is the case for
|
|
// DDS image only).
|
|
// --------------------------------------------------------------------------
|
|
bool Image::isCompressed () const {
|
|
switch (_format)
|
|
{
|
|
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
|
|
case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
|
|
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Image::isPowerOfTwo
|
|
//
|
|
// Check if the image dimensions are powers of two.
|
|
// --------------------------------------------------------------------------
|
|
bool Image::isPowerOfTwo () const {
|
|
GLsizei m;
|
|
for (m = 1; m < _width; m *= 2)
|
|
;
|
|
|
|
if (m != _width)
|
|
return false;
|
|
|
|
for (m = 1; m < _height; m *= 2)
|
|
;
|
|
|
|
if (m != _height)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// class ImageDDS implementation.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// FourCC auto generation with templates
|
|
template <char ch0, char ch1, char ch2, char ch3>
|
|
struct MakeFourCC {
|
|
enum {
|
|
Val =(((ch3 << 24) & 0xFF000000) |
|
|
((ch2 << 16) & 0x00FF0000) |
|
|
((ch1 << 8) & 0x0000FF00) |
|
|
(ch0 & 0x000000FF))
|
|
};
|
|
};
|
|
|
|
const unsigned int FOURCC_DXT1 = MakeFourCC<'D', 'X', 'T', '1'>::Val;
|
|
const unsigned int FOURCC_DXT3 = MakeFourCC<'D', 'X', 'T', '3'>::Val;
|
|
const unsigned int FOURCC_DXT5 = MakeFourCC<'D', 'X', 'T', '5'>::Val;
|
|
|
|
// --------------------------------------------------------------------------
|
|
// ImageDDS::ImageDDS
|
|
//
|
|
// Constructor. Read a DDS image from memory.
|
|
// --------------------------------------------------------------------------
|
|
ImageDDS::ImageDDS (const ImageBuffer &ibuff)
|
|
: _ddsd (NULL) {
|
|
_name = ibuff.filename ();
|
|
|
|
// DDS images use the GDI+ coordinate system (starts upper-left corner)
|
|
_standardCoordSystem = false;
|
|
|
|
// There are extensions required for handling compressed DDS
|
|
// images as textures
|
|
if (!GLEW_ARB_texture_compression)
|
|
throw ImageException ("missing GL_ARB_texture_compression"
|
|
" extension", _name);
|
|
|
|
if (!GLEW_EXT_texture_compression_s3tc)
|
|
throw ImageException ("missing GL_EXT_texture_compression_s3tc"
|
|
" extension", _name);
|
|
|
|
try {
|
|
// Get pointer on file data
|
|
const GLubyte *data_ptr = ibuff.data ();
|
|
int bufferSize = ibuff.length ();
|
|
|
|
// Check if is a valid DDS file
|
|
string magic;
|
|
magic.assign (reinterpret_cast<const char *>(data_ptr), 4);
|
|
if (magic.compare (0, 4, "DDS ") != 0)
|
|
throw ImageException ("Not a valid DDS file", _name);
|
|
|
|
// Eat 4 bytes magic number
|
|
data_ptr += 4;
|
|
bufferSize -= 4;
|
|
|
|
// Read the surface descriptor and init some member variables
|
|
_ddsd = reinterpret_cast<const DDSurfaceDesc*>(data_ptr);
|
|
data_ptr += sizeof (DDSurfaceDesc);
|
|
bufferSize -= sizeof (DDSurfaceDesc);
|
|
|
|
_width = _ddsd->width;
|
|
_height = _ddsd->height;
|
|
_numMipmaps = _ddsd->mipMapLevels;
|
|
|
|
switch (_ddsd->format.fourCC)
|
|
{
|
|
case FOURCC_DXT1:
|
|
// DXT1's compression ratio is 8:1
|
|
_format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
|
|
_components = 3;
|
|
break;
|
|
|
|
case FOURCC_DXT3:
|
|
// DXT3's compression ratio is 4:1
|
|
_format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
|
|
_components = 4;
|
|
break;
|
|
|
|
case FOURCC_DXT5:
|
|
// DXT5's compression ratio is 4:1
|
|
_format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
|
|
_components = 4;
|
|
break;
|
|
|
|
default:
|
|
// Bad fourCC, unsupported or bad format
|
|
throw ImageException ("Unsupported DXT format", _name);
|
|
}
|
|
|
|
// Read pixel data with mipmaps
|
|
_pixels = new GLubyte[bufferSize];
|
|
memcpy (_pixels, data_ptr, bufferSize);
|
|
}
|
|
catch (...)
|
|
{
|
|
delete [] _pixels;
|
|
throw;
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// class ImageTGA implementation.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Pixel's component table access
|
|
int ImageTGA::rgbaTable[4] = { 2, 1, 0, 3 };
|
|
int ImageTGA::bgraTable[4] = { 0, 1, 2, 3 };
|
|
|
|
// --------------------------------------------------------------------------
|
|
// ImageTGA::ImageTGA
|
|
//
|
|
// Constructor. Read a TGA image from memory.
|
|
// --------------------------------------------------------------------------
|
|
ImageTGA::ImageTGA (const ImageBuffer &ibuff)
|
|
: _header (NULL) {
|
|
const GLubyte *colormap = NULL;
|
|
const GLubyte *data_ptr;
|
|
|
|
try
|
|
{
|
|
_name = ibuff.filename ();
|
|
data_ptr = ibuff.data ();
|
|
_standardCoordSystem = true;
|
|
|
|
// Read TGA header
|
|
_header = reinterpret_cast<const TGA_Header *>(data_ptr);
|
|
data_ptr += sizeof (TGA_Header) + _header->id_lenght;
|
|
|
|
// Get image information
|
|
getTextureInfo ();
|
|
|
|
// Memory allocation for pixel data
|
|
_pixels = new GLubyte[_width * _height * _components];
|
|
|
|
// Read color map, if present
|
|
if (_header->colormap_type)
|
|
{
|
|
// NOTE: color map is stored in BGR
|
|
colormap = data_ptr;
|
|
data_ptr += _header->cm_length * (_header->cm_size >> 3);
|
|
}
|
|
|
|
// Decode image data
|
|
switch (_header->image_type)
|
|
{
|
|
case 0:
|
|
// No data
|
|
break;
|
|
|
|
case 1:
|
|
// Uncompressed 8 bits color index
|
|
readTGA8bits (data_ptr, colormap);
|
|
break;
|
|
|
|
case 2:
|
|
// Uncompressed 16-24-32 bits
|
|
switch (_header->pixel_depth)
|
|
{
|
|
case 16:
|
|
readTGA16bits (data_ptr);
|
|
break;
|
|
|
|
case 24:
|
|
readTGA24bits (data_ptr);
|
|
break;
|
|
|
|
case 32:
|
|
readTGA32bits (data_ptr);
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case 3:
|
|
// Uncompressed 8 or 16 bits grayscale
|
|
if (_header->pixel_depth == 8)
|
|
readTGAgray8bits (data_ptr);
|
|
else // 16 bits
|
|
readTGAgray16bits (data_ptr);
|
|
|
|
break;
|
|
|
|
case 9:
|
|
// RLE compressed 8 bits color index
|
|
readTGA8bitsRLE (data_ptr, colormap);
|
|
break;
|
|
|
|
case 10:
|
|
// RLE compressed 16-24-32 bits
|
|
switch (_header->pixel_depth)
|
|
{
|
|
case 16:
|
|
readTGA16bitsRLE (data_ptr);
|
|
break;
|
|
|
|
case 24:
|
|
readTGA24bitsRLE (data_ptr);
|
|
break;
|
|
|
|
case 32:
|
|
readTGA32bitsRLE (data_ptr);
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case 11:
|
|
// RLE compressed 8 or 16 bits grayscale
|
|
if (_header->pixel_depth == 8)
|
|
readTGAgray8bitsRLE (data_ptr);
|
|
else // 16 bits
|
|
readTGAgray16bitsRLE (data_ptr);
|
|
|
|
break;
|
|
|
|
default:
|
|
// Image type is not correct, free memory and quit
|
|
throw ImageException ("Unknown TGA image type", _name);
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
delete [] _pixels;
|
|
throw;
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// ImageTGA::getTextureInfo
|
|
//
|
|
// Extract OpenGL texture informations from TGA header.
|
|
// --------------------------------------------------------------------------
|
|
void ImageTGA::getTextureInfo () {
|
|
_width = _header->width;
|
|
_height = _header->height;
|
|
|
|
switch (_header->image_type)
|
|
{
|
|
case 3: // grayscale 8 bits
|
|
case 11: // grayscale 8 bits (RLE)
|
|
{
|
|
if (_header->pixel_depth == 8)
|
|
{
|
|
_format = GL_LUMINANCE;
|
|
_components = 1;
|
|
}
|
|
else // 16 bits
|
|
{
|
|
_format = GL_LUMINANCE_ALPHA;
|
|
_components = 2;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case 1: // 8 bits color index
|
|
case 2: // BGR 16-24-32 bits
|
|
case 9: // 8 bits color index (RLE)
|
|
case 10: // BGR 16-24-32 bits (RLE)
|
|
{
|
|
// 8 bits and 16 bits images will be converted to 24 bits
|
|
if (_header->pixel_depth <= 24)
|
|
{
|
|
_format = GLEW_EXT_bgra ? GL_BGR : GL_RGB;
|
|
_components = 3;
|
|
}
|
|
else // 32 bits
|
|
{
|
|
_format = GLEW_EXT_bgra ? GL_BGRA : GL_RGBA;
|
|
_components = 4;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// ImageTGA::readTGA8bits
|
|
//
|
|
// Read 8 bits pixel data from TGA file.
|
|
// --------------------------------------------------------------------------
|
|
void ImageTGA::readTGA8bits (const GLubyte *data, const GLubyte *colormap) {
|
|
const GLubyte *pData = data;
|
|
int *compTable = GLEW_EXT_bgra ? bgraTable : rgbaTable;
|
|
|
|
for (int i = 0; i < _width * _height; ++i)
|
|
{
|
|
// Read index color byte
|
|
GLubyte color = *(pData++);
|
|
|
|
// Convert to BGR/RGB 24 bits
|
|
_pixels[(i * 3) + compTable[0]] = colormap[(color * 3) + 0];
|
|
_pixels[(i * 3) + compTable[1]] = colormap[(color * 3) + 1];
|
|
_pixels[(i * 3) + compTable[2]] = colormap[(color * 3) + 2];
|
|
}
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
// ImageTGA::readTGA16bits
|
|
//
|
|
// Read 16 bits pixel data from TGA file.
|
|
// --------------------------------------------------------------------------
|
|
void ImageTGA::readTGA16bits (const GLubyte *data) {
|
|
const GLubyte *pData = data;
|
|
int *compTable = GLEW_EXT_bgra ? bgraTable : rgbaTable;
|
|
|
|
for (int i = 0; i < _width * _height; ++i, pData += 2) {
|
|
// Read color word
|
|
unsigned short color = pData[0] + (pData[1] << 8);
|
|
|
|
// convert to BGR/RGB 24 bits
|
|
_pixels[(i * 3) + compTable[2]] = (((color & 0x7C00) >> 10) << 3);
|
|
_pixels[(i * 3) + compTable[1]] = (((color & 0x03E0) >> 5) << 3);
|
|
_pixels[(i * 3) + compTable[0]] = (((color & 0x001F) >> 0) << 3);
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// ImageTGA::readTGA24bits
|
|
//
|
|
// Read 24 bits pixel data from TGA file.
|
|
// --------------------------------------------------------------------------
|
|
void ImageTGA::readTGA24bits (const GLubyte *data) {
|
|
if (GLEW_EXT_bgra)
|
|
{
|
|
memcpy (_pixels, data, _width * _height * 3);
|
|
}
|
|
else
|
|
{
|
|
const GLubyte *pData = data;
|
|
|
|
for (int i = 0; i < _width * _height; ++i)
|
|
{
|
|
// Read RGB 24 bits pixel
|
|
_pixels[(i * 3) + rgbaTable[0]] = *(pData++);
|
|
_pixels[(i * 3) + rgbaTable[1]] = *(pData++);
|
|
_pixels[(i * 3) + rgbaTable[2]] = *(pData++);
|
|
}
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// ImageTGA::readTGA32bits
|
|
//
|
|
// Read 32 bits pixel data from TGA file.
|
|
// --------------------------------------------------------------------------
|
|
void ImageTGA::readTGA32bits (const GLubyte *data) {
|
|
if (GLEW_EXT_bgra)
|
|
{
|
|
memcpy (_pixels, data, _width * _height * 4);
|
|
}
|
|
else
|
|
{
|
|
const GLubyte *pData = data;
|
|
|
|
for (int i = 0; i < _width * _height; ++i)
|
|
{
|
|
// Read RGB 32 bits pixel
|
|
_pixels[(i * 4) + rgbaTable[0]] = *(pData++);
|
|
_pixels[(i * 4) + rgbaTable[1]] = *(pData++);
|
|
_pixels[(i * 4) + rgbaTable[2]] = *(pData++);
|
|
_pixels[(i * 4) + rgbaTable[3]] = *(pData++);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
// ImageTGA::readTGAgray8bits
|
|
//
|
|
// Read grey 8 bits pixel data from TGA file.
|
|
// --------------------------------------------------------------------------
|
|
void ImageTGA::readTGAgray8bits (const GLubyte *data) {
|
|
memcpy (_pixels, data, _width * _height);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// ImageTGA::readTGAgray16bits
|
|
//
|
|
// Read grey 16 bits pixel data from TGA file.
|
|
// --------------------------------------------------------------------------
|
|
|
|
void
|
|
ImageTGA::readTGAgray16bits (const GLubyte *data)
|
|
{
|
|
memcpy (_pixels, data, _width * _height * 2);
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
// ImageTGA::readTGA8bitsRLE
|
|
//
|
|
// Read 8 bits pixel data from TGA file with RLE compression.
|
|
// --------------------------------------------------------------------------
|
|
|
|
void
|
|
ImageTGA::readTGA8bitsRLE (const GLubyte *data, const GLubyte *colormap)
|
|
{
|
|
GLubyte *ptr = _pixels;
|
|
const GLubyte *pData = data;
|
|
int *compTable = GLEW_EXT_bgra ? bgraTable : rgbaTable;
|
|
|
|
while (ptr < _pixels + (_width * _height) * 3)
|
|
{
|
|
// Read first byte
|
|
GLubyte packet_header = *(pData++);
|
|
int size = 1 + (packet_header & 0x7f);
|
|
|
|
if (packet_header & 0x80)
|
|
{
|
|
// Run-length packet
|
|
GLubyte color = *(pData++);
|
|
|
|
for (int i = 0; i < size; ++i, ptr += 3)
|
|
{
|
|
ptr[0] = colormap[(color * 3) + compTable[0]];
|
|
ptr[1] = colormap[(color * 3) + compTable[1]];
|
|
ptr[2] = colormap[(color * 3) + compTable[2]];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Non run-length packet
|
|
for (int i = 0; i < size; ++i, ptr += 3)
|
|
{
|
|
GLubyte color = *(pData++);
|
|
|
|
ptr[0] = colormap[(color * 3) + compTable[0]];
|
|
ptr[1] = colormap[(color * 3) + compTable[1]];
|
|
ptr[2] = colormap[(color * 3) + compTable[2]];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
// ImageTGA::readTGA16bitsRLE
|
|
//
|
|
// Read 16 bits pixel data from TGA file with RLE compression.
|
|
// --------------------------------------------------------------------------
|
|
|
|
void
|
|
ImageTGA::readTGA16bitsRLE (const GLubyte *data)
|
|
{
|
|
const GLubyte *pData = data;
|
|
GLubyte *ptr = _pixels;
|
|
int *compTable = GLEW_EXT_bgra ? bgraTable : rgbaTable;
|
|
|
|
while (ptr < _pixels + (_width * _height) * 3)
|
|
{
|
|
// Read first byte
|
|
GLubyte packet_header = *(pData++);
|
|
int size = 1 + (packet_header & 0x7f);
|
|
|
|
if (packet_header & 0x80)
|
|
{
|
|
// Run-length packet
|
|
unsigned short color = pData[0] + (pData[1] << 8);
|
|
pData += 2;
|
|
|
|
for (int i = 0; i < size; ++i, ptr += 3)
|
|
{
|
|
ptr[compTable[2]] = (((color & 0x7C00) >> 10) << 3);
|
|
ptr[compTable[1]] = (((color & 0x03E0) >> 5) << 3);
|
|
ptr[compTable[0]] = (((color & 0x001F) >> 0) << 3);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Non run-length packet
|
|
for (int i = 0; i < size; ++i, ptr += 3)
|
|
{
|
|
unsigned short color = pData[0] + (pData[1] << 8);
|
|
pData += 2;
|
|
|
|
ptr[compTable[2]] = (((color & 0x7C00) >> 10) << 3);
|
|
ptr[compTable[1]] = (((color & 0x03E0) >> 5) << 3);
|
|
ptr[compTable[0]] = (((color & 0x001F) >> 0) << 3);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
// ImageTGA::readTGA24bitsRLE
|
|
//
|
|
// Read 24 bits pixel data from TGA file with RLE compression.
|
|
// --------------------------------------------------------------------------
|
|
|
|
void
|
|
ImageTGA::readTGA24bitsRLE (const GLubyte *data)
|
|
{
|
|
const GLubyte *pData = data;
|
|
GLubyte *ptr = _pixels;
|
|
int *compTable = GLEW_EXT_bgra ? bgraTable : rgbaTable;
|
|
|
|
while (ptr < _pixels + (_width * _height) * 3)
|
|
{
|
|
// Read first byte
|
|
GLubyte packet_header = *(pData++);
|
|
int size = 1 + (packet_header & 0x7f);
|
|
|
|
if (packet_header & 0x80)
|
|
{
|
|
// Run-length packet
|
|
for (int i = 0; i < size; ++i, ptr += 3)
|
|
{
|
|
ptr[0] = pData[compTable[0]];
|
|
ptr[1] = pData[compTable[1]];
|
|
ptr[2] = pData[compTable[2]];
|
|
}
|
|
|
|
pData += 3;
|
|
}
|
|
else
|
|
{
|
|
// Non run-length packet
|
|
for (int i = 0; i < size; ++i, ptr += 3)
|
|
{
|
|
ptr[0] = pData[compTable[0]];
|
|
ptr[1] = pData[compTable[1]];
|
|
ptr[2] = pData[compTable[2]];
|
|
pData += 3;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
// ImageTGA::readTGA32bitsRLE
|
|
//
|
|
// Read 32 bits pixel data from TGA file with RLE compression.
|
|
// --------------------------------------------------------------------------
|
|
|
|
void
|
|
ImageTGA::readTGA32bitsRLE (const GLubyte *data)
|
|
{
|
|
const GLubyte *pData = data;
|
|
GLubyte *ptr = _pixels;
|
|
int *compTable = GLEW_EXT_bgra ? bgraTable : rgbaTable;
|
|
|
|
while (ptr < _pixels + (_width * _height) * 4)
|
|
{
|
|
// Read first byte
|
|
GLubyte packet_header = *(pData++);
|
|
int size = 1 + (packet_header & 0x7f);
|
|
|
|
if (packet_header & 0x80)
|
|
{
|
|
// Run-length packet */
|
|
for (int i = 0; i < size; ++i, ptr += 4)
|
|
{
|
|
ptr[0] = pData[compTable[0]];
|
|
ptr[1] = pData[compTable[1]];
|
|
ptr[2] = pData[compTable[2]];
|
|
ptr[3] = pData[compTable[3]];
|
|
}
|
|
|
|
pData += 4;
|
|
}
|
|
else
|
|
{
|
|
// Non run-length packet
|
|
for (int i = 0; i < size; ++i, ptr += 4)
|
|
{
|
|
ptr[0] = pData[compTable[0]];
|
|
ptr[1] = pData[compTable[1]];
|
|
ptr[2] = pData[compTable[2]];
|
|
ptr[3] = pData[compTable[3]];
|
|
pData += 4;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
// ImageTGA::readTGAgray8bitsRLE
|
|
//
|
|
// Read grey 8 bits pixel data from TGA file with RLE compression.
|
|
// --------------------------------------------------------------------------
|
|
|
|
void
|
|
ImageTGA::readTGAgray8bitsRLE (const GLubyte *data)
|
|
{
|
|
const GLubyte *pData = data;
|
|
GLubyte *ptr = _pixels;
|
|
|
|
while (ptr < _pixels + (_width * _height))
|
|
{
|
|
// Read first byte
|
|
GLubyte packet_header = *(pData++);
|
|
int size = 1 + (packet_header & 0x7f);
|
|
|
|
if (packet_header & 0x80)
|
|
{
|
|
// Run-length packet
|
|
GLubyte color = *(pData++);
|
|
|
|
memset (ptr, color, size);
|
|
ptr += size;
|
|
}
|
|
else
|
|
{
|
|
// Non run-length packet
|
|
memcpy (ptr, pData, size);
|
|
ptr += size;
|
|
pData += size;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
// ImageTGA::readTGAgray16bitsRLE
|
|
//
|
|
// Read grey 16 bits pixel data from TGA file with RLE compression.
|
|
// --------------------------------------------------------------------------
|
|
|
|
void
|
|
ImageTGA::readTGAgray16bitsRLE (const GLubyte *data)
|
|
{
|
|
const GLubyte *pData = data;
|
|
GLubyte *ptr = _pixels;
|
|
|
|
while (ptr < _pixels + (_width * _height) * 2)
|
|
{
|
|
// Read first byte
|
|
GLubyte packet_header = *(pData++);
|
|
int size = 1 + (packet_header & 0x7f);
|
|
|
|
if (packet_header & 0x80)
|
|
{
|
|
// Run-length packet
|
|
GLubyte color = *(pData++);
|
|
GLubyte alpha = *(pData++);
|
|
|
|
for (int i = 0; i < size; ++i, ptr += 2)
|
|
{
|
|
ptr[0] = color;
|
|
ptr[1] = alpha;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Non run-length packet
|
|
memcpy (ptr, pData, size * 2);
|
|
ptr += size * 2;
|
|
pData += size * 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// class ImagePCX implementation.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Pixel's component table access
|
|
int ImagePCX::rgbTable[3] = { 0, 1, 2 };
|
|
int ImagePCX::bgrTable[3] = { 2, 1, 0 };
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
// ImagePCX::ImagePCX
|
|
//
|
|
// Constructor. Read a PCX image from memory.
|
|
// --------------------------------------------------------------------------
|
|
|
|
ImagePCX::ImagePCX (const ImageBuffer &ibuff)
|
|
: _header (NULL)
|
|
{
|
|
const GLubyte *data_ptr;
|
|
|
|
try
|
|
{
|
|
_name = ibuff.filename ();
|
|
_standardCoordSystem = true;
|
|
|
|
// Get pointer on file data
|
|
data_ptr = ibuff.data ();
|
|
|
|
// Read PCX header
|
|
_header = reinterpret_cast<const PCX_Header *>(data_ptr);
|
|
data_ptr += sizeof (PCX_Header);
|
|
|
|
// Check if is valid PCX file
|
|
if (_header->manufacturer != 0x0a)
|
|
throw ImageException ("Bad version number", _name);
|
|
|
|
// Initialize image variables
|
|
_width = _header->xmax - _header->xmin + 1;
|
|
_height = _header->ymax - _header->ymin + 1;
|
|
_format = GLEW_EXT_bgra ? GL_BGR : GL_RGB;
|
|
_components = 3;
|
|
_pixels = new GLubyte[_width * _height * _components];
|
|
|
|
int bitcount = _header->bitsPerPixel * _header->numColorPlanes;
|
|
int palette_pos = ibuff.length () - 768;
|
|
|
|
// Read image data
|
|
switch (bitcount)
|
|
{
|
|
case 1:
|
|
// 1 bit color index
|
|
readPCX1bit (data_ptr);
|
|
break;
|
|
|
|
case 4:
|
|
// 4 bits color index
|
|
readPCX4bits (data_ptr);
|
|
break;
|
|
|
|
case 8:
|
|
// 8 bits color index
|
|
readPCX8bits (data_ptr, ibuff.data () + palette_pos);
|
|
break;
|
|
|
|
case 24:
|
|
// 24 bits
|
|
readPCX24bits (data_ptr);
|
|
break;
|
|
|
|
default:
|
|
// Unsupported
|
|
throw ImageException ("Unhandled PCX format (bad bitcount)", _name);
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
delete [] _pixels;
|
|
throw;
|
|
}
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
// ImagePCX::readPCX1bit
|
|
//
|
|
// Read 1 bit PCX image.
|
|
// --------------------------------------------------------------------------
|
|
|
|
void
|
|
ImagePCX::readPCX1bit (const GLubyte *data)
|
|
{
|
|
int rle_count = 0, rle_value = 0;
|
|
const GLubyte *pData = data;
|
|
GLubyte *ptr = _pixels;
|
|
int *compTable = GLEW_EXT_bgra ? bgrTable : rgbTable;
|
|
|
|
for (int y = 0; y < _height; ++y)
|
|
{
|
|
ptr = &_pixels[(_height - (y + 1)) * _width * 3];
|
|
int bytes = _header->bytesPerScanLine;
|
|
|
|
// Decode line number y
|
|
while (bytes--)
|
|
{
|
|
if (rle_count == 0)
|
|
{
|
|
if ( (rle_value = *(pData++)) < 0xc0)
|
|
{
|
|
rle_count = 1;
|
|
}
|
|
else
|
|
{
|
|
rle_count = rle_value - 0xc0;
|
|
rle_value = *(pData++);
|
|
}
|
|
}
|
|
|
|
rle_count--;
|
|
|
|
// Fill height pixels chunk
|
|
for (int i = 7; i >= 0; --i, ptr += 3)
|
|
{
|
|
int colorIndex = ((rle_value & (1 << i)) > 0);
|
|
|
|
ptr[0] = _header->palette[colorIndex * 3 + compTable[0]];
|
|
ptr[1] = _header->palette[colorIndex * 3 + compTable[1]];
|
|
ptr[2] = _header->palette[colorIndex * 3 + compTable[2]];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
// ImagePCX::readPCX4bits
|
|
//
|
|
// Read 4 bits PCX image.
|
|
// --------------------------------------------------------------------------
|
|
|
|
void
|
|
ImagePCX::readPCX4bits (const GLubyte *data)
|
|
{
|
|
const GLubyte *pData = data;
|
|
GLubyte *colorIndex = NULL;
|
|
GLubyte *line = NULL;
|
|
GLubyte *ptr;
|
|
int rle_count = 0, rle_value = 0;
|
|
int *compTable = GLEW_EXT_bgra ? bgrTable : rgbTable;
|
|
|
|
try
|
|
{
|
|
// Memory allocation for temporary buffers
|
|
colorIndex = new GLubyte[_width];
|
|
line = new GLubyte[_header->bytesPerScanLine];
|
|
}
|
|
catch (std::bad_alloc &err)
|
|
{
|
|
delete [] colorIndex;
|
|
delete [] line;
|
|
throw;
|
|
}
|
|
|
|
for (int y = 0; y < _height; ++y)
|
|
{
|
|
ptr = &_pixels[(_height - (y + 1)) * _width * 3];
|
|
memset (colorIndex, 0, _width);
|
|
|
|
for (int c = 0; c < 4; ++c)
|
|
{
|
|
GLubyte *pLine = line;
|
|
int bytes = _header->bytesPerScanLine;
|
|
|
|
// Decode line number y
|
|
while (bytes--)
|
|
{
|
|
if (rle_count == 0)
|
|
{
|
|
if ( (rle_value = *(pData++)) < 0xc0)
|
|
{
|
|
rle_count = 1;
|
|
}
|
|
else
|
|
{
|
|
rle_count = rle_value - 0xc0;
|
|
rle_value = *(pData++);
|
|
}
|
|
}
|
|
|
|
rle_count--;
|
|
*(pLine++) = rle_value;
|
|
}
|
|
|
|
// Compute line's color indexes
|
|
for (int x = 0; x < _width; ++x)
|
|
{
|
|
if (line[x / 8] & (128 >> (x % 8)))
|
|
colorIndex[x] += (1 << c);
|
|
}
|
|
}
|
|
|
|
// Decode scanline. color index => rgb
|
|
for (int x = 0; x < _width; ++x, ptr += 3)
|
|
{
|
|
ptr[0] = _header->palette[colorIndex[x] * 3 + compTable[0]];
|
|
ptr[1] = _header->palette[colorIndex[x] * 3 + compTable[1]];
|
|
ptr[2] = _header->palette[colorIndex[x] * 3 + compTable[2]];
|
|
}
|
|
}
|
|
|
|
// Release memory
|
|
delete [] colorIndex;
|
|
delete [] line;
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
// ImagePCX::readPCX8bits
|
|
//
|
|
// Read 8 bits PCX image.
|
|
// --------------------------------------------------------------------------
|
|
|
|
void
|
|
ImagePCX::readPCX8bits (const GLubyte *data, const GLubyte *palette)
|
|
{
|
|
const GLubyte *pData = data;
|
|
int rle_count = 0, rle_value = 0;
|
|
GLubyte *ptr;
|
|
int *compTable = GLEW_EXT_bgra ? bgrTable : rgbTable;
|
|
|
|
// Palette should be preceded by a value of 0x0c (12)...
|
|
GLubyte magic = palette[-1];
|
|
if (magic != 0x0c)
|
|
{
|
|
// ... but sometimes it is not
|
|
std::cerr << "Warning: PCX palette should start with "
|
|
<< "a value of 0x0c (12)!" << std::endl;
|
|
}
|
|
|
|
// Read pixel data
|
|
for (int y = 0; y < _height; ++y)
|
|
{
|
|
ptr = &_pixels[(_height - (y + 1)) * _width * 3];
|
|
int bytes = _header->bytesPerScanLine;
|
|
|
|
// Decode line number y
|
|
while (bytes--)
|
|
{
|
|
if (rle_count == 0)
|
|
{
|
|
if( (rle_value = *(pData++)) < 0xc0)
|
|
{
|
|
rle_count = 1;
|
|
}
|
|
else
|
|
{
|
|
rle_count = rle_value - 0xc0;
|
|
rle_value = *(pData++);
|
|
}
|
|
}
|
|
|
|
rle_count--;
|
|
|
|
ptr[0] = palette[rle_value * 3 + compTable[0]];
|
|
ptr[1] = palette[rle_value * 3 + compTable[1]];
|
|
ptr[2] = palette[rle_value * 3 + compTable[2]];
|
|
ptr += 3;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
// ImagePCX::readPCX24bits
|
|
//
|
|
// Read 24 bits PCX image.
|
|
// --------------------------------------------------------------------------
|
|
|
|
void
|
|
ImagePCX::readPCX24bits (const GLubyte *data)
|
|
{
|
|
const GLubyte *pData = data;
|
|
GLubyte *ptr;
|
|
int rle_count = 0, rle_value = 0;
|
|
int *compTable = GLEW_EXT_bgra ? bgrTable : rgbTable;
|
|
|
|
for (int y = 0; y < _height; ++y)
|
|
{
|
|
// For each color plane
|
|
for (int c = 0; c < 3; ++c)
|
|
{
|
|
ptr = &_pixels[(_height - (y + 1)) * _width * 3];
|
|
int bytes = _header->bytesPerScanLine;
|
|
|
|
// Decode line number y
|
|
while (bytes--)
|
|
{
|
|
if (rle_count == 0)
|
|
{
|
|
if( (rle_value = *(pData++)) < 0xc0)
|
|
{
|
|
rle_count = 1;
|
|
}
|
|
else
|
|
{
|
|
rle_count = rle_value - 0xc0;
|
|
rle_value = *(pData++);
|
|
}
|
|
}
|
|
|
|
rle_count--;
|
|
ptr[compTable[c]] = static_cast<GLubyte>(rle_value);
|
|
ptr += 3;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// class ImageJPEG implementation.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// --------------------------------------------------------------------------
|
|
// ImageJPEG::ImageJPEG
|
|
//
|
|
// Constructor. Read a JPEG image from memory, using libjpeg.
|
|
// --------------------------------------------------------------------------
|
|
|
|
ImageJPEG::ImageJPEG (const ImageBuffer &ibuff)
|
|
{
|
|
jpeg_decompress_struct cinfo;
|
|
my_error_mgr jerr;
|
|
jpeg_source_mgr jsrc;
|
|
JSAMPROW j;
|
|
|
|
try
|
|
{
|
|
_name = ibuff.filename ();
|
|
_standardCoordSystem = true;
|
|
|
|
// Create and configure decompress object
|
|
jpeg_create_decompress (&cinfo);
|
|
cinfo.err = jpeg_std_error (&jerr.pub);
|
|
cinfo.src = &jsrc;
|
|
|
|
// Configure error manager
|
|
jerr.pub.error_exit = errorExit_callback;
|
|
jerr.pub.output_message = outputMessage_callback;
|
|
|
|
if (setjmp (jerr.setjmp_buffer))
|
|
throw ImageException (jerr.errorMsg, _name);
|
|
|
|
// Configure source manager
|
|
jsrc.next_input_byte = ibuff.data ();
|
|
jsrc.bytes_in_buffer = ibuff.length ();
|
|
|
|
jsrc.init_source = initSource_callback;
|
|
jsrc.fill_input_buffer = fillInputBuffer_callback;
|
|
jsrc.skip_input_data = skipInputData_callback;
|
|
jsrc.resync_to_restart = jpeg_resync_to_restart;
|
|
jsrc.term_source = termSource_callback;
|
|
|
|
// Read file's header and prepare for decompression
|
|
jpeg_read_header (&cinfo, TRUE);
|
|
jpeg_start_decompress (&cinfo);
|
|
|
|
// Initialize image's member variables
|
|
_width = cinfo.image_width;
|
|
_height = cinfo.image_height;
|
|
_components = cinfo.num_components;
|
|
_format = (cinfo.num_components == 1) ? GL_LUMINANCE : GL_RGB;
|
|
_pixels = new GLubyte[_width * _height * _components];
|
|
|
|
// Read scanlines
|
|
for (int i = 0; i < _height; ++i)
|
|
{
|
|
//j = &_pixels[_width * i * _components];
|
|
j = (_pixels + ((_height - (i + 1)) * _width * _components));
|
|
jpeg_read_scanlines (&cinfo, &j, 1);
|
|
}
|
|
|
|
// Finish decompression and release memory
|
|
jpeg_finish_decompress (&cinfo);
|
|
jpeg_destroy_decompress (&cinfo);
|
|
}
|
|
catch (...)
|
|
{
|
|
delete [] _pixels;
|
|
jpeg_destroy_decompress (&cinfo);
|
|
|
|
throw;
|
|
}
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
// ImageJPEG::initSource_callback
|
|
// ImageJPEG::fillInputBuffer_callback
|
|
// ImageJPEG::skipInputData_callback
|
|
// ImageJPEG::termSource_callback
|
|
//
|
|
// Callback functions used by libjpeg for reading data from memory.
|
|
// --------------------------------------------------------------------------
|
|
|
|
void
|
|
ImageJPEG::initSource_callback (j_decompress_ptr cinfo)
|
|
{
|
|
// Nothing to do here
|
|
}
|
|
|
|
|
|
boolean
|
|
ImageJPEG::fillInputBuffer_callback (j_decompress_ptr cinfo)
|
|
{
|
|
JOCTET eoi_buffer[2] = { 0xFF, JPEG_EOI };
|
|
struct jpeg_source_mgr *jsrc = cinfo->src;
|
|
|
|
// Create a fake EOI marker
|
|
jsrc->next_input_byte = eoi_buffer;
|
|
jsrc->bytes_in_buffer = 2;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void
|
|
ImageJPEG::skipInputData_callback (j_decompress_ptr cinfo, long num_bytes)
|
|
{
|
|
struct jpeg_source_mgr *jsrc = cinfo->src;
|
|
|
|
if (num_bytes > 0)
|
|
{
|
|
while (num_bytes > static_cast<long>(jsrc->bytes_in_buffer))
|
|
{
|
|
num_bytes -= static_cast<long>(jsrc->bytes_in_buffer);
|
|
fillInputBuffer_callback (cinfo);
|
|
}
|
|
|
|
jsrc->next_input_byte += num_bytes;
|
|
jsrc->bytes_in_buffer -= num_bytes;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
ImageJPEG::termSource_callback (j_decompress_ptr cinfo)
|
|
{
|
|
// Nothing to do here
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
// ImageJPEG::errorExit
|
|
// ImageJPEG::outputMessage
|
|
//
|
|
// Callback functions used by libjpeg for error handling.
|
|
// --------------------------------------------------------------------------
|
|
|
|
void
|
|
ImageJPEG::errorExit_callback (j_common_ptr cinfo)
|
|
{
|
|
my_error_ptr jerr = reinterpret_cast<my_error_ptr>(cinfo->err);
|
|
|
|
// Create the error message
|
|
char message[JMSG_LENGTH_MAX];
|
|
(*cinfo->err->format_message) (cinfo, message);
|
|
jerr->errorMsg.assign (message);
|
|
|
|
// Return control to the setjmp point
|
|
longjmp (jerr->setjmp_buffer, 1);
|
|
}
|
|
|
|
|
|
void
|
|
ImageJPEG::outputMessage_callback (j_common_ptr cinfo)
|
|
{
|
|
my_error_ptr jerr = reinterpret_cast<my_error_ptr>(cinfo->err);
|
|
|
|
// Create the error message
|
|
char message[JMSG_LENGTH_MAX];
|
|
(*cinfo->err->format_message) (cinfo, message);
|
|
jerr->errorMsg.assign (message);
|
|
|
|
// Send it to stderr, adding a newline
|
|
std::cerr << "libjpeg: " << jerr->errorMsg << std::endl;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// class ImagePNG implementation.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// --------------------------------------------------------------------------
|
|
// ImagePNG::ImagePNG
|
|
//
|
|
// Constructor. Read a PNG image from memory, using libpng.
|
|
// --------------------------------------------------------------------------
|
|
|
|
ImagePNG::ImagePNG (const ImageBuffer &ibuff)
|
|
{
|
|
png_structp png_ptr = NULL;
|
|
png_infop info_ptr = NULL;
|
|
png_bytep *row_pointers = NULL;
|
|
int bit_depth, color_type;
|
|
my_source_mgr src_mgr (ibuff);
|
|
|
|
try
|
|
{
|
|
_name = ibuff.filename ();
|
|
_standardCoordSystem = true;
|
|
|
|
png_byte sig[8];
|
|
memcpy (sig, reinterpret_cast<const png_byte*>(ibuff.data ()), 8);
|
|
|
|
// Check for valid magic number
|
|
if (!png_sig_cmp (sig, 0, 8))
|
|
throw ImageException ("Not a valid PNG file", _name);
|
|
|
|
// Create PNG read struct
|
|
png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, &errorMsg,
|
|
error_callback, warning_callback);
|
|
if (!png_ptr)
|
|
throw ImageException ("Failed to create png read struct", _name);
|
|
|
|
if (setjmp (png_jmpbuf (png_ptr)))
|
|
throw ImageException (errorMsg, _name);
|
|
|
|
// Create PNG info struct
|
|
info_ptr = png_create_info_struct (png_ptr);
|
|
if (!info_ptr)
|
|
throw ImageException ("Failed to create png info struct", _name);
|
|
|
|
// Set "read" callback function and give source of data
|
|
png_set_read_fn (png_ptr, &src_mgr, read_callback);
|
|
|
|
// Read png info
|
|
png_read_info (png_ptr, info_ptr);
|
|
|
|
// Get some usefull information from header
|
|
bit_depth = png_get_bit_depth (png_ptr, info_ptr);
|
|
color_type = png_get_color_type (png_ptr, info_ptr);
|
|
|
|
// Convert index color images to RGB images
|
|
if (color_type == PNG_COLOR_TYPE_PALETTE)
|
|
png_set_palette_to_rgb (png_ptr);
|
|
|
|
// Convert 1-2-4 bits grayscale images to 8 bits
|
|
// grayscale.
|
|
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
|
|
png_set_expand_gray_1_2_4_to_8(png_ptr);
|
|
|
|
if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS))
|
|
png_set_tRNS_to_alpha (png_ptr);
|
|
|
|
if (bit_depth == 16)
|
|
png_set_strip_16 (png_ptr);
|
|
else if (bit_depth < 8)
|
|
png_set_packing (png_ptr);
|
|
|
|
// Update info structure to apply transformations
|
|
png_read_update_info (png_ptr, info_ptr);
|
|
|
|
// Get updated information
|
|
png_get_IHDR (png_ptr, info_ptr,
|
|
reinterpret_cast<png_uint_32*>(&_width),
|
|
reinterpret_cast<png_uint_32*>(&_height),
|
|
&bit_depth, &color_type,
|
|
NULL, NULL, NULL);
|
|
|
|
// Get image format and components per pixel
|
|
getTextureInfo (color_type);
|
|
|
|
// Memory allocation for storing pixel data
|
|
_pixels = new GLubyte[_width * _height * _components];
|
|
|
|
// Pointer array. Each one points at the begening of a row.
|
|
row_pointers = new png_bytep[_height];
|
|
|
|
for (int i = 0; i < _height; ++i)
|
|
{
|
|
row_pointers[i] = (png_bytep)(_pixels +
|
|
((_height - (i + 1)) * _width * _components));
|
|
}
|
|
|
|
// Read pixel data using row pointers
|
|
png_read_image (png_ptr, row_pointers);
|
|
|
|
// Finish decompression and release memory
|
|
png_read_end (png_ptr, NULL);
|
|
png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
|
|
|
|
delete [] row_pointers;
|
|
}
|
|
catch (...)
|
|
{
|
|
delete [] _pixels;
|
|
delete [] row_pointers;
|
|
|
|
if (png_ptr)
|
|
png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
|
|
|
|
throw;
|
|
}
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
// ImagePNG::getTextureInfo
|
|
//
|
|
// Extract OpenGL texture informations from PNG info.
|
|
// --------------------------------------------------------------------------
|
|
|
|
void
|
|
ImagePNG::getTextureInfo (int color_type)
|
|
{
|
|
switch (color_type)
|
|
{
|
|
case PNG_COLOR_TYPE_GRAY:
|
|
_format = GL_LUMINANCE;
|
|
_components = 1;
|
|
break;
|
|
|
|
case PNG_COLOR_TYPE_GRAY_ALPHA:
|
|
_format = GL_LUMINANCE_ALPHA;
|
|
_components = 2;
|
|
break;
|
|
|
|
case PNG_COLOR_TYPE_RGB:
|
|
_format = GL_RGB;
|
|
_components = 3;
|
|
break;
|
|
|
|
case PNG_COLOR_TYPE_RGB_ALPHA:
|
|
_format = GL_RGBA;
|
|
_components = 4;
|
|
break;
|
|
|
|
default:
|
|
// Badness
|
|
throw ImageException ("Bad PNG color type", _name);
|
|
}
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
// ImagePNG::read_callback
|
|
// ImagePNG::error_callback
|
|
// ImagePNG::warning_callback
|
|
//
|
|
// Callback functions used by libpng for reading data from memory
|
|
// and error handling.
|
|
// --------------------------------------------------------------------------
|
|
|
|
void
|
|
ImagePNG::read_callback (png_structp png_ptr, png_bytep data, png_size_t length)
|
|
{
|
|
my_source_ptr src = static_cast<my_source_ptr>(png_get_io_ptr(png_ptr));
|
|
|
|
// Copy data from image buffer
|
|
memcpy (data, src->pibuff->data () + src->offset, length);
|
|
|
|
// Advance in the file
|
|
src->offset += length;
|
|
}
|
|
|
|
|
|
void
|
|
ImagePNG::error_callback (png_structp png_ptr, png_const_charp error_msg)
|
|
{
|
|
static_cast<string *>(png_get_error_ptr(png_ptr))->assign (error_msg);
|
|
|
|
longjmp (png_jmpbuf (png_ptr), 1);
|
|
}
|
|
|
|
|
|
void
|
|
ImagePNG::warning_callback (png_structp png_ptr, png_const_charp warning_msg)
|
|
{
|
|
std::cerr << "libpng: " << warning_msg << std::endl;
|
|
}
|
|
|
|
}}} //end namespace
|