diff --git a/CMakeLists.txt b/CMakeLists.txt index dfbf35e..f89a820 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,4 +3,4 @@ project(caff_previewer C) set(CMAKE_C_STANDARD 11) -add_executable(caff_previewer src/main.c src/magic_memory.c src/magic_memory.h) \ No newline at end of file +add_executable(caff_previewer src/main.c src/magic_memory.c src/magic_memory.h src/libtarga.c src/libtarga.h) \ No newline at end of file diff --git a/src/libtarga.c b/src/libtarga.c new file mode 100644 index 0000000..d2d6064 --- /dev/null +++ b/src/libtarga.c @@ -0,0 +1,1261 @@ + +/************************************************************************** + ** 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 +#include + +#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 + +} diff --git a/src/libtarga.h b/src/libtarga.h new file mode 100644 index 0000000..57aeffe --- /dev/null +++ b/src/libtarga.h @@ -0,0 +1,133 @@ +#ifndef _libtarga_h_ +#define _libtarga_h_ + +/************************************************************************** + ** 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. + **/ + +/* uncomment this line if you're compiling on a big-endian machine */ +/* #define WORDS_BIGENDIAN */ + + +/* make sure these types reflect your system's type sizes. */ +#define byte char +#define int32 int +#define int16 short + +#define ubyte unsigned byte +#define uint32 unsigned int32 +#define uint16 unsigned int16 + + + +/* + Truecolor images supported: + + bits breakdown components + -------------------------------------- + 32 8-8-8-8 RGBA + 24 8-8-8 RGB + 16 5-6-5 RGB + 15 5-5-5-1 RGB (ignore extra bit) + + + Paletted images supported: + + index size palette entry breakdown components + ------------------------------------------------------ + 8 .. + 16 .. + 24 .. + +*/ + + + +/* + + Targa files are read in and converted to + any of these three for you -- you choose which you want. + + This is the 'format' argument to tga_create/load/write. + + For create and load, format is what you want the data + converted to. + + For write, format is what format the data you're writing + is in. (NOT the format you want written) + + Only TGA_TRUECOLOR_32 supports an alpha channel. + +*/ + +#define TGA_TRUECOLOR_32 (4) +#define TGA_TRUECOLOR_24 (3) + + +/* + Image data will start in the low-left corner + of the image. +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Error handling routines */ +int tga_get_last_error(); +const char * tga_error_string( int error_code ); + + +/* Creating/Loading images -- a return of NULL indicates a fatal error */ +void * tga_create( int width, int height, unsigned int format ); +void * tga_load( const char * file, int * width, int * height, unsigned int format ); + + +/* Writing images to file -- a return of 1 indicates success, 0 indicates error*/ +int tga_write_raw( const char * file, int width, int height, unsigned char * dat, unsigned int format ); +int tga_write_rle( const char * file, int width, int height, unsigned char * dat, unsigned int format ); + + + +#ifdef __cplusplus +} +#endif + + +#endif /* _libtarga_h_ */ diff --git a/src/main.c b/src/main.c index fce5704..ef85258 100644 --- a/src/main.c +++ b/src/main.c @@ -1,15 +1,24 @@ #include #include +#include +#include "libtarga.h" #include "magic_memory.h" int main() { - magic_memory_t* magic_run = magic_memory_begin(); + magic_memory_context_t *magic_run = magic_memory_begin(); + + uint8_t *picture = (uint8_t *) magic_malloc(magic_run, 50 * 50 * 3); + for (uint16_t i = 0; i < (50*50);i++) { + picture[(i*3)] = 0; + picture[(i*3)+1] = (i % 100)*25; + picture[(i*3)+2] = 255; + } + + if (!tga_write_raw("test.tga", 50, 50, picture, TGA_TRUECOLOR_24)) { + printf("%s", tga_error_string(tga_get_last_error())); + } - char* str = (char*)magic_malloc(magic_run, 120); - strcpy(str,"Test string! Hello world!!"); - printf("%s",str); - //magic_free(magic_run, str); magic_cleanup(magic_run); // Free all memory used automagically return 0;