caff-previewer/src/libtarga.c
2020-11-03 18:26:19 +01:00

1262 lines
33 KiB
C

/**************************************************************************
** Simplified TARGA library for Intro to Graphics Classes
**
** This is a simple library for reading and writing image files in
** the TARGA file format (which is a simple format).
** The routines are intentionally designed to be simple for use in
** into to graphics assignments - a more full-featured targa library
** also exists for other uses.
**
** This library was originally written by Alex Mohr who has assigned
** copyright to Michael Gleicher. The code is made available under an
** "MIT" Open Source license.
**/
/**
** Copyright (c) 2005 Michael L. Gleicher
**
** 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.
**/
/*
** libtarga.c -- routines for reading targa files.
*/
/*
Modified by yu-chi because of initialization of variables at tga_load
09-16-2005
*/
#include <stdio.h>
#include <malloc.h>
#include "libtarga.h"
#define TGA_IMG_NODATA (0)
#define TGA_IMG_UNC_PALETTED (1)
#define TGA_IMG_UNC_TRUECOLOR (2)
#define TGA_IMG_UNC_GRAYSCALE (3)
#define TGA_IMG_RLE_PALETTED (9)
#define TGA_IMG_RLE_TRUECOLOR (10)
#define TGA_IMG_RLE_GRAYSCALE (11)
#define TGA_LOWER_LEFT (0)
#define TGA_LOWER_RIGHT (1)
#define TGA_UPPER_LEFT (2)
#define TGA_UPPER_RIGHT (3)
#define HDR_LENGTH (18)
#define HDR_IDLEN (0)
#define HDR_CMAP_TYPE (1)
#define HDR_IMAGE_TYPE (2)
#define HDR_CMAP_FIRST (3)
#define HDR_CMAP_LENGTH (5)
#define HDR_CMAP_ENTRY_SIZE (7)
#define HDR_IMG_SPEC_XORIGIN (8)
#define HDR_IMG_SPEC_YORIGIN (10)
#define HDR_IMG_SPEC_WIDTH (12)
#define HDR_IMG_SPEC_HEIGHT (14)
#define HDR_IMG_SPEC_PIX_DEPTH (16)
#define HDR_IMG_SPEC_IMG_DESC (17)
#define TGA_ERR_NONE (0)
#define TGA_ERR_BAD_HEADER (1)
#define TGA_ERR_OPEN_FAILS (2)
#define TGA_ERR_BAD_FORMAT (3)
#define TGA_ERR_UNEXPECTED_EOF (4)
#define TGA_ERR_NODATA_IMAGE (5)
#define TGA_ERR_COLORMAP_FOR_GRAY (6)
#define TGA_ERR_BAD_COLORMAP_ENTRY_SIZE (7)
#define TGA_ERR_BAD_COLORMAP (8)
#define TGA_ERR_READ_FAILS (9)
#define TGA_ERR_BAD_IMAGE_TYPE (10)
#define TGA_ERR_BAD_DIMENSIONS (11)
static uint32 TargaError;
static int16 ttohs( int16 val );
static int16 htots( int16 val );
static int32 ttohl( int32 val );
static int32 htotl( int32 val );
static uint32 tga_get_pixel( FILE * tga, ubyte bytes_per_pix,
ubyte * colormap, ubyte cmap_bytes_entry );
static uint32 tga_convert_color( uint32 pixel, uint32 bpp_in, ubyte alphabits, uint32 format_out );
static void tga_write_pixel_to_mem( ubyte * dat, ubyte img_spec, uint32 number,
uint32 w, uint32 h, uint32 pixel, uint32 format );
/* returns the last error encountered */
int tga_get_last_error() {
return( TargaError );
}
/* returns a pointer to the string for an error code */
const char * tga_error_string( int error_code ) {
switch( error_code ) {
case TGA_ERR_NONE:
return( "no error" );
case TGA_ERR_BAD_HEADER:
return( "bad image header" );
case TGA_ERR_OPEN_FAILS:
return( "cannot open file" );
case TGA_ERR_BAD_FORMAT:
return( "bad format argument" );
case TGA_ERR_UNEXPECTED_EOF:
return( "unexpected end-of-file" );
case TGA_ERR_NODATA_IMAGE:
return( "image contains no data" );
case TGA_ERR_COLORMAP_FOR_GRAY:
return( "found colormap for a grayscale image" );
case TGA_ERR_BAD_COLORMAP_ENTRY_SIZE:
return( "unsupported colormap entry size" );
case TGA_ERR_BAD_COLORMAP:
return( "bad colormap" );
case TGA_ERR_READ_FAILS:
return( "cannot read from file" );
case TGA_ERR_BAD_IMAGE_TYPE:
return( "unknown image type" );
case TGA_ERR_BAD_DIMENSIONS:
return( "image has size 0 width or height (or both)" );
default:
return( "unknown error" );
}
// shut up compiler..
return( NULL );
}
/* creates a targa image of the desired format */
void * tga_create( int width, int height, unsigned int format ) {
switch( format ) {
case TGA_TRUECOLOR_32:
return( (void *)malloc( width * height * 4 ) );
case TGA_TRUECOLOR_24:
return( (void *)malloc( width * height * 3 ) );
default:
TargaError = TGA_ERR_BAD_FORMAT;
break;
}
return( NULL );
}
/* loads and converts a targa from disk */
void * tga_load( const char * filename,
int * width, int * height, unsigned int format ) {
ubyte idlen; // length of the image_id string below.
ubyte cmap_type; // paletted image <=> cmap_type
ubyte image_type; // can be any of the IMG_TYPE constants above.
uint16 cmap_first; //
uint16 cmap_length; // how long the colormap is
ubyte cmap_entry_size; // how big a palette entry is.
uint16 img_spec_xorig; // the x origin of the image in the image data.
uint16 img_spec_yorig; // the y origin of the image in the image data.
uint16 img_spec_width; // the width of the image.
uint16 img_spec_height; // the height of the image.
ubyte img_spec_pix_depth; // the depth of a pixel in the image.
ubyte img_spec_img_desc; // the image descriptor.
FILE * targafile;
ubyte * tga_hdr = NULL;
ubyte * colormap = NULL;
//***********************************************************************
// Add by Yu-Chi because of variable initialization.
// Add all = 0 to all the following variables
//***********************************************************************
ubyte cmap_bytes_entry = 0;
uint32 cmap_bytes = 0;
uint32 tmp_col = 0;
uint32 tmp_int32 = 0;
ubyte tmp_byte = 0;
ubyte alphabits = 0;
uint32 num_pixels = 0;
uint32 i = 0;
uint32 j = 0;
ubyte * image_data = 0;
uint32 img_dat_len = 0;
ubyte bytes_per_pix = 0;
ubyte true_bits_per_pixel = 0;
uint32 bytes_total = 0;
ubyte packet_header = 0;
ubyte repcount = 0;
switch( format ) {
case TGA_TRUECOLOR_24:
case TGA_TRUECOLOR_32:
break;
default:
TargaError = TGA_ERR_BAD_FORMAT;
return( NULL );
}
/* open binary image file */
targafile = fopen( filename, "rb" );
if( targafile == NULL ) {
TargaError = TGA_ERR_OPEN_FAILS;
return( NULL );
}
/* allocate memory for the header */
tga_hdr = (ubyte *)malloc( HDR_LENGTH );
/* read the header in. */
if( fread( (void *)tga_hdr, 1, HDR_LENGTH, targafile ) != HDR_LENGTH ) {
free( tga_hdr );
TargaError = TGA_ERR_BAD_HEADER;
return( NULL );
}
/* byte order is important here. */
idlen = (ubyte)tga_hdr[HDR_IDLEN];
image_type = (ubyte)tga_hdr[HDR_IMAGE_TYPE];
cmap_type = (ubyte)tga_hdr[HDR_CMAP_TYPE];
cmap_first = ttohs( *(uint16 *)(&tga_hdr[HDR_CMAP_FIRST]) );
cmap_length = ttohs( *(uint16 *)(&tga_hdr[HDR_CMAP_LENGTH]) );
cmap_entry_size = (ubyte)tga_hdr[HDR_CMAP_ENTRY_SIZE];
img_spec_xorig = ttohs( *(uint16 *)(&tga_hdr[HDR_IMG_SPEC_XORIGIN]) );
img_spec_yorig = ttohs( *(uint16 *)(&tga_hdr[HDR_IMG_SPEC_YORIGIN]) );
img_spec_width = ttohs( *(uint16 *)(&tga_hdr[HDR_IMG_SPEC_WIDTH]) );
img_spec_height = ttohs( *(uint16 *)(&tga_hdr[HDR_IMG_SPEC_HEIGHT]) );
img_spec_pix_depth = (ubyte)tga_hdr[HDR_IMG_SPEC_PIX_DEPTH];
img_spec_img_desc = (ubyte)tga_hdr[HDR_IMG_SPEC_IMG_DESC];
free( tga_hdr );
num_pixels = img_spec_width * img_spec_height;
if( num_pixels == 0 ) {
TargaError = TGA_ERR_BAD_DIMENSIONS;
return( NULL );
}
alphabits = img_spec_img_desc & 0x0F;
/* seek past the image id, if there is one */
if( idlen ) {
if( fseek( targafile, idlen, SEEK_CUR ) ) {
TargaError = TGA_ERR_UNEXPECTED_EOF;
return( NULL );
}
}
/* if this is a 'nodata' image, just jump out. */
if( image_type == TGA_IMG_NODATA ) {
TargaError = TGA_ERR_NODATA_IMAGE;
return( NULL );
}
/* now we're starting to get into the meat of the matter. */
/* deal with the colormap, if there is one. */
if( cmap_type ) {
switch( image_type ) {
case TGA_IMG_UNC_PALETTED:
case TGA_IMG_RLE_PALETTED:
break;
case TGA_IMG_UNC_TRUECOLOR:
case TGA_IMG_RLE_TRUECOLOR:
// this should really be an error, but some really old
// crusty targas might actually be like this (created by TrueVision, no less!)
// so, we'll hack our way through it.
break;
case TGA_IMG_UNC_GRAYSCALE:
case TGA_IMG_RLE_GRAYSCALE:
TargaError = TGA_ERR_COLORMAP_FOR_GRAY;
return( NULL );
}
/* ensure colormap entry size is something we support */
if( !(cmap_entry_size == 15 ||
cmap_entry_size == 16 ||
cmap_entry_size == 24 ||
cmap_entry_size == 32) ) {
TargaError = TGA_ERR_BAD_COLORMAP_ENTRY_SIZE;
return( NULL );
}
/* allocate memory for a colormap */
if( cmap_entry_size & 0x07 ) {
cmap_bytes_entry = (((8 - (cmap_entry_size & 0x07)) + cmap_entry_size) >> 3);
} else {
cmap_bytes_entry = (cmap_entry_size >> 3);
}
cmap_bytes = cmap_bytes_entry * cmap_length;
colormap = (ubyte *)malloc( cmap_bytes );
for( i = 0; i < cmap_length; i++ ) {
/* seek ahead to first entry used */
if( cmap_first != 0 ) {
fseek( targafile, cmap_first * cmap_bytes_entry, SEEK_CUR );
}
tmp_int32 = 0;
for( j = 0; j < cmap_bytes_entry; j++ ) {
if( !fread( &tmp_byte, 1, 1, targafile ) ) {
free( colormap );
TargaError = TGA_ERR_BAD_COLORMAP;
return( NULL );
}
tmp_int32 += tmp_byte << (j * 8);
}
// byte order correct.
tmp_int32 = ttohl( tmp_int32 );
for( j = 0; j < cmap_bytes_entry; j++ ) {
colormap[i * cmap_bytes_entry + j] = (tmp_int32 >> (8 * j)) & 0xFF;
}
}
}
// compute number of bytes in an image data unit (either index or BGR triple)
if( img_spec_pix_depth & 0x07 ) {
bytes_per_pix = (((8 - (img_spec_pix_depth & 0x07)) + img_spec_pix_depth) >> 3);
} else {
bytes_per_pix = (img_spec_pix_depth >> 3);
}
/* assume that there's one byte per pixel */
if( bytes_per_pix == 0 ) {
bytes_per_pix = 1;
}
/* compute how many bytes of storage we need for the image */
bytes_total = img_spec_width * img_spec_height * format;
image_data = (ubyte *)malloc( bytes_total );
img_dat_len = img_spec_width * img_spec_height * bytes_per_pix;
// compute the true number of bits per pixel
true_bits_per_pixel = cmap_type ? cmap_entry_size : img_spec_pix_depth;
switch( image_type ) {
case TGA_IMG_UNC_TRUECOLOR:
case TGA_IMG_UNC_GRAYSCALE:
case TGA_IMG_UNC_PALETTED:
/* FIXME: support grayscale */
for( i = 0; i < num_pixels; i++ ) {
// get the color value.
tmp_col = tga_get_pixel( targafile, bytes_per_pix, colormap, cmap_bytes_entry );
tmp_col = tga_convert_color( tmp_col, true_bits_per_pixel, alphabits, format );
// now write the data out.
tga_write_pixel_to_mem( image_data, img_spec_img_desc,
i, img_spec_width, img_spec_height, tmp_col, format );
}
break;
case TGA_IMG_RLE_TRUECOLOR:
case TGA_IMG_RLE_GRAYSCALE:
case TGA_IMG_RLE_PALETTED:
// FIXME: handle grayscale..
for( i = 0; i < num_pixels; ) {
/* a bit of work to do to read the data.. */
if( fread( &packet_header, 1, 1, targafile ) < 1 ) {
// well, just let them fill the rest with null pixels then...
packet_header = 1;
}
if( packet_header & 0x80 ) {
/* run length packet */
tmp_col = tga_get_pixel( targafile, bytes_per_pix, colormap, cmap_bytes_entry );
tmp_col = tga_convert_color( tmp_col, true_bits_per_pixel, alphabits, format );
repcount = (packet_header & 0x7F) + 1;
/* write all the data out */
for( j = 0; j < repcount; j++ ) {
tga_write_pixel_to_mem( image_data, img_spec_img_desc,
i + j, img_spec_width, img_spec_height, tmp_col, format );
}
i += repcount;
} else {
/* raw packet */
/* get pixel from file */
repcount = (packet_header & 0x7F) + 1;
for( j = 0; j < repcount; j++ ) {
tmp_col = tga_get_pixel( targafile, bytes_per_pix, colormap, cmap_bytes_entry );
tmp_col = tga_convert_color( tmp_col, true_bits_per_pixel, alphabits, format );
tga_write_pixel_to_mem( image_data, img_spec_img_desc,
i + j, img_spec_width, img_spec_height, tmp_col, format );
}
i += repcount;
}
}
break;
default:
TargaError = TGA_ERR_BAD_IMAGE_TYPE;
return( NULL );
}
fclose( targafile );
*width = img_spec_width;
*height = img_spec_height;
return( (void *)image_data );
}
int tga_write_raw( const char * file, int width, int height, unsigned char * dat, unsigned int format ) {
FILE * tga;
uint32 i, j;
uint32 size = width * height;
float red, green, blue, alpha;
char id[] = "written with libtarga";
ubyte idlen = 21;
ubyte zeroes[5] = { 0, 0, 0, 0, 0 };
uint32 pixbuf;
ubyte one = 1;
ubyte cmap_type = 0;
ubyte img_type = 2; // 2 - uncompressed truecolor 10 - RLE truecolor
uint16 xorigin = 0;
uint16 yorigin = 0;
ubyte pixdepth = format * 8; // bpp
ubyte img_desc;
switch( format ) {
case TGA_TRUECOLOR_24:
img_desc = 0;
break;
case TGA_TRUECOLOR_32:
img_desc = 8;
break;
default:
TargaError = TGA_ERR_BAD_FORMAT;
return( 0 );
break;
}
tga = fopen( file, "wb" );
if( tga == NULL ) {
TargaError = TGA_ERR_OPEN_FAILS;
return( 0 );
}
// write id length
fwrite( &idlen, 1, 1, tga );
// write colormap type
fwrite( &cmap_type, 1, 1, tga );
// write image type
fwrite( &img_type, 1, 1, tga );
// write cmap spec.
fwrite( &zeroes, 5, 1, tga );
// write image spec.
fwrite( &xorigin, 2, 1, tga );
fwrite( &yorigin, 2, 1, tga );
fwrite( &width, 2, 1, tga );
fwrite( &height, 2, 1, tga );
fwrite( &pixdepth, 1, 1, tga );
fwrite( &img_desc, 1, 1, tga );
// write image id.
fwrite( &id, idlen, 1, tga );
// color correction -- data is in RGB, need BGR.
for( i = 0; i < size; i++ ) {
pixbuf = 0;
for( j = 0; j < format; j++ ) {
pixbuf += dat[i*format+j] << (8 * j);
}
switch( format ) {
case TGA_TRUECOLOR_24:
pixbuf = ((pixbuf & 0xFF) << 16) +
(pixbuf & 0xFF00) +
((pixbuf & 0xFF0000) >> 16);
pixbuf = htotl( pixbuf );
fwrite( &pixbuf, 3, 1, tga );
break;
case TGA_TRUECOLOR_32:
/* need to un-premultiply alpha.. */
red = (pixbuf & 0xFF) / 255.0f;
green = ((pixbuf & 0xFF00) >> 8) / 255.0f;
blue = ((pixbuf & 0xFF0000) >> 16) / 255.0f;
alpha = ((pixbuf & 0xFF000000) >> 24) / 255.0f;
if( alpha > 0.0001 ) {
red /= alpha;
green /= alpha;
blue /= alpha;
}
/* clamp to 1.0f */
red = red > 1.0f ? 255.0f : red * 255.0f;
green = green > 1.0f ? 255.0f : green * 255.0f;
blue = blue > 1.0f ? 255.0f : blue * 255.0f;
alpha = alpha > 1.0f ? 255.0f : alpha * 255.0f;
pixbuf = (ubyte)blue + (((ubyte)green) << 8) +
(((ubyte)red) << 16) + (((ubyte)alpha) << 24);
pixbuf = htotl( pixbuf );
fwrite( &pixbuf, 4, 1, tga );
break;
}
}
fclose( tga );
return( 1 );
}
int tga_write_rle( const char * file, int width, int height, unsigned char * dat, unsigned int format ) {
FILE * tga;
uint32 i, j;
uint32 oc, nc;
enum RLE_STATE { INIT, NONE, RLP, RAWP };
int state = INIT;
uint32 size = width * height;
uint16 shortwidth = (uint16)width;
uint16 shortheight = (uint16)height;
ubyte repcount;
float red, green, blue, alpha;
int idx, row, column;
// have to buffer a whole line for raw packets.
unsigned char * rawbuf = (unsigned char *)malloc( width * format );
char id[] = "written with libtarga";
ubyte idlen = 21;
ubyte zeroes[5] = { 0, 0, 0, 0, 0 };
uint32 pixbuf;
ubyte one = 1;
ubyte cmap_type = 0;
ubyte img_type = 10; // 2 - uncompressed truecolor 10 - RLE truecolor
uint16 xorigin = 0;
uint16 yorigin = 0;
ubyte pixdepth = format * 8; // bpp
ubyte img_desc = format == TGA_TRUECOLOR_32 ? 8 : 0;
switch( format ) {
case TGA_TRUECOLOR_24:
case TGA_TRUECOLOR_32:
break;
default:
TargaError = TGA_ERR_BAD_FORMAT;
return( 0 );
}
tga = fopen( file, "wb" );
if( tga == NULL ) {
TargaError = TGA_ERR_OPEN_FAILS;
return( 0 );
}
// write id length
fwrite( &idlen, 1, 1, tga );
// write colormap type
fwrite( &cmap_type, 1, 1, tga );
// write image type
fwrite( &img_type, 1, 1, tga );
// write cmap spec.
fwrite( &zeroes, 5, 1, tga );
// write image spec.
fwrite( &xorigin, 2, 1, tga );
fwrite( &yorigin, 2, 1, tga );
fwrite( &shortwidth, 2, 1, tga );
fwrite( &shortheight, 2, 1, tga );
fwrite( &pixdepth, 1, 1, tga );
fwrite( &img_desc, 1, 1, tga );
// write image id.
fwrite( &id, idlen, 1, tga );
// initial color values -- just to shut up the compiler.
nc = 0;
// color correction -- data is in RGB, need BGR.
// also run-length-encoding.
for( i = 0; i < size; i++ ) {
idx = i * format;
row = i / width;
column = i % width;
//printf( "row: %d, col: %d\n", row, column );
pixbuf = 0;
for( j = 0; j < format; j++ ) {
pixbuf += dat[idx+j] << (8 * j);
}
switch( format ) {
case TGA_TRUECOLOR_24:
pixbuf = ((pixbuf & 0xFF) << 16) +
(pixbuf & 0xFF00) +
((pixbuf & 0xFF0000) >> 16);
pixbuf = htotl( pixbuf );
break;
case TGA_TRUECOLOR_32:
/* need to un-premultiply alpha.. */
red = (pixbuf & 0xFF) / 255.0f;
green = ((pixbuf & 0xFF00) >> 8) / 255.0f;
blue = ((pixbuf & 0xFF0000) >> 16) / 255.0f;
alpha = ((pixbuf & 0xFF000000) >> 24) / 255.0f;
if( alpha > 0.0001 ) {
red /= alpha;
green /= alpha;
blue /= alpha;
}
/* clamp to 1.0f */
red = red > 1.0f ? 255.0f : red * 255.0f;
green = green > 1.0f ? 255.0f : green * 255.0f;
blue = blue > 1.0f ? 255.0f : blue * 255.0f;
alpha = alpha > 1.0f ? 255.0f : alpha * 255.0f;
pixbuf = (ubyte)blue + (((ubyte)green) << 8) +
(((ubyte)red) << 16) + (((ubyte)alpha) << 24);
pixbuf = htotl( pixbuf );
break;
}
oc = nc;
nc = pixbuf;
switch( state ) {
case INIT:
// this is just used to make sure we have 2 pixel values to consider.
state = NONE;
break;
case NONE:
if( column == 0 ) {
// write a 1 pixel raw packet for the old pixel, then go thru again.
repcount = 0;
fwrite( &repcount, 1, 1, tga );
#ifdef WORDS_BIGENDIAN
fwrite( (&oc)+4, format, 1, tga ); // byte order..
#else
fwrite( &oc, format, 1, tga );
#endif
state = NONE;
break;
}
if( nc == oc ) {
repcount = 0;
state = RLP;
} else {
repcount = 0;
state = RAWP;
for( j = 0; j < format; j++ ) {
#ifdef WORDS_BIGENDIAN
rawbuf[(repcount * format) + j] = (ubyte)(*((&oc)+format-j-1));
#else
rawbuf[(repcount * format) + j] = *(((ubyte *)(&oc)) + j);
#endif
}
}
break;
case RLP:
repcount++;
if( column == 0 ) {
// finish off rlp.
repcount |= 0x80;
fwrite( &repcount, 1, 1, tga );
#ifdef WORDS_BIGENDIAN
fwrite( (&oc)+4, format, 1, tga ); // byte order..
#else
fwrite( &oc, format, 1, tga );
#endif
state = NONE;
break;
}
if( repcount == 127 ) {
// finish off rlp.
repcount |= 0x80;
fwrite( &repcount, 1, 1, tga );
#ifdef WORDS_BIGENDIAN
fwrite( (&oc)+4, format, 1, tga ); // byte order..
#else
fwrite( &oc, format, 1, tga );
#endif
state = NONE;
break;
}
if( nc != oc ) {
// finish off rlp
repcount |= 0x80;
fwrite( &repcount, 1, 1, tga );
#ifdef WORDS_BIGENDIAN
fwrite( (&oc)+4, format, 1, tga ); // byte order..
#else
fwrite( &oc, format, 1, tga );
#endif
state = NONE;
}
break;
case RAWP:
repcount++;
if( column == 0 ) {
// finish off rawp.
for( j = 0; j < format; j++ ) {
#ifdef WORDS_BIGENDIAN
rawbuf[(repcount * format) + j] = (ubyte)(*((&oc)+format-j-1));
#else
rawbuf[(repcount * format) + j] = *(((ubyte *)(&oc)) + j);
#endif
}
fwrite( &repcount, 1, 1, tga );
fwrite( rawbuf, (repcount + 1) * format, 1, tga );
state = NONE;
break;
}
if( repcount == 127 ) {
// finish off rawp.
for( j = 0; j < format; j++ ) {
#ifdef WORDS_BIGENDIAN
rawbuf[(repcount * format) + j] = (ubyte)(*((&oc)+format-j-1));
#else
rawbuf[(repcount * format) + j] = *(((ubyte *)(&oc)) + j);
#endif
}
fwrite( &repcount, 1, 1, tga );
fwrite( rawbuf, (repcount + 1) * format, 1, tga );
state = NONE;
break;
}
if( nc == oc ) {
// finish off rawp
repcount--;
fwrite( &repcount, 1, 1, tga );
fwrite( rawbuf, (repcount + 1) * format, 1, tga );
// start new rlp
repcount = 0;
state = RLP;
break;
}
// continue making rawp
for( j = 0; j < format; j++ ) {
#ifdef WORDS_BIGENDIAN
rawbuf[(repcount * format) + j] = (ubyte)(*((&oc)+format-j-1));
#else
rawbuf[(repcount * format) + j] = *(((ubyte *)(&oc)) + j);
#endif
}
break;
}
}
// clean up state.
switch( state ) {
case INIT:
break;
case NONE:
// write the last 2 pixels in a raw packet.
fwrite( &one, 1, 1, tga );
#ifdef WORDS_BIGENDIAN
fwrite( (&oc)+4, format, 1, tga ); // byte order..
#else
fwrite( &oc, format, 1, tga );
#endif
#ifdef WORDS_BIGENDIAN
fwrite( (&nc)+4, format, 1, tga ); // byte order..
#else
fwrite( &nc, format, 1, tga );
#endif
break;
case RLP:
repcount++;
repcount |= 0x80;
fwrite( &repcount, 1, 1, tga );
#ifdef WORDS_BIGENDIAN
fwrite( (&oc)+4, format, 1, tga ); // byte order..
#else
fwrite( &oc, format, 1, tga );
#endif
break;
case RAWP:
repcount++;
for( j = 0; j < format; j++ ) {
#ifdef WORDS_BIGENDIAN
rawbuf[(repcount * format) + j] = (ubyte)(*((&oc)+format-j-1));
#else
rawbuf[(repcount * format) + j] = *(((ubyte *)(&oc)) + j);
#endif
}
fwrite( &repcount, 1, 1, tga );
fwrite( rawbuf, (repcount + 1) * 3, 1, tga );
break;
}
// close the file.
fclose( tga );
free( rawbuf );
return( 1 );
}
/*************************************************************************************************/
static void tga_write_pixel_to_mem( ubyte * dat, ubyte img_spec, uint32 number,
uint32 w, uint32 h, uint32 pixel, uint32 format ) {
// write the pixel to the data regarding how the
// header says the data is ordered.
uint32 j;
uint32 x, y;
uint32 addy;
switch( (img_spec & 0x30) >> 4 ) {
case TGA_LOWER_RIGHT:
x = w - 1 - (number % w);
y = number / h;
break;
case TGA_UPPER_LEFT:
x = number % w;
y = h - 1 - (number / w);
break;
case TGA_UPPER_RIGHT:
x = w - 1 - (number % w);
y = h - 1 - (number / w);
break;
case TGA_LOWER_LEFT:
default:
x = number % w;
y = number / w;
break;
}
addy = (y * w + x) * format;
for( j = 0; j < format; j++ ) {
dat[addy + j] = (ubyte)((pixel >> (j * 8)) & 0xFF);
}
}
static uint32 tga_get_pixel( FILE * tga, ubyte bytes_per_pix,
ubyte * colormap, ubyte cmap_bytes_entry ) {
/* get the image data value out */
uint32 tmp_col;
uint32 tmp_int32;
ubyte tmp_byte;
uint32 j;
tmp_int32 = 0;
for( j = 0; j < bytes_per_pix; j++ ) {
if( fread( &tmp_byte, 1, 1, tga ) < 1 ) {
tmp_int32 = 0;
} else {
tmp_int32 += tmp_byte << (j * 8);
}
}
/* byte-order correct the thing */
switch( bytes_per_pix ) {
case 2:
tmp_int32 = ttohs( (uint16)tmp_int32 );
break;
case 3: /* intentional fall-thru */
case 4:
tmp_int32 = ttohl( tmp_int32 );
break;
}
if( colormap != NULL ) {
/* need to look up value to get real color */
tmp_col = 0;
for( j = 0; j < cmap_bytes_entry; j++ ) {
tmp_col += colormap[cmap_bytes_entry * tmp_int32 + j] << (8 * j);
}
} else {
tmp_col = tmp_int32;
}
return( tmp_col );
}
static uint32 tga_convert_color( uint32 pixel, uint32 bpp_in, ubyte alphabits, uint32 format_out ) {
// this is not only responsible for converting from different depths
// to other depths, it also switches BGR to RGB.
// this thing will also premultiply alpha, on a pixel by pixel basis.
ubyte r, g, b, a;
switch( bpp_in ) {
case 32:
if( alphabits == 0 ) {
goto is_24_bit_in_disguise;
}
// 32-bit to 32-bit -- nop.
break;
case 24:
is_24_bit_in_disguise:
// 24-bit to 32-bit; (only force alpha to full)
pixel |= 0xFF000000;
break;
case 15:
is_15_bit_in_disguise:
r = (ubyte)(((float)((pixel & 0x7C00) >> 10)) * 8.2258f);
g = (ubyte)(((float)((pixel & 0x03E0) >> 5 )) * 8.2258f);
b = (ubyte)(((float)(pixel & 0x001F)) * 8.2258f);
// 15-bit to 32-bit; (force alpha to full)
pixel = 0xFF000000 + (r << 16) + (g << 8) + b;
break;
case 16:
if( alphabits == 1 ) {
goto is_15_bit_in_disguise;
}
// 16-bit to 32-bit; (force alpha to full)
r = (ubyte)(((float)((pixel & 0xF800) >> 11)) * 8.2258f);
g = (ubyte)(((float)((pixel & 0x07E0) >> 5 )) * 4.0476f);
b = (ubyte)(((float)(pixel & 0x001F)) * 8.2258f);
pixel = 0xFF000000 + (r << 16) + (g << 8) + b;
break;
}
// convert the 32-bit pixel from BGR to RGB.
pixel = (pixel & 0xFF00FF00) + ((pixel & 0xFF) << 16) + ((pixel & 0xFF0000) >> 16);
r = pixel & 0x000000FF;
g = (pixel & 0x0000FF00) >> 8;
b = (pixel & 0x00FF0000) >> 16;
a = (pixel & 0xFF000000) >> 24;
// not premultiplied alpha -- multiply.
r = (ubyte)(((float)r / 255.0f) * ((float)a / 255.0f) * 255.0f);
g = (ubyte)(((float)g / 255.0f) * ((float)a / 255.0f) * 255.0f);
b = (ubyte)(((float)b / 255.0f) * ((float)a / 255.0f) * 255.0f);
pixel = r + (g << 8) + (b << 16) + (a << 24);
/* now convert from 32-bit to whatever they want. */
switch( format_out ) {
case TGA_TRUECOLOR_32:
// 32 to 32 -- nop.
break;
case TGA_TRUECOLOR_24:
// 32 to 24 -- discard alpha.
pixel &= 0x00FFFFFF;
break;
}
return( pixel );
}
static int16 ttohs( int16 val ) {
#ifdef WORDS_BIGENDIAN
return( ((val & 0xFF) << 8) + (val >> 8) );
#else
return( val );
#endif
}
static int16 htots( int16 val ) {
#ifdef WORDS_BIGENDIAN
return( ((val & 0xFF) << 8) + (val >> 8) );
#else
return( val );
#endif
}
static int32 ttohl( int32 val ) {
#ifdef WORDS_BIGENDIAN
return( ((val & 0x000000FF) << 24) +
((val & 0x0000FF00) << 8) +
((val & 0x00FF0000) >> 8) +
((val & 0xFF000000) >> 24) );
#else
return( val );
#endif
}
static int32 htotl( int32 val ) {
#ifdef WORDS_BIGENDIAN
return( ((val & 0x000000FF) << 24) +
((val & 0x0000FF00) << 8) +
((val & 0x00FF0000) >> 8) +
((val & 0xFF000000) >> 24) );
#else
return( val );
#endif
}