From 0273b9172a108523dd6be5887b41a1d3b3f4ecc6 Mon Sep 17 00:00:00 2001 From: Neszt Tibor Date: Sat, 25 Jun 2011 22:08:55 +0200 Subject: [PATCH] Initial commit img_merge added to git --- .gitignore | 2 + Makefile | 23 ++ img_merge.c | 339 ++++++++++++++++++ img_merge.h | 33 ++ targa.c | 1102 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ targa.h | 202 +++++++++++ 6 files changed, 1701 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 img_merge.c create mode 100644 img_merge.h create mode 100644 targa.c create mode 100644 targa.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a4dee17 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.o +img_merge diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..de4b1d4 --- /dev/null +++ b/Makefile @@ -0,0 +1,23 @@ +INC = +CFLAGS = $(INC) +CXXFLAGS = $(INC) -Wall -Wextra -W +LDFLAGS = + +C_SOURCES = img_merge.c +C_OBJS = $(C_SOURCES:.c=.o) +CXX_SOURCES = +CXX_OBJS = $(CXX_SOURCES:.cpp=.o) +PROG = img_merge + +C_SOURCES += targa.c + + +all: $(PROG) + +$(PROG): $(C_OBJS) $(CXX_OBJS) + $(CXX) -o $@ $(C_OBJS) $(CXX_OBJS) $(LDFLAGS) + +clean: + rm -f $(PROG) $(C_OBJS) $(CXX_OBJS) *~ + +.PHONY: all clean diff --git a/img_merge.c b/img_merge.c new file mode 100644 index 0000000..0860bc4 --- /dev/null +++ b/img_merge.c @@ -0,0 +1,339 @@ +/* ---------------------------------------- */ +/* - Image merger for tga files ----------- */ +/* - Coded by Neszt Tibor ----------------- */ +/* ---------------------------------------- */ + +#include "img_merge.h" +#include "stdlib.h" + +/* ----------------------------------------------- */ +void print_help(void) { + + printf("Usage: img_merge -p1 pic1 -c1 brightnes1 .. .. -s step -o outbasename\n"); + + printf(" Usage: img_merge\n"); + printf(" -n number of images to merge (1-999)\n"); + printf(" (optional field)\n"); + printf(" -p[n] image filename of n (only tga files allowed)\n"); + printf(" (optional, except the first filename)\n"); + printf(" -c[n|a] percent for image number n\n"); + printf(" (default is 100)\n"); + printf(" -t merge mode: S (summ), X (max), N (min), A (average), D (difference)\n"); + printf(" (default is S)\n"); + printf(" -s, -step number of step after each round\n"); + printf(" (default is 1)\n"); + printf(" -f, -framestep number of step between each frame\n"); + printf(" (default is 1)\n"); + printf(" -o, -outbasename\n"); + printf(" (default is \"res\", that means res00000.tga and so on.. )\n"); + printf(" -v, -verbose\n"); + printf(" (default is off)\n"); + printf(" -h, -help\n"); + printf(" This help.\n"); + printf("\n"); + printf(" those p[n]s, what is not defined will be automatically\n"); + printf(" set correspondingly as the last value. // later on!\n"); +} +/* ----------------------------------------------- */ +void scan_args(int argc, char **argv) { + int i, j, r, tmpint; + + #if debug>1 + printf("* Debug * Arguments being processed\n"); + #endif + for ( i=1 ; i i + 1 && isdigit(argv[i+1][0]) ) { + if ( num ) { + printf("Only one -n parameter allowed!\n", num); + exit(0); + } + if ( !p_base[0] ) { + printf("You must give -p firstpic before -n!\n", num); + exit(0); + } + num = atoi(&argv[i+1][0]); + if ( num > maxn ) { + printf("Then given -n parameter is over the maximun: %d\n", maxn); + exit(0); + } + #if debug>1 + printf("* Debug * Found -n: %d\n", num); + #endif + i++; + for ( j = 0 ; j < num ; j++ ) { + sprintf(p[j+1], "%s", p_base); + increment_str(p_base); + } + } + break; + + case 'p': + if ( isdigit(argv[i][2]) ) { + j = atoi(&argv[i][2]); + sprintf(p[j], "%s", argv[i+1]); + i++, num++; + #if debug>1 + printf("* Debug * Found -p%d : %s\n", j, p[j]); + #endif + } else if ( argc>i+1 ) { + sprintf(p_base, "%s", argv[i+1]); + #if debug>1 + printf("* Debug * Found -p global: %s\n", argv[i+1]); + #endif + } + break; + + case 'c': + #if debug>1 + printf("* Debug * Found -c : "); + #endif + if ( isdigit(argv[i][2]) && argc>i+1 && isdigit(argv[i+1][0]) ) { + #if debug>1 + printf("* Debug * Found -c%d : %d\n", atoi(&argv[i][2]), atoi(&argv[i+1][0])); + #endif + c[ atoi(&argv[i][2]) ] = atoi(&argv[i+1][0]), i++; + } else if ( argc>i+1 && isdigit(argv[i+1][0]) ) { + r = atoi( &argv[i+1][0] ); + for ( j = 0 ; j < maxn ; j++ ) { c[ j ] = r; }; + i++; + #if debug>1 + printf("* Debug * Found -c for all numbers : %d\n", r); + #endif + } else if ( argc>i+1 && argv[i+1][0] == 'a' ) { + c[0] = -1; + i++; + } + break; + + case 't': + #if debug>1 + printf("* Debug * Found -t\n"); + #endif + if ( argc>=i+1 ) { + if ( argv[i+1][0] == 'S' || argv[i+1][0] == 's' ) { + t = 0; + } else if ( argv[i+1][0] == 'X' || argv[i+1][0] == 'x' ) { + t = 1; + } else if ( argv[i+1][0] == 'N' || argv[i+1][0] == 'n' ) { + t = 2; + } else if ( argv[i+1][0] == 'A' || argv[i+1][0] == 'a' ) { + t = 3; + } else if ( argv[i+1][0] == 'D' || argv[i+1][0] == 'd' ) { + t = 4; + } else { + printf("Then given -t merge type (%s) is unknown!\n", &argv[i+1][0]); + exit(0); + } + #if debug>1 + printf("* Debug * Found -t : %c\n", argv[i+1][0]); + #endif + } + break; + + case 's': + if ( isdigit(argv[i+1][0]) ) { + #if debug>1 + printf("* Debug * Found -s : %d\n", atoi(&argv[i+1][0])); + #endif + step = atoi(&argv[i+1][0]), i++; + } + break; + + case 'f': + if ( isdigit(argv[i+1][0]) ) { + #if debug>1 + printf("* Debug * Found -f : %d\n", atoi(&argv[i+1][0])); + #endif + framestep = atoi(&argv[i+1][0]), i++; + } + break; + + case 'o': + #if debug>1 + printf("* Debug * Found -o : %s\n",argv[i+1]); + #endif + sprintf(outbasename, "%s", argv[i+1]); + i++; + break; + } + break; + case '?': + print_help(); + break; + default: + break; + } + if ( c[0] == -1 && num > 0 ) { + for ( j = 0 ; j < maxn ; j++ ) { c[ j ] = 100/num; }; + } + } + if (num<2) { + print_help(); + exit(0); + } +} +/* ----------------------------------------- */ +void merge_begin(uint8_t *img, int c) { + int i,j,h3i,j3, pos; + + for( i = 0 ; i < w ; i++ ) { + h3i = h * 3 * i; + for( j = 0 ; j < h ; j++ ) { + j3 = 3*j; + pos = h3i + j3; + outbuffer[pos+0] = c*img[pos+0]; + outbuffer[pos+1] = c*img[pos+1]; + outbuffer[pos+2] = c*img[pos+2]; + } + } +} +/* ----------------------------------------- */ +void merge(uint8_t *img, int c) { + int i,j,h3i,j3, pos; + + for( i = 0 ; i < w ; i++ ) { + h3i = h*3*i; + for( j = 0 ; j < h ; j++ ) { + j3 = 3 * j; + pos = h3i + j3; + if ( t == 1 ) { // max + if ( c*img[pos+0] > outbuffer[pos+0] ) { outbuffer[pos+0] = c*img[pos+0]; } + if ( c*img[pos+1] > outbuffer[pos+1] ) { outbuffer[pos+1] = c*img[pos+1]; } + if ( c*img[pos+2] > outbuffer[pos+2] ) { outbuffer[pos+2] = c*img[pos+2]; } + } else if ( t == 2 ) { // min + if ( c*img[pos+0] < outbuffer[pos+0] ) { outbuffer[pos+0] = c*img[pos+0]; } + if ( c*img[pos+1] < outbuffer[pos+1] ) { outbuffer[pos+1] = c*img[pos+1]; } + if ( c*img[pos+2] < outbuffer[pos+2] ) { outbuffer[pos+2] = c*img[pos+2]; } + } else if ( t == 4 ) { // diff + if ( c*img[pos+0] != outbuffer[pos+0] ) { outbuffer[pos+0] += c*img[pos+0]; } else { outbuffer[pos+0] = 0; } + if ( c*img[pos+1] != outbuffer[pos+1] ) { outbuffer[pos+1] += c*img[pos+1]; } else { outbuffer[pos+1] = 0; } + if ( c*img[pos+2] != outbuffer[pos+2] ) { outbuffer[pos+2] += c*img[pos+2]; } else { outbuffer[pos+2] = 0; } + } else { + outbuffer[pos+0] += c*img[pos+0]; + outbuffer[pos+1] += c*img[pos+1]; + outbuffer[pos+2] += c*img[pos+2]; + } + } + } +} +/* ----------------------------------------- */ +void merge_finish(void) { + int i,j,h3i,j3, pos; + + for( i = 0 ; i < w ; i++ ) { + h3i = h*3*i; + for( j = 0 ; j < h ; j++ ) { + j3 = 3 * j; + pos = h3i + j3; + if ( t == 3 ) { // avg + outimage[pos+0] = (uint8_t) ( outbuffer[pos+2]/100/num <= 255 ? outbuffer[pos+2]/100/num : 255 ); + outimage[pos+1] = (uint8_t) ( outbuffer[pos+1]/100/num <= 255 ? outbuffer[pos+1]/100/num : 255 ); + outimage[pos+2] = (uint8_t) ( outbuffer[pos+0]/100/num <= 255 ? outbuffer[pos+0]/100/num : 255 ); + } else { + outimage[pos+0] = (uint8_t) ( outbuffer[pos+2]/100 <= 255 ? outbuffer[pos+2]/100 : 255 ); + outimage[pos+1] = (uint8_t) ( outbuffer[pos+1]/100 <= 255 ? outbuffer[pos+1]/100 : 255 ); + outimage[pos+2] = (uint8_t) ( outbuffer[pos+0]/100 <= 255 ? outbuffer[pos+0]/100 : 255 ); + } + } + } +} +/* ----------------------------------------------- */ +void increment_str( char *p ) { + int i; + + for ( i = 0 ; p[i] != '\0' ; i++ ); + i-=5; + while ( (++p[i]) == ('9'+1) ) { + p[i] = '0'; + i--; + } +} +/* ----------------------------------------------- */ +void increment(void) { + int i,j; + + for ( i = 1 ; i < num + 1 ; i++ ) { + for ( j = 0 ; p[i][j] != '\0' ; j++ ); + j -= 5; + while ( (++p[i][j]) == ('9'+1) ) { + p[i][j] = '0'; + j--; + } + } +} +/* ----------------------------------------- */ +/* ----------------------------------------- */ +/* ----------------------------------------- */ +int main(int argc, char **argv) { + tga_image img1; + tga_image img2; + tga_result result; + int i, j; + + scan_args(argc, argv); + + #if debug>1 + printf("* Debug * Checking first file:%s\n",p[1]); + #endif + result = tga_read(&tga_images[1], p[1]); + if (result != TGA_NOERR) { + printf("Error opening first file!\n"); + exit(0); + } + if (!tga_is_top_to_bottom(&tga_images[1])) tga_flip_vert(&tga_images[1]); + w = tga_images[1].width; + h = tga_images[1].height; + #if debug>1 + printf("* Debug * Width:%d , Height:%d\n",w,h); + #endif + tga_free_buffers(&tga_images[1]); + + outimage = (uint8_t*) malloc( w*h*3*sizeof(uint8_t) ); + outbuffer = (int*) malloc( w*h*3*sizeof(int) ); + + while(step) { + for ( i = 1 ; i < num + 1 ; i += framestep ) { + result = tga_read(&tga_images[i], p[i]); + if (result == TGA_NOERR) { + if (!tga_is_top_to_bottom(&tga_images[i])) tga_flip_vert(&tga_images[i]); + if (!( tga_images[i].width==w && tga_images[i].height==h )) { + printf("Not identical dimensions!\n"); + exit(0); + } + image_datas[i] = tga_images[i].image_data; + if ( i == 1 ) { + merge_begin(image_datas[i], c[i]); + } else { + merge(image_datas[i], c[i]); + } + #if debug>1 + printf("* Debug * Image merged:%s - %d%%\n",p[i],c[i]); + #endif + tga_free_buffers(&tga_images[i]); + } else { + printf("Error opening image:%s!\n",p[i]); + exit(0); + } + } + merge_finish(); + sprintf(outname,"%s%05d.tga",outbasename,outindex++); + tga_write_rgb(outname, (uint8_t *) outimage, w, h, 24); + #if debug>1 + printf("* Debug * Image writen:%s\n",outname); + #endif + for ( j = 0 ; j < step ; j++ ) { increment(); } + } + + free(outimage); + free(outbuffer); + + return 0; +} diff --git a/img_merge.h b/img_merge.h new file mode 100644 index 0000000..214f04a --- /dev/null +++ b/img_merge.h @@ -0,0 +1,33 @@ +#ifndef _IMG_MERGE_H_ +#define _IMG_MERGE_H_ + +#include "targa.h" + +#define debug 0 +#define maxn 1000 +#define maxfilenamelenght 255 + +char p[maxn][maxfilenamelenght]; +char p_base[maxfilenamelenght] = ""; +char outbasename[maxfilenamelenght] = "res"; +char outname[maxfilenamelenght] = "res"; +int c[maxn]; +int t; // 0-S(sum), 1-X(max), 2-N(min), 3-A(average), 4-D(difference) +int num = 0; +int step = 1; +int framestep = 1; +int verbose = 0; +int w, h, outindex = 0; +int *outbuffer; +uint8_t *outimage, *image_datas[maxn]; +tga_image tga_images[maxn]; + +void print_help(void); +void scan_args(int argc, char **argv); +void merge_begin(uint8_t *img, int c); +void merge(uint8_t *img, int c); +void merge_finish(void); +void increment_str(char *); +void increment(void); + +#endif /* !_IMG_MERGE_H_ */ diff --git a/targa.c b/targa.c new file mode 100644 index 0000000..c932681 --- /dev/null +++ b/targa.c @@ -0,0 +1,1102 @@ +/* --------------------------------------------------------------------------- + * Truevision Targa Reader/Writer + * Copyright (C) 2001-2003, Emil Mikulic. + * + * Source and binary redistribution of this code, with or without changes, for + * free or for profit, is allowed as long as this copyright notice is kept + * intact. Modified versions must be clearly marked as modified. + * + * This code is provided without any warranty. The copyright holder is + * not liable for anything bad that might happen as a result of the + * code. + * -------------------------------------------------------------------------*/ + +/*@unused@*/ static const char rcsid[] = + "$Id: targa.c,v 1.7 2003/06/21 09:30:53 emikulic Exp $"; + +#define TGA_KEEP_MACROS /* BIT, htole16, letoh16 */ +#include "targa.h" +#include +#include /* memcpy, memcmp */ + +#define SANE_DEPTH(x) ((x) == 8 || (x) == 16 || (x) == 24 || (x) == 32) +#define UNMAP_DEPTH(x) ((x) == 16 || (x) == 24 || (x) == 32) + +static const char tga_id[] = + "\0\0\0\0" /* extension area offset */ + "\0\0\0\0" /* developer directory offset */ + "TRUEVISION-XFILE."; + +static const size_t tga_id_length = 26; /* tga_id + \0 */ + + + +/* helpers */ +static tga_result tga_read_rle(tga_image *dest, FILE *fp); +static tga_result tga_write_row_RLE(FILE *fp, + const tga_image *src, const uint8_t *row); +typedef enum { RAW, RLE } packet_type; +static packet_type rle_packet_type(const uint8_t *row, const uint16_t pos, + const uint16_t width, const uint16_t bpp); +static uint8_t rle_packet_len(const uint8_t *row, const uint16_t pos, + const uint16_t width, const uint16_t bpp, const packet_type type); + + + +uint8_t tga_get_attribute_bits(const tga_image *tga) +{ + return tga->image_descriptor & TGA_ATTRIB_BITS; +} + +int tga_is_right_to_left(const tga_image *tga) +{ + return (tga->image_descriptor & TGA_R_TO_L_BIT) != 0; +} + +int tga_is_top_to_bottom(const tga_image *tga) +{ + return (tga->image_descriptor & TGA_T_TO_B_BIT) != 0; +} + +int tga_is_colormapped(const tga_image *tga) +{ + return ( + tga->image_type == TGA_IMAGE_TYPE_COLORMAP || + tga->image_type == TGA_IMAGE_TYPE_COLORMAP_RLE + ); +} + +int tga_is_rle(const tga_image *tga) +{ + return ( + tga->image_type == TGA_IMAGE_TYPE_COLORMAP_RLE || + tga->image_type == TGA_IMAGE_TYPE_BGR_RLE || + tga->image_type == TGA_IMAGE_TYPE_MONO_RLE + ); +} + +int tga_is_mono(const tga_image *tga) +{ + return ( + tga->image_type == TGA_IMAGE_TYPE_MONO || + tga->image_type == TGA_IMAGE_TYPE_MONO_RLE + ); +} + + + +/* --------------------------------------------------------------------------- + * Convert the numerical into a verbose error string. + * + * Returns: an error string + */ +const char *tga_error(const tga_result errcode) +{ + switch (errcode) + { + case TGA_NOERR: + return "no error"; + case TGAERR_FOPEN: + return "error opening file"; + case TGAERR_EOF: + return "premature end of file"; + case TGAERR_WRITE: + return "error writing to file"; + case TGAERR_CMAP_TYPE: + return "invalid color map type"; + case TGAERR_IMG_TYPE: + return "invalid image type"; + case TGAERR_NO_IMG: + return "no image data included"; + case TGAERR_CMAP_MISSING: + return "color-mapped image without color map"; + case TGAERR_CMAP_PRESENT: + return "non-color-mapped image with extraneous color map"; + case TGAERR_CMAP_LENGTH: + return "color map has zero length"; + case TGAERR_CMAP_DEPTH: + return "invalid color map depth"; + case TGAERR_ZERO_SIZE: + return "the image dimensions are zero"; + case TGAERR_PIXEL_DEPTH: + return "invalid pixel depth"; + case TGAERR_NO_MEM: + return "out of memory"; + case TGAERR_NOT_CMAP: + return "image is not color mapped"; + case TGAERR_RLE: + return "RLE data is corrupt"; + case TGAERR_INDEX_RANGE: + return "color map index out of range"; + case TGAERR_MONO: + return "image is mono"; + default: + return "unknown error code"; + } +} + + + +/* --------------------------------------------------------------------------- + * Read a Targa image from a file named to . This is just a + * wrapper around tga_read_from_FILE(). + * + * Returns: TGA_NOERR on success, or a matching TGAERR_* code on failure. + */ +tga_result tga_read(tga_image *dest, const char *filename) +{ + tga_result result; + FILE *fp = fopen(filename, "rb"); + if (fp == NULL) return TGAERR_FOPEN; + result = tga_read_from_FILE(dest, fp); + fclose(fp); + return result; +} + + + +/* --------------------------------------------------------------------------- + * Read a Targa image from to . + * + * Returns: TGA_NOERR on success, or a TGAERR_* code on failure. In the + * case of failure, the contents of dest are not guaranteed to be + * valid. + */ +tga_result tga_read_from_FILE(tga_image *dest, FILE *fp) +{ + #define BARF(errcode) \ + { tga_free_buffers(dest); return errcode; } + + #define READ(destptr, size) \ + if (fread(destptr, size, 1, fp) != 1) BARF(TGAERR_EOF) + + #define READ16(dest) \ + { if (fread(&(dest), 2, 1, fp) != 1) BARF(TGAERR_EOF); \ + dest = letoh16(dest); } + + dest->image_id = NULL; + dest->color_map_data = NULL; + dest->image_data = NULL; + + READ(&dest->image_id_length,1); + READ(&dest->color_map_type,1); + if (dest->color_map_type != TGA_COLOR_MAP_ABSENT && + dest->color_map_type != TGA_COLOR_MAP_PRESENT) + BARF(TGAERR_CMAP_TYPE); + + READ(&dest->image_type, 1); + if (dest->image_type == TGA_IMAGE_TYPE_NONE) + BARF(TGAERR_NO_IMG); + + if (dest->image_type != TGA_IMAGE_TYPE_COLORMAP && + dest->image_type != TGA_IMAGE_TYPE_BGR && + dest->image_type != TGA_IMAGE_TYPE_MONO && + dest->image_type != TGA_IMAGE_TYPE_COLORMAP_RLE && + dest->image_type != TGA_IMAGE_TYPE_BGR_RLE && + dest->image_type != TGA_IMAGE_TYPE_MONO_RLE) + BARF(TGAERR_IMG_TYPE); + + if (tga_is_colormapped(dest) && + dest->color_map_type == TGA_COLOR_MAP_ABSENT) + BARF(TGAERR_CMAP_MISSING); + + if (!tga_is_colormapped(dest) && + dest->color_map_type == TGA_COLOR_MAP_PRESENT) + BARF(TGAERR_CMAP_PRESENT); + + READ16(dest->color_map_origin); + READ16(dest->color_map_length); + READ(&dest->color_map_depth, 1); + if (dest->color_map_type == TGA_COLOR_MAP_PRESENT) + { + if (dest->color_map_length == 0) + BARF(TGAERR_CMAP_LENGTH); + + if (!UNMAP_DEPTH(dest->color_map_depth)) + BARF(TGAERR_CMAP_DEPTH); + } + + READ16(dest->origin_x); + READ16(dest->origin_y); + READ16(dest->width); + READ16(dest->height); + + if (dest->width == 0 || dest->height == 0) + BARF(TGAERR_ZERO_SIZE); + + READ(&dest->pixel_depth, 1); + if (!SANE_DEPTH(dest->pixel_depth) || + (dest->pixel_depth != 8 && tga_is_colormapped(dest)) ) + BARF(TGAERR_PIXEL_DEPTH); + + READ(&dest->image_descriptor, 1); + + if (dest->image_id_length > 0) + { + dest->image_id = (uint8_t*)malloc(dest->image_id_length); + if (dest->image_id == NULL) BARF(TGAERR_NO_MEM); + READ(dest->image_id, dest->image_id_length); + } + + if (dest->color_map_type == TGA_COLOR_MAP_PRESENT) + { + dest->color_map_data = (uint8_t*)malloc( + (dest->color_map_origin + dest->color_map_length) * + dest->color_map_depth / 8); + if (dest->color_map_data == NULL) BARF(TGAERR_NO_MEM); + READ(dest->color_map_data + + (dest->color_map_origin * dest->color_map_depth / 8), + dest->color_map_length * dest->color_map_depth / 8); + } + + dest->image_data = (uint8_t*) malloc( + dest->width * dest->height * dest->pixel_depth / 8); + if (dest->image_data == NULL) + BARF(TGAERR_NO_MEM); + + if (tga_is_rle(dest)) + { + /* read RLE */ + tga_result result = tga_read_rle(dest, fp); + if (result != TGA_NOERR) BARF(result); + } + else + { + /* uncompressed */ + READ(dest->image_data, + dest->width * dest->height * dest->pixel_depth / 8); + } + + return TGA_NOERR; + #undef BARF + #undef READ + #undef READ16 +} + + + +/* --------------------------------------------------------------------------- + * Helper function for tga_read_from_FILE(). Decompresses RLE image data from + * . Assumes header fields are set correctly. + */ +static tga_result tga_read_rle(tga_image *dest, FILE *fp) +{ + #define RLE_BIT BIT(7) + #define READ(dest, size) \ + if (fread(dest, size, 1, fp) != 1) return TGAERR_EOF + + uint8_t *pos; + uint32_t p_loaded = 0, + p_expected = dest->width * dest->height; + uint8_t bpp = dest->pixel_depth/8; /* bytes per pixel */ + + pos = dest->image_data; + + while ((p_loaded < p_expected) && !feof(fp)) + { + uint8_t b; + READ(&b, 1); + if (b & RLE_BIT) + { + /* is an RLE packet */ + uint8_t count, tmp[4], i; + + count = (b & ~RLE_BIT) + 1; + READ(tmp, bpp); + + for (i=0; i p_expected) return TGAERR_RLE; + memcpy(pos, tmp, bpp); + pos += bpp; + } + } + else /* RAW packet */ + { + uint8_t count; + + count = (b & ~RLE_BIT) + 1; + if (p_loaded + count > p_expected) return TGAERR_RLE; + + p_loaded += count; + READ(pos, bpp*count); + pos += count * bpp; + } + } + return TGA_NOERR; + #undef RLE_BIT + #undef READ +} + + + +/* --------------------------------------------------------------------------- + * Write a Targa image to a file named from . This is just a + * wrapper around tga_write_to_FILE(). + * + * Returns: TGA_NOERR on success, or a matching TGAERR_* code on failure. + */ +tga_result tga_write(const char *filename, const tga_image *src) +{ + tga_result result; + FILE *fp = fopen(filename, "wb"); + if (fp == NULL) return TGAERR_FOPEN; + result = tga_write_to_FILE(fp, src); + fclose(fp); + return result; +} + + + +/* --------------------------------------------------------------------------- + * Write one row of an image to using RLE. This is a helper function + * called from tga_write_to_FILE(). It assumes that has its header + * fields set up correctly. + */ +#define PIXEL(ofs) ( row + (ofs)*bpp ) +static tga_result tga_write_row_RLE(FILE *fp, + const tga_image *src, const uint8_t *row) +{ + #define WRITE(src, size) \ + if (fwrite(src, size, 1, fp) != 1) return TGAERR_WRITE + + uint16_t pos = 0; + uint16_t bpp = src->pixel_depth / 8; + + while (pos < src->width) + { + packet_type type = rle_packet_type(row, pos, src->width, bpp); + uint8_t len = rle_packet_len(row, pos, src->width, bpp, type); + uint8_t packet_header; + + packet_header = len - 1; + if (type == RLE) packet_header |= BIT(7); + + WRITE(&packet_header, 1); + if (type == RLE) + { + WRITE(PIXEL(pos), bpp); + } + else /* type == RAW */ + { + WRITE(PIXEL(pos), bpp*len); + } + + pos += len; + } + + return TGA_NOERR; + #undef WRITE +} + + + +/* --------------------------------------------------------------------------- + * Determine whether the next packet should be RAW or RLE for maximum + * efficiency. This is a helper function called from rle_packet_len() and + * tga_write_row_RLE(). + */ +#define SAME(ofs1, ofs2) (memcmp(PIXEL(ofs1), PIXEL(ofs2), bpp) == 0) + +static packet_type rle_packet_type(const uint8_t *row, const uint16_t pos, + const uint16_t width, const uint16_t bpp) +{ + if (pos == width - 1) return RAW; /* one pixel */ + if (SAME(pos,pos+1)) /* dupe pixel */ + { + if (bpp > 1) return RLE; /* inefficient for bpp=1 */ + + /* three repeats makes the bpp=1 case efficient enough */ + if ((pos < width - 2) && SAME(pos+1,pos+2)) return RLE; + } + return RAW; +} + + + +/* --------------------------------------------------------------------------- + * Find the length of the current RLE packet. This is a helper function + * called from tga_write_row_RLE(). + */ +static uint8_t rle_packet_len(const uint8_t *row, const uint16_t pos, + const uint16_t width, const uint16_t bpp, const packet_type type) +{ + uint8_t len = 2; + + if (pos == width - 1) return 1; + if (pos == width - 2) return 2; + + if (type == RLE) + { + while (pos + len < width) + { + if (SAME(pos, pos+len)) + len++; + else + return len; + + if (len == 128) return 128; + } + } + else /* type == RAW */ + { + while (pos + len < width) + { + if (rle_packet_type(row, pos+len, width, bpp) == RAW) + len++; + else + return len; + if (len == 128) return 128; + } + } + return len; /* hit end of row (width) */ +} +#undef SAME +#undef PIXEL + + + +/* --------------------------------------------------------------------------- + * Writes a Targa image to from . + * + * Returns: TGA_NOERR on success, or a TGAERR_* code on failure. + * On failure, the contents of the file are not guaranteed + * to be valid. + */ +tga_result tga_write_to_FILE(FILE *fp, const tga_image *src) +{ + #define WRITE(srcptr, size) \ + if (fwrite(srcptr, size, 1, fp) != 1) return TGAERR_WRITE + + #define WRITE16(src) \ + { uint16_t _temp = htole16(src); \ + if (fwrite(&_temp, 2, 1, fp) != 1) return TGAERR_WRITE; } + + WRITE(&src->image_id_length, 1); + + if (src->color_map_type != TGA_COLOR_MAP_ABSENT && + src->color_map_type != TGA_COLOR_MAP_PRESENT) + return TGAERR_CMAP_TYPE; + WRITE(&src->color_map_type, 1); + + if (src->image_type == TGA_IMAGE_TYPE_NONE) + return TGAERR_NO_IMG; + if (src->image_type != TGA_IMAGE_TYPE_COLORMAP && + src->image_type != TGA_IMAGE_TYPE_BGR && + src->image_type != TGA_IMAGE_TYPE_MONO && + src->image_type != TGA_IMAGE_TYPE_COLORMAP_RLE && + src->image_type != TGA_IMAGE_TYPE_BGR_RLE && + src->image_type != TGA_IMAGE_TYPE_MONO_RLE) + return TGAERR_IMG_TYPE; + WRITE(&src->image_type, 1); + + if (tga_is_colormapped(src) && + src->color_map_type == TGA_COLOR_MAP_ABSENT) + return TGAERR_CMAP_MISSING; + if (!tga_is_colormapped(src) && + src->color_map_type == TGA_COLOR_MAP_PRESENT) + return TGAERR_CMAP_PRESENT; + if (src->color_map_type == TGA_COLOR_MAP_PRESENT) + { + if (src->color_map_length == 0) + return TGAERR_CMAP_LENGTH; + + if (!UNMAP_DEPTH(src->color_map_depth)) + return TGAERR_CMAP_DEPTH; + } + WRITE16(src->color_map_origin); + WRITE16(src->color_map_length); + WRITE(&src->color_map_depth, 1); + + WRITE16(src->origin_x); + WRITE16(src->origin_y); + + if (src->width == 0 || src->height == 0) + return TGAERR_ZERO_SIZE; + WRITE16(src->width); + WRITE16(src->height); + + if (!SANE_DEPTH(src->pixel_depth) || + (src->pixel_depth != 8 && tga_is_colormapped(src)) ) + return TGAERR_PIXEL_DEPTH; + WRITE(&src->pixel_depth, 1); + + WRITE(&src->image_descriptor, 1); + + if (src->image_id_length > 0) + WRITE(&src->image_id, src->image_id_length); + + if (src->color_map_type == TGA_COLOR_MAP_PRESENT) + WRITE(src->color_map_data + + (src->color_map_origin * src->color_map_depth / 8), + src->color_map_length * src->color_map_depth / 8); + + if (tga_is_rle(src)) + { + uint16_t row; + for (row=0; rowheight; row++) + { + tga_result result = tga_write_row_RLE(fp, src, + src->image_data + row*src->width*src->pixel_depth/8); + if (result != TGA_NOERR) return result; + } + } + else + { + /* uncompressed */ + WRITE(src->image_data, + src->width * src->height * src->pixel_depth / 8); + } + + WRITE(tga_id, tga_id_length); + + return TGA_NOERR; + #undef WRITE + #undef WRITE16 +} + + + +/* Convenient writing functions --------------------------------------------*/ + +/* + * This is just a helper function to initialise the header fields in a + * tga_image struct. + */ +static void init_tga_image(tga_image *img, uint8_t *image, + const uint16_t width, const uint16_t height, const uint8_t depth) +{ + img->image_id_length = 0; + img->color_map_type = TGA_COLOR_MAP_ABSENT; + img->image_type = TGA_IMAGE_TYPE_NONE; /* override this below! */ + img->color_map_origin = 0; + img->color_map_length = 0; + img->color_map_depth = 0; + img->origin_x = 0; + img->origin_y = 0; + img->width = width; + img->height = height; + img->pixel_depth = depth; + img->image_descriptor = TGA_T_TO_B_BIT; + img->image_id = NULL; + img->color_map_data = NULL; + img->image_data = image; +} + + + +tga_result tga_write_mono(const char *filename, uint8_t *image, + const uint16_t width, const uint16_t height) +{ + tga_image img; + init_tga_image(&img, image, width, height, 8); + img.image_type = TGA_IMAGE_TYPE_MONO; + return tga_write(filename, &img); +} + + + +tga_result tga_write_mono_rle(const char *filename, uint8_t *image, + const uint16_t width, const uint16_t height) +{ + tga_image img; + init_tga_image(&img, image, width, height, 8); + img.image_type = TGA_IMAGE_TYPE_MONO_RLE; + return tga_write(filename, &img); +} + + + +tga_result tga_write_bgr(const char *filename, uint8_t *image, + const uint16_t width, const uint16_t height, const uint8_t depth) +{ + tga_image img; + init_tga_image(&img, image, width, height, depth); + img.image_type = TGA_IMAGE_TYPE_BGR; + return tga_write(filename, &img); +} + + + +tga_result tga_write_bgr_rle(const char *filename, uint8_t *image, + const uint16_t width, const uint16_t height, const uint8_t depth) +{ + tga_image img; + init_tga_image(&img, image, width, height, depth); + img.image_type = TGA_IMAGE_TYPE_BGR_RLE; + return tga_write(filename, &img); +} + + + +/* Note: this function will MODIFY */ +tga_result tga_write_rgb(const char *filename, uint8_t *image, + const uint16_t width, const uint16_t height, const uint8_t depth) +{ + tga_image img; + init_tga_image(&img, image, width, height, depth); + img.image_type = TGA_IMAGE_TYPE_BGR; + (void)tga_swap_red_blue(&img); + return tga_write(filename, &img); +} + + + +/* Note: this function will MODIFY */ +tga_result tga_write_rgb_rle(const char *filename, uint8_t *image, + const uint16_t width, const uint16_t height, const uint8_t depth) +{ + tga_image img; + init_tga_image(&img, image, width, height, depth); + img.image_type = TGA_IMAGE_TYPE_BGR_RLE; + (void)tga_swap_red_blue(&img); + return tga_write(filename, &img); +} + + + +/* Convenient manipulation functions ---------------------------------------*/ + +/* --------------------------------------------------------------------------- + * Horizontally flip the image in place. Reverses the right-to-left bit in + * the image descriptor. + */ +tga_result tga_flip_horiz(tga_image *img) +{ + uint16_t row; + size_t bpp; + uint8_t *left, *right; + int r_to_l; + + if (!SANE_DEPTH(img->pixel_depth)) return TGAERR_PIXEL_DEPTH; + bpp = (size_t)(img->pixel_depth / 8); /* bytes per pixel */ + + for (row=0; rowheight; row++) + { + left = img->image_data + row * img->width * bpp; + right = left + (img->width - 1) * bpp; + + /* reverse from left to right */ + while (left < right) + { + uint8_t buffer[4]; + + /* swap */ + memcpy(buffer, left, bpp); + memcpy(left, right, bpp); + memcpy(right, buffer, bpp); + + left += bpp; + right -= bpp; + } + } + + /* Correct image_descriptor's left-to-right-ness. */ + r_to_l = tga_is_right_to_left(img); + img->image_descriptor &= ~TGA_R_TO_L_BIT; /* mask out r-to-l bit */ + if (!r_to_l) + /* was l-to-r, need to set r_to_l */ + img->image_descriptor |= TGA_R_TO_L_BIT; + /* else bit is already rubbed out */ + + return TGA_NOERR; +} + + + +/* --------------------------------------------------------------------------- + * Vertically flip the image in place. Reverses the top-to-bottom bit in + * the image descriptor. + */ +tga_result tga_flip_vert(tga_image *img) +{ + uint16_t col; + size_t bpp, line; + uint8_t *top, *bottom; + int t_to_b; + + if (!SANE_DEPTH(img->pixel_depth)) return TGAERR_PIXEL_DEPTH; + bpp = (size_t)(img->pixel_depth / 8); /* bytes per pixel */ + line = bpp * img->width; /* bytes per line */ + + for (col=0; colwidth; col++) + { + top = img->image_data + col * bpp; + bottom = top + (img->height - 1) * line; + + /* reverse from top to bottom */ + while (top < bottom) + { + uint8_t buffer[4]; + + /* swap */ + memcpy(buffer, top, bpp); + memcpy(top, bottom, bpp); + memcpy(bottom, buffer, bpp); + + top += line; + bottom -= line; + } + } + + /* Correct image_descriptor's top-to-bottom-ness. */ + t_to_b = tga_is_top_to_bottom(img); + img->image_descriptor &= ~TGA_T_TO_B_BIT; /* mask out t-to-b bit */ + if (!t_to_b) + /* was b-to-t, need to set t_to_b */ + img->image_descriptor |= TGA_T_TO_B_BIT; + /* else bit is already rubbed out */ + + return TGA_NOERR; +} + + + +/* --------------------------------------------------------------------------- + * Convert a color-mapped image to unmapped BGR. Reallocates image_data to a + * bigger size, then converts the image backwards to avoid using a secondary + * buffer. Alters the necessary header fields and deallocates the color map. + */ +tga_result tga_color_unmap(tga_image *img) +{ + uint8_t bpp = img->color_map_depth / 8; /* bytes per pixel */ + int pos; + void *tmp; + + if (!tga_is_colormapped(img)) return TGAERR_NOT_CMAP; + if (img->pixel_depth != 8) return TGAERR_PIXEL_DEPTH; + if (!SANE_DEPTH(img->color_map_depth)) return TGAERR_CMAP_DEPTH; + + tmp = realloc(img->image_data, img->width * img->height * bpp); + if (tmp == NULL) return TGAERR_NO_MEM; + img->image_data = (uint8_t*) tmp; + + for (pos = img->width * img->height - 1; pos >= 0; pos--) + { + uint8_t c_index = img->image_data[pos]; + uint8_t *c_bgr = img->color_map_data + (c_index * bpp); + + if (c_index >= img->color_map_origin + img->color_map_length) + return TGAERR_INDEX_RANGE; + + memcpy(img->image_data + (pos*bpp), c_bgr, (size_t)bpp); + } + + /* clean up */ + img->image_type = TGA_IMAGE_TYPE_BGR; + img->pixel_depth = img->color_map_depth; + + free(img->color_map_data); + img->color_map_data = NULL; + img->color_map_type = TGA_COLOR_MAP_ABSENT; + img->color_map_origin = 0; + img->color_map_length = 0; + img->color_map_depth = 0; + + return TGA_NOERR; +} + + + +/* --------------------------------------------------------------------------- + * Return a pointer to a given pixel. Accounts for image orientation (T_TO_B, + * R_TO_L, etc). Returns NULL if the pixel is out of range. + */ +uint8_t *tga_find_pixel(const tga_image *img, uint16_t x, uint16_t y) +{ + if (x >= img->width || y >= img->height) + return NULL; + + if (!tga_is_top_to_bottom(img)) y = img->height - 1 - y; + if (tga_is_right_to_left(img)) x = img->width - 1 - x; + return img->image_data + (x + y * img->width) * img->pixel_depth/8; +} + + + +/* --------------------------------------------------------------------------- + * Unpack the pixel at the src pointer according to bits. Any of b,g,r,a can + * be set to NULL if not wanted. Returns TGAERR_PIXEL_DEPTH if a stupid + * number of bits is given. + */ +tga_result tga_unpack_pixel(const uint8_t *src, const uint8_t bits, + uint8_t *b, uint8_t *g, uint8_t *r, uint8_t *a) +{ + switch (bits) + { + case 32: + if (b) *b = src[0]; + if (g) *g = src[1]; + if (r) *r = src[2]; + if (a) *a = src[3]; + break; + + case 24: + if (b) *b = src[0]; + if (g) *g = src[1]; + if (r) *r = src[2]; + if (a) *a = 0; + break; + + case 16: + { + uint16_t src16 = (uint16_t)(src[1] << 8) | (uint16_t)src[0]; + + #define FIVE_BITS (BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4)) + if (b) *b = ((src16 ) & FIVE_BITS) << 3; + if (g) *g = ((src16 >> 5) & FIVE_BITS) << 3; + if (r) *r = ((src16 >> 10) & FIVE_BITS) << 3; + if (a) *a = (uint8_t)( (src16 & BIT(15)) ? 255 : 0 ); + #undef FIVE_BITS + break; + } + + case 8: + if (b) *b = *src; + if (g) *g = *src; + if (r) *r = *src; + if (a) *a = 0; + break; + + default: + return TGAERR_PIXEL_DEPTH; + } + return TGA_NOERR; +} + + + +/* --------------------------------------------------------------------------- + * Pack the pixel at the dest pointer according to bits. Returns + * TGAERR_PIXEL_DEPTH if a stupid number of bits is given. + */ +tga_result tga_pack_pixel(uint8_t *dest, const uint8_t bits, + const uint8_t b, const uint8_t g, const uint8_t r, const uint8_t a) +{ + switch (bits) + { + case 32: + dest[0] = b; + dest[1] = g; + dest[2] = r; + dest[3] = a; + break; + + case 24: + dest[0] = b; + dest[1] = g; + dest[2] = r; + break; + + case 16: + { + uint16_t tmp; + + #define FIVE_BITS (BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4)) + tmp = (b >> 3) & FIVE_BITS; + tmp |= ((g >> 3) & FIVE_BITS) << 5; + tmp |= ((r >> 3) & FIVE_BITS) << 10; + if (a > 127) tmp |= BIT(15); + #undef FIVE_BITS + + dest[0] = (uint8_t) (tmp & 0x00FF); + dest[1] = (uint8_t)((tmp & 0xFF00) >> 8); + break; + } + + default: + return TGAERR_PIXEL_DEPTH; + } + return TGA_NOERR; +} + + + +/* --------------------------------------------------------------------------- + * Desaturate the specified Targa using the specified coefficients: + * output = ( red * cr + green * cg + blue * cb ) / dv + */ +tga_result tga_desaturate(tga_image *img, const int cr, const int cg, + const int cb, const int dv) +{ + uint8_t bpp = img->pixel_depth / 8; /* bytes per pixel */ + uint8_t *dest, *src, *tmp; + + if (tga_is_mono(img)) return TGAERR_MONO; + if (tga_is_colormapped(img)) + { + tga_result result = tga_color_unmap(img); + if (result != TGA_NOERR) return result; + } + if (!UNMAP_DEPTH(img->pixel_depth)) return TGAERR_PIXEL_DEPTH; + + dest = img->image_data; + for (src = img->image_data; + src < img->image_data + img->width*img->height*bpp; + src += bpp) + { + uint8_t b, g, r; + (void)tga_unpack_pixel(src, img->pixel_depth, &b, &g, &r, NULL); + + *dest = (uint8_t)( ( (int)b * cb + + (int)g * cg + + (int)r * cr ) / dv ); + dest++; + } + + /* shrink */ + tmp = realloc(img->image_data, img->width * img->height); + if (tmp == NULL) return TGAERR_NO_MEM; + img->image_data = tmp; + + img->pixel_depth = 8; + img->image_type = TGA_IMAGE_TYPE_MONO; + return TGA_NOERR; +} + +tga_result tga_desaturate_rec_601_1(tga_image *img) +{ + return tga_desaturate(img, 2989, 5866, 1145, 10000); +} + +tga_result tga_desaturate_rec_709(tga_image *img) +{ + return tga_desaturate(img, 2126, 7152, 722, 10000); +} + +tga_result tga_desaturate_itu(tga_image *img) +{ + return tga_desaturate(img, 2220, 7067, 713, 10000); +} + +tga_result tga_desaturate_avg(tga_image *img) +{ + return tga_desaturate(img, 1,1,1, 3); +} + + + +/* --------------------------------------------------------------------------- + * Convert an image to the given pixel depth. (one of 32, 24, 16) Avoids + * using a secondary buffer to do the conversion. + */ +tga_result tga_convert_depth(tga_image *img, const uint8_t bits) +{ + size_t src_size, dest_size; + uint8_t src_bpp, dest_bpp; + uint8_t *src, *dest; + + if (!UNMAP_DEPTH(bits) || + !SANE_DEPTH(img->pixel_depth) + ) return TGAERR_PIXEL_DEPTH; + + if (tga_is_colormapped(img)) + { + tga_result result = tga_color_unmap(img); + if (result != TGA_NOERR) return result; + } + + if (img->pixel_depth == bits) return TGA_NOERR; /* no op, no err */ + + src_bpp = img->pixel_depth / 8; + dest_bpp = bits / 8; + + src_size = (size_t)( img->width * img->height * src_bpp ); + dest_size = (size_t)( img->width * img->height * dest_bpp ); + + if (src_size > dest_size) + { + void *tmp; + + /* convert forwards */ + dest = img->image_data; + for (src = img->image_data; + src < img->image_data + img->width * img->height * src_bpp; + src += src_bpp) + { + uint8_t r,g,b,a; + (void)tga_unpack_pixel(src, img->pixel_depth, &r, &g, &b, &a); + (void)tga_pack_pixel(dest, bits, r, g, b, a); + dest += dest_bpp; + } + + /* shrink */ + tmp = realloc(img->image_data, img->width * img->height * dest_bpp); + if (tmp == NULL) return TGAERR_NO_MEM; + img->image_data = tmp; + } + else + { + /* expand */ + void *tmp = realloc(img->image_data, + img->width * img->height * dest_bpp); + if (tmp == NULL) return TGAERR_NO_MEM; + img->image_data = (uint8_t*) tmp; + + /* convert backwards */ + dest = img->image_data + (img->width*img->height - 1) * dest_bpp; + for (src = img->image_data + (img->width*img->height - 1) * src_bpp; + src >= img->image_data; + src -= src_bpp) + { + uint8_t r,g,b,a; + (void)tga_unpack_pixel(src, img->pixel_depth, &r, &g, &b, &a); + (void)tga_pack_pixel(dest, bits, r, g, b, a); + dest -= dest_bpp; + } + } + + img->pixel_depth = bits; + return TGA_NOERR; +} + + + +/* --------------------------------------------------------------------------- + * Swap red and blue (RGB becomes BGR and vice verse). (in-place) + */ +tga_result tga_swap_red_blue(tga_image *img) +{ + uint8_t *ptr; + uint8_t bpp = img->pixel_depth / 8; + + if (!UNMAP_DEPTH(img->pixel_depth)) return TGAERR_PIXEL_DEPTH; + + for (ptr = img->image_data; + ptr < img->image_data + (img->width * img->height - 1) * bpp; + ptr += bpp) + { + uint8_t r,g,b,a; + (void)tga_unpack_pixel(ptr, img->pixel_depth, &b,&g,&r,&a); + (void)tga_pack_pixel(ptr, img->pixel_depth, r,g,b,a); + } + return TGA_NOERR; +} + + + +/* --------------------------------------------------------------------------- + * Free the image_id, color_map_data and image_data buffers of the specified + * tga_image, if they're not already NULL. + */ +void tga_free_buffers(tga_image *img) +{ + if (img->image_id != NULL) + { + free(img->image_id); + img->image_id = NULL; + } + if (img->color_map_data != NULL) + { + free(img->color_map_data); + img->color_map_data = NULL; + } + if (img->image_data != NULL) + { + free(img->image_data); + img->image_data = NULL; + } +} + +/* vim:set tabstop=4 shiftwidth=4 textwidth=78 expandtab: */ diff --git a/targa.h b/targa.h new file mode 100644 index 0000000..b889b82 --- /dev/null +++ b/targa.h @@ -0,0 +1,202 @@ +/* --------------------------------------------------------------------------- + * Truevision Targa Reader/Writer + * $Id: targa.h,v 1.7 2003/06/21 09:30:53 emikulic Exp $ + * + * Copyright (C) 2001-2003, Emil Mikulic. + * + * Source and binary redistribution of this code, with or without + * changes, for free or for profit, is allowed as long as this copyright + * notice is kept intact. Modified versions have to be clearly marked + * as modified. + * + * This code is provided without any warranty. The copyright holder is + * not liable for anything bad that might happen as a result of the + * code. + * -------------------------------------------------------------------------*/ + +#ifndef _TARGA_H_ +#define _TARGA_H_ + +#include +#ifndef _MSC_VER +# include +#else /* MSVC */ + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; +#endif + +#define BIT(index) (1 << (index)) + +#ifdef _BIG_ENDIAN +# define htole16(x) ( (((x) & 0x00FF) << 8) | (((x) & 0xFF00) >> 8) ) +# define letoh16(x) htole16(x) +#else /* little endian */ +# define htole16(x) (x) +# define letoh16(x) (x) +#endif /* endianness */ + + + +/* Targa image and header fields -------------------------------------------*/ +typedef struct +{ + /* Note that Targa is stored in little-endian order */ + uint8_t image_id_length; + + uint8_t color_map_type; + /* color map = palette */ + #define TGA_COLOR_MAP_ABSENT 0 + #define TGA_COLOR_MAP_PRESENT 1 + + uint8_t image_type; + #define TGA_IMAGE_TYPE_NONE 0 /* no image data */ + #define TGA_IMAGE_TYPE_COLORMAP 1 /* uncompressed, color-mapped */ + #define TGA_IMAGE_TYPE_BGR 2 /* uncompressed, true-color */ + #define TGA_IMAGE_TYPE_MONO 3 /* uncompressed, black and white */ + #define TGA_IMAGE_TYPE_COLORMAP_RLE 9 /* run-length, color-mapped */ + #define TGA_IMAGE_TYPE_BGR_RLE 10 /* run-length, true-color */ + #define TGA_IMAGE_TYPE_MONO_RLE 11 /* run-length, black and white */ + + /* color map specification */ + uint16_t color_map_origin; /* index of first entry */ + uint16_t color_map_length; /* number of entries included */ + uint8_t color_map_depth; /* number of bits per entry */ + + /* image specification */ + uint16_t origin_x; + uint16_t origin_y; + uint16_t width; + uint16_t height; + uint8_t pixel_depth; + + uint8_t image_descriptor; + /* bits 0,1,2,3 - attribute bits per pixel + * bit 4 - set if image is stored right-to-left + * bit 5 - set if image is stored top-to-bottom + * bits 6,7 - unused (must be set to zero) + */ + #define TGA_ATTRIB_BITS (uint8_t)(BIT(0)|BIT(1)|BIT(2)|BIT(3)) + #define TGA_R_TO_L_BIT (uint8_t)BIT(4) + #define TGA_T_TO_B_BIT (uint8_t)BIT(5) + #define TGA_UNUSED_BITS (uint8_t)(BIT(6)|BIT(7)) + /* Note: right-to-left order is not honored by some Targa readers */ + + uint8_t *image_id; + /* The length of this field is given in image_id_length, it's read raw + * from the file so it's not not guaranteed to be zero-terminated. If + * it's not NULL, it needs to be deallocated. see: tga_free_buffers() + */ + + uint8_t *color_map_data; + /* See the "color map specification" fields above. If not NULL, this + * field needs to be deallocated. see: tga_free_buffers() + */ + + uint8_t *image_data; + /* Follows image specification fields (see above) */ + + /* Extension area and developer area are silently ignored. The Targa 2.0 + * spec says we're not required to read or write them. + */ + +} tga_image; + + + +/* For decoding header bits ------------------------------------------------*/ +uint8_t tga_get_attribute_bits(const tga_image *tga); +int tga_is_right_to_left(const tga_image *tga); +int tga_is_top_to_bottom(const tga_image *tga); +int tga_is_colormapped(const tga_image *tga); +int tga_is_rle(const tga_image *tga); +int tga_is_mono(const tga_image *tga); + + + +/* Error handling ----------------------------------------------------------*/ +typedef enum { + TGA_NOERR, + TGAERR_FOPEN, + TGAERR_EOF, + TGAERR_WRITE, + TGAERR_CMAP_TYPE, + TGAERR_IMG_TYPE, + TGAERR_NO_IMG, + TGAERR_CMAP_MISSING, + TGAERR_CMAP_PRESENT, + TGAERR_CMAP_LENGTH, + TGAERR_CMAP_DEPTH, + TGAERR_ZERO_SIZE, + TGAERR_PIXEL_DEPTH, + TGAERR_NO_MEM, + TGAERR_NOT_CMAP, + TGAERR_RLE, + TGAERR_INDEX_RANGE, + TGAERR_MONO +} tga_result; + +const char *tga_error(const tga_result errcode); + + + +/* Load/save ---------------------------------------------------------------*/ +tga_result tga_read(tga_image *dest, const char *filename); +tga_result tga_read_from_FILE(tga_image *dest, FILE *fp); +tga_result tga_write(const char *filename, const tga_image *src); +tga_result tga_write_to_FILE(FILE *fp, const tga_image *src); + + + +/* Convenient writing functions --------------------------------------------*/ +tga_result tga_write_mono(const char *filename, uint8_t *image, + const uint16_t width, const uint16_t height); + +tga_result tga_write_mono_rle(const char *filename, uint8_t *image, + const uint16_t width, const uint16_t height); + +tga_result tga_write_bgr(const char *filename, uint8_t *image, + const uint16_t width, const uint16_t height, const uint8_t depth); + +tga_result tga_write_bgr_rle(const char *filename, uint8_t *image, + const uint16_t width, const uint16_t height, const uint8_t depth); + +/* These functions will use tga_swap_red_blue to MODIFY your image data */ +tga_result tga_write_rgb(const char *filename, uint8_t *image, + const uint16_t width, const uint16_t height, const uint8_t depth); + +tga_result tga_write_rgb_rle(const char *filename, uint8_t *image, + const uint16_t width, const uint16_t height, const uint8_t depth); + + + +/* Manipulation ------------------------------------------------------------*/ +tga_result tga_flip_horiz(tga_image *img); +tga_result tga_flip_vert(tga_image *img); +tga_result tga_color_unmap(tga_image *img); + +uint8_t *tga_find_pixel(const tga_image *img, uint16_t x, uint16_t y); +tga_result tga_unpack_pixel(const uint8_t *src, const uint8_t bits, + uint8_t *b, uint8_t *g, uint8_t *r, uint8_t *a); +tga_result tga_pack_pixel(uint8_t *dest, const uint8_t bits, + const uint8_t b, const uint8_t g, const uint8_t r, const uint8_t a); + +tga_result tga_desaturate(tga_image *img, + const int cr, const int cg, const int cb, const int dv); +tga_result tga_desaturate_rec_601_1(tga_image *img); +tga_result tga_desaturate_rec_709(tga_image *img); +tga_result tga_desaturate_itu(tga_image *img); +tga_result tga_desaturate_avg(tga_image *img); +tga_result tga_convert_depth(tga_image *img, const uint8_t bits); +tga_result tga_swap_red_blue(tga_image *img); + +void tga_free_buffers(tga_image *img); + + + +#ifndef TGA_KEEP_MACROS /* useful for targa.c */ +# undef htole16 +# undef letoh16 +#endif + +#endif /* !_TARGA_H_ */ -- 2.1.4