bluecore/glfw/lib/tga.c

408 lines
12 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.
//
//------------------------------------------------------------------------
// $Id: tga.c,v 1.6 2007/03/15 03:20:19 elmindreda Exp $
//========================================================================
//========================================================================
// 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;
}