406 lines
11 KiB
C
406 lines
11 KiB
C
//========================================================================
|
|
// GLFW - An OpenGL framework
|
|
// File: tga.c
|
|
// Platform: Any
|
|
// API version: 2.6
|
|
// WWW: http://glfw.sourceforge.net
|
|
//------------------------------------------------------------------------
|
|
// Copyright (c) 2002-2006 Camilla Berglund
|
|
//
|
|
// This software is provided 'as-is', without any express or implied
|
|
// warranty. In no event will the authors be held liable for any damages
|
|
// arising from the use of this software.
|
|
//
|
|
// Permission is granted to anyone to use this software for any purpose,
|
|
// including commercial applications, and to alter it and redistribute it
|
|
// freely, subject to the following restrictions:
|
|
//
|
|
// 1. The origin of this software must not be misrepresented; you must not
|
|
// claim that you wrote the original software. If you use this software
|
|
// in a product, an acknowledgment in the product documentation would
|
|
// be appreciated but is not required.
|
|
//
|
|
// 2. Altered source versions must be plainly marked as such, and must not
|
|
// be misrepresented as being the original software.
|
|
//
|
|
// 3. This notice may not be removed or altered from any source
|
|
// distribution.
|
|
//
|
|
//========================================================================
|
|
|
|
//========================================================================
|
|
// Description:
|
|
//
|
|
// TGA format image file loader. This module supports version 1 Targa
|
|
// images, with these restrictions:
|
|
// - Pixel format may only be 8, 24 or 32 bits
|
|
// - Colormaps must be no longer than 256 entries
|
|
//
|
|
//========================================================================
|
|
|
|
|
|
#include "internal.h"
|
|
|
|
|
|
//************************************************************************
|
|
//**** GLFW internal functions & declarations ****
|
|
//************************************************************************
|
|
|
|
//========================================================================
|
|
// TGA file header information
|
|
//========================================================================
|
|
|
|
typedef struct {
|
|
int idlen; // 1 byte
|
|
int cmaptype; // 1 byte
|
|
int imagetype; // 1 byte
|
|
int cmapfirstidx; // 2 bytes
|
|
int cmaplen; // 2 bytes
|
|
int cmapentrysize; // 1 byte
|
|
int xorigin; // 2 bytes
|
|
int yorigin; // 2 bytes
|
|
int width; // 2 bytes
|
|
int height; // 2 bytes
|
|
int bitsperpixel; // 1 byte
|
|
int imageinfo; // 1 byte
|
|
int _alphabits; // (derived from imageinfo)
|
|
int _origin; // (derived from imageinfo)
|
|
} _tga_header_t;
|
|
|
|
#define _TGA_CMAPTYPE_NONE 0
|
|
#define _TGA_CMAPTYPE_PRESENT 1
|
|
|
|
#define _TGA_IMAGETYPE_NONE 0
|
|
#define _TGA_IMAGETYPE_CMAP 1
|
|
#define _TGA_IMAGETYPE_TC 2
|
|
#define _TGA_IMAGETYPE_GRAY 3
|
|
#define _TGA_IMAGETYPE_CMAP_RLE 9
|
|
#define _TGA_IMAGETYPE_TC_RLE 10
|
|
#define _TGA_IMAGETYPE_GRAY_RLE 11
|
|
|
|
#define _TGA_IMAGEINFO_ALPHA_MASK 0x0f
|
|
#define _TGA_IMAGEINFO_ALPHA_SHIFT 0
|
|
#define _TGA_IMAGEINFO_ORIGIN_MASK 0x30
|
|
#define _TGA_IMAGEINFO_ORIGIN_SHIFT 4
|
|
|
|
#define _TGA_ORIGIN_BL 0
|
|
#define _TGA_ORIGIN_BR 1
|
|
#define _TGA_ORIGIN_UL 2
|
|
#define _TGA_ORIGIN_UR 3
|
|
|
|
|
|
//========================================================================
|
|
// _glfwReadTGAHeader() - Read TGA file header (and check that it is
|
|
// valid)
|
|
//========================================================================
|
|
|
|
static int _glfwReadTGAHeader( _GLFWstream *s, _tga_header_t *h )
|
|
{
|
|
unsigned char buf[ 18 ];
|
|
int pos;
|
|
|
|
// Read TGA file header from file
|
|
pos = _glfwTellStream( s );
|
|
_glfwReadStream( s, buf, 18 );
|
|
|
|
// Interpret header (endian independent parsing)
|
|
h->idlen = (int) buf[0];
|
|
h->cmaptype = (int) buf[1];
|
|
h->imagetype = (int) buf[2];
|
|
h->cmapfirstidx = (int) buf[3] | (((int) buf[4]) << 8);
|
|
h->cmaplen = (int) buf[5] | (((int) buf[6]) << 8);
|
|
h->cmapentrysize = (int) buf[7];
|
|
h->xorigin = (int) buf[8] | (((int) buf[9]) << 8);
|
|
h->yorigin = (int) buf[10] | (((int) buf[11]) << 8);
|
|
h->width = (int) buf[12] | (((int) buf[13]) << 8);
|
|
h->height = (int) buf[14] | (((int) buf[15]) << 8);
|
|
h->bitsperpixel = (int) buf[16];
|
|
h->imageinfo = (int) buf[17];
|
|
|
|
// Extract alphabits and origin information
|
|
h->_alphabits = (int) (h->imageinfo & _TGA_IMAGEINFO_ALPHA_MASK) >>
|
|
_TGA_IMAGEINFO_ALPHA_SHIFT;
|
|
h->_origin = (int) (h->imageinfo & _TGA_IMAGEINFO_ORIGIN_MASK) >>
|
|
_TGA_IMAGEINFO_ORIGIN_SHIFT;
|
|
|
|
// Validate TGA header (is this a TGA file?)
|
|
if( (h->cmaptype == 0 || h->cmaptype == 1) &&
|
|
((h->imagetype >= 1 && h->imagetype <= 3) ||
|
|
(h->imagetype >= 9 && h->imagetype <= 11)) &&
|
|
(h->bitsperpixel == 8 || h->bitsperpixel == 24 ||
|
|
h->bitsperpixel == 32) )
|
|
{
|
|
// Skip the ID field
|
|
_glfwSeekStream( s, h->idlen, SEEK_CUR );
|
|
|
|
// Indicate that the TGA header was valid
|
|
return GL_TRUE;
|
|
}
|
|
else
|
|
{
|
|
// Restore file position
|
|
_glfwSeekStream( s, pos, SEEK_SET );
|
|
|
|
// Indicate that the TGA header was invalid
|
|
return GL_FALSE;
|
|
}
|
|
}
|
|
|
|
//========================================================================
|
|
// _glfwReadTGA_RLE() - Read Run-Length Encoded data
|
|
//========================================================================
|
|
|
|
static void _glfwReadTGA_RLE( unsigned char *buf, int size, int bpp,
|
|
_GLFWstream *s )
|
|
{
|
|
int repcount, bytes, k, n;
|
|
unsigned char pixel[ 4 ];
|
|
char c;
|
|
|
|
// Dummy check
|
|
if( bpp > 4 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
while( size > 0 )
|
|
{
|
|
// Get repetition count
|
|
_glfwReadStream( s, &c, 1 );
|
|
repcount = (unsigned int) c;
|
|
bytes = ((repcount & 127) + 1) * bpp;
|
|
if( size < bytes )
|
|
{
|
|
bytes = size;
|
|
}
|
|
|
|
// Run-Length packet?
|
|
if( repcount & 128 )
|
|
{
|
|
_glfwReadStream( s, pixel, bpp );
|
|
for( n = 0; n < (repcount & 127) + 1; n ++ )
|
|
{
|
|
for( k = 0; k < bpp; k ++ )
|
|
{
|
|
*buf ++ = pixel[ k ];
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// It's a Raw packet
|
|
_glfwReadStream( s, buf, bytes );
|
|
buf += bytes;
|
|
}
|
|
|
|
size -= bytes;
|
|
}
|
|
}
|
|
|
|
|
|
//========================================================================
|
|
// _glfwReadTGA() - Read a TGA image from a file
|
|
//========================================================================
|
|
|
|
int _glfwReadTGA( _GLFWstream *s, GLFWimage *img, int flags )
|
|
{
|
|
_tga_header_t h;
|
|
unsigned char *cmap, *pix, tmp, *src, *dst;
|
|
int cmapsize, pixsize, pixsize2;
|
|
int bpp, bpp2, k, m, n, swapx, swapy;
|
|
|
|
// Read TGA header
|
|
if( !_glfwReadTGAHeader( s, &h ) )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// Is there a colormap?
|
|
cmapsize = (h.cmaptype == _TGA_CMAPTYPE_PRESENT ? 1 : 0) * h.cmaplen *
|
|
((h.cmapentrysize+7) / 8);
|
|
if( cmapsize > 0 )
|
|
{
|
|
// Is it a colormap that we can handle?
|
|
if( (h.cmapentrysize != 24 && h.cmapentrysize != 32) ||
|
|
h.cmaplen == 0 || h.cmaplen > 256 )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// Allocate memory for colormap
|
|
cmap = (unsigned char *) malloc( cmapsize );
|
|
if( cmap == NULL )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// Read colormap from file
|
|
_glfwReadStream( s, cmap, cmapsize );
|
|
}
|
|
else
|
|
{
|
|
cmap = NULL;
|
|
}
|
|
|
|
// Size of pixel data
|
|
pixsize = h.width * h.height * ((h.bitsperpixel + 7) / 8);
|
|
|
|
// Bytes per pixel (pixel data - unexpanded)
|
|
bpp = (h.bitsperpixel + 7) / 8;
|
|
|
|
// Bytes per pixel (expanded pixels - not colormap indeces)
|
|
if( cmap )
|
|
{
|
|
bpp2 = (h.cmapentrysize + 7) / 8;
|
|
}
|
|
else
|
|
{
|
|
bpp2 = bpp;
|
|
}
|
|
|
|
// For colormaped images, the RGB/RGBA image data may use more memory
|
|
// than the stored pixel data
|
|
pixsize2 = h.width * h.height * bpp2;
|
|
|
|
// Allocate memory for pixel data
|
|
pix = (unsigned char *) malloc( pixsize2 );
|
|
if( pix == NULL )
|
|
{
|
|
if( cmap )
|
|
{
|
|
free( cmap );
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Read pixel data from file
|
|
if( h.imagetype >= _TGA_IMAGETYPE_CMAP_RLE )
|
|
{
|
|
_glfwReadTGA_RLE( pix, pixsize, bpp, s );
|
|
}
|
|
else
|
|
{
|
|
_glfwReadStream( s, pix, pixsize );
|
|
}
|
|
|
|
// If the image origin is not what we want, re-arrange the pixels
|
|
switch( h._origin )
|
|
{
|
|
default:
|
|
case _TGA_ORIGIN_UL:
|
|
swapx = 0;
|
|
swapy = 1;
|
|
break;
|
|
|
|
case _TGA_ORIGIN_BL:
|
|
swapx = 0;
|
|
swapy = 0;
|
|
break;
|
|
|
|
case _TGA_ORIGIN_UR:
|
|
swapx = 1;
|
|
swapy = 1;
|
|
break;
|
|
|
|
case _TGA_ORIGIN_BR:
|
|
swapx = 1;
|
|
swapy = 0;
|
|
break;
|
|
}
|
|
if( (swapy && !(flags & GLFW_ORIGIN_UL_BIT)) ||
|
|
(!swapy && (flags & GLFW_ORIGIN_UL_BIT)) )
|
|
{
|
|
src = pix;
|
|
dst = &pix[ (h.height-1)*h.width*bpp ];
|
|
for( n = 0; n < h.height/2; n ++ )
|
|
{
|
|
for( m = 0; m < h.width ; m ++ )
|
|
{
|
|
for( k = 0; k < bpp; k ++ )
|
|
{
|
|
tmp = *src;
|
|
*src ++ = *dst;
|
|
*dst ++ = tmp;
|
|
}
|
|
}
|
|
dst -= 2*h.width*bpp;
|
|
}
|
|
}
|
|
if( swapx )
|
|
{
|
|
src = pix;
|
|
dst = &pix[ (h.width-1)*bpp ];
|
|
for( n = 0; n < h.height; n ++ )
|
|
{
|
|
for( m = 0; m < h.width/2 ; m ++ )
|
|
{
|
|
for( k = 0; k < bpp; k ++ )
|
|
{
|
|
tmp = *src;
|
|
*src ++ = *dst;
|
|
*dst ++ = tmp;
|
|
}
|
|
dst -= 2*bpp;
|
|
}
|
|
src += ((h.width+1)/2)*bpp;
|
|
dst += ((3*h.width+1)/2)*bpp;
|
|
}
|
|
}
|
|
|
|
// Convert BGR/BGRA to RGB/RGBA, and optionally colormap indeces to
|
|
// RGB/RGBA values
|
|
if( cmap )
|
|
{
|
|
// Convert colormap pixel format (BGR -> RGB or BGRA -> RGBA)
|
|
if( bpp2 == 3 || bpp2 == 4 )
|
|
{
|
|
for( n = 0; n < h.cmaplen; n ++ )
|
|
{
|
|
tmp = cmap[ n*bpp2 ];
|
|
cmap[ n*bpp2 ] = cmap[ n*bpp2 + 2 ];
|
|
cmap[ n*bpp2 + 2 ] = tmp;
|
|
}
|
|
}
|
|
|
|
// Convert pixel data to RGB/RGBA data
|
|
for( m = h.width * h.height - 1; m >= 0; m -- )
|
|
{
|
|
n = pix[ m ];
|
|
for( k = 0; k < bpp2; k ++ )
|
|
{
|
|
pix[ m*bpp2 + k ] = cmap[ n*bpp2 + k ];
|
|
}
|
|
}
|
|
|
|
// Free memory for colormap (it's not needed anymore)
|
|
free( cmap );
|
|
}
|
|
else
|
|
{
|
|
// Convert image pixel format (BGR -> RGB or BGRA -> RGBA)
|
|
if( bpp2 == 3 || bpp2 == 4 )
|
|
{
|
|
src = pix;
|
|
dst = &pix[ 2 ];
|
|
for( n = 0; n < h.height * h.width; n ++ )
|
|
{
|
|
tmp = *src;
|
|
*src = *dst;
|
|
*dst = tmp;
|
|
src += bpp2;
|
|
dst += bpp2;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fill out GLFWimage struct (the Format field will be set by
|
|
// glfwReadImage)
|
|
img->Width = h.width;
|
|
img->Height = h.height;
|
|
img->BytesPerPixel = bpp2;
|
|
img->Data = pix;
|
|
|
|
return 1;
|
|
}
|
|
|