/* tga.c ===== 7 Oct 2001: les problemes de boutisme sont regles. */ #include #include #include #include #include "../tthimage.h" /*::------------------------------------------------------------------::*/ #define LIBTGA_VERSION "0.2.42" typedef struct { uint8_t text_size; char is_color_map; /* 0=NO, 1=YES */ char type; uint16_t map_start, map_length; char map_bits; uint16_t x_start, y_start; uint16_t width, height; char bits; /* bits per pixel (8, 24) */ char o_flags; /* orientation flags ? */ } Tga_file_header; /*::------------------------------------------------------------------::*/ int Image_TGA_write_header(FILE *fp, Tga_file_header *head) { fwrite (&head->text_size, 1, 1, fp); fwrite (&head->is_color_map, 1, 1, fp); fwrite (&head->type, 1, 1, fp); Image_basicIO_write_I_short(fp, head->map_start); Image_basicIO_write_I_short(fp, head->map_length); fwrite (&head->map_bits, 1, 1, fp); Image_basicIO_write_I_short(fp, head->x_start); Image_basicIO_write_I_short(fp, head->y_start); Image_basicIO_write_I_short(fp, head->width); Image_basicIO_write_I_short(fp, head->height); fwrite (&head->bits, 1, 1, fp); fwrite (&head->o_flags, 1, 1, fp); return 0; } /*::------------------------------------------------------------------::*/ static void fatal_error(char *txt) { fprintf(stderr, "TGA: (pid:%d) Fatal error: %s\n", getpid(), txt); #if FORCE_ABORT abort(); #endif exit(10); } /*::------------------------------------------------------------------::*/ static int tga_pack_line(void) { /* * ___TODO___ compressed targa file ! * * Novembre 2002: il serait temps de s'y mettre... * */ return -1; } /*::------------------------------------------------------------------::*/ /* * il manque un grand nombre d'options * * - la compression ---> URGENT !!! * - les images a palettes */ int Image_TGA_save(char *nom, Image_Desc *img, int compress) { int ltxt, line, foo; Tga_file_header head; FILE *fp; uint8_t *Rptr, *Gptr, *Bptr, *Aptr; uint8_t *buffer, *ptr; if (nom == NULL) fatal_error("save image: name ptr is NULL"); if (img == NULL) fatal_error("save image: descriptor is NULL"); /* 5 Dec 2001: add a check on img */ if (img->magic != MAGIC_OF_IMAGE) { fprintf(stderr, "TGASAVE: need Dead Beef!\n"); return NOT_AN_IMAGE_DESC; } /* * mais c,a marche pas a tous les coups. il faudrait pouvoir tester si * le pointeur 'img' est coherent, mais je sais pas comment faire... */ if (compress != 0) { fprintf(stderr, "%s: compress %d not implemented\n", __func__, compress); } /* 10 Aout 2000: si le nom est "-", alors on ecrit le fichier sur STDOUT ce qui permet(tra) de faire des CGI :) */ if (strcmp("-", nom)) { if ( (fp=fopen(nom, "wb")) == NULL ) { perror(nom); return FILE_CREATE_ERR; /* fatal_error("save TGA image: error fopen"); */ } } else { fp = stdout; } ltxt = strlen(img->comment); #if DEBUG_LEVEL > 1 fprintf(stderr, "text size = %d\n", ltxt); if (ltxt) fprintf(stderr, " [%s]\n", img->comment); #endif /* first, we populate the file header... */ head.text_size = ltxt; head.x_start = head.y_start = 0; head.width = img->width; head.height = img->height; head.o_flags = 0; /* XXX a preciser ... */ #if DEBUG_LEVEL > 1 fprintf(stderr, "TGASAVE: img@ %p nb planes: %d\n", img, img->nb_planes); #endif switch (img->nb_planes) { case 1: /* images en niveau de gris */ #if DEBUG_LEVEL fprintf(stderr, "%s saving gray level image\n", __func__); #endif head.type = 3; head.bits = 8; break; case 3: head.type = 2; /* uncompressed RGB image */ head.bits = 24; head.is_color_map = 0; /* No */ head.map_start = 0; /* Color */ head.map_length = 0; /* Map */ head.map_bits = 0; /* ? (Aout 1999) */ break; case 4: #if DEBUG_LEVEL fputs("*!* saving image with alpha channel\n", stderr); #endif head.type = 2; /* uncompressed RGB image with alpha */ head.bits = 32; head.is_color_map = 0; /* No */ head.map_start = 0; /* Color */ head.map_length = 0; /* Map */ head.map_bits = 0; head.o_flags = 0x08; /* fixed by Betatech */ break; default: fprintf(stderr, "img->nb_planes = %d\n", img->nb_planes); fatal_error("Tga_save_image: unknow type"); break; } /* * 2 octobre 2001 - prise en main du BOUTISME * fwrite(&head, sizeof(head), 1, fp); * * 5 oct 01: il faudrait faire une fonction genre 'ecrire_header_tga' */ fwrite (&head.text_size, 1, 1, fp); fwrite (&head.is_color_map, 1, 1, fp); fwrite (&head.type, 1, 1, fp); Image_basicIO_write_I_short(fp, head.map_start); Image_basicIO_write_I_short(fp, head.map_length); fwrite (&head.map_bits, 1, 1, fp); Image_basicIO_write_I_short(fp, head.x_start); Image_basicIO_write_I_short(fp, head.y_start); Image_basicIO_write_I_short(fp, head.width); Image_basicIO_write_I_short(fp, head.height); fwrite (&head.bits, 1, 1, fp); fwrite (&head.o_flags, 1, 1, fp); /* if we have a comment, write it to the file */ if (ltxt) { fwrite(img->comment, 1, ltxt, fp); } if ((buffer=calloc(img->width, 4)) == NULL) { fatal_error("no memory buffer for save operation"); } /* XXX why ? memset(buffer, 0x55, sizeof(uint8_t)*4*img->width); */ /* Ahem, now, write all the picture datas to the file... */ for (line=(img->height-1); line>=0; line--) { switch (img->nb_planes) { case 1: fwrite(img->Rpix[line], 1, img->width, fp); break; case 3: Rptr = img->Rpix[line]; Gptr = img->Gpix[line]; Bptr = img->Bpix[line]; ptr = buffer; for (foo=0; foowidth; foo++) { *ptr++ = Bptr[foo]; *ptr++ = Gptr[foo]; *ptr++ = Rptr[foo]; } fwrite(buffer, 3, img->width, fp); break; case 4: Rptr = img->Rpix[line]; Gptr = img->Gpix[line]; Bptr = img->Bpix[line]; Aptr = img->Apix[line]; ptr = buffer; for (foo=0; foowidth; foo++) { *ptr++ = Bptr[foo]; *ptr++ = Gptr[foo]; *ptr++ = Rptr[foo]; *ptr++ = Aptr[foo]; } fwrite(buffer, 4, img->width, fp); break; default: fatal_error("TGA save image: unknow type..."); break; } } free(buffer); /* 1 Fev 00 */ img->modified = 0; if (fp != stdout) fclose(fp); return OLL_KORRECT; } /*::------------------------------------------------------------------::*/ /* new 31 Jan 2002 * save one of the component as a gray TGA file * * 1 mars 2002: in fact, it was a palettized 8 bits file * */ int Image_TGA_save_component(char *nom, Image_Desc *img, char channel, int comp) { int ltxt, line, foo; FILE *fp; uint8_t colormap[256*3]; Tga_file_header head; if (img->magic != MAGIC_OF_IMAGE) { fprintf(stderr, "TGASAVE: need Dead Beef!\n"); return NOT_AN_IMAGE_DESC; } if (comp != 0) { fprintf(stderr, "%s : param 'comp' %d invalid\n", __func__, comp); return BAD_PARAMETER; } if ( (fp=fopen(nom, "wb")) == NULL ) { fatal_error("save image: err fopen"); } ltxt = strlen(img->comment); head.text_size = ltxt; head.x_start = head.y_start = 0; head.width = img->width; head.height = img->height; head.is_color_map = 1; head.o_flags = 0; /* XXX a preciser ... */ head.type = 3; /* Grayscale Image */ head.map_start = 0; head.map_length = 256; head.map_bits = 24; head.bits = 8; fwrite (&head.text_size, 1, 1, fp); fwrite (&head.is_color_map, 1, 1, fp); fwrite (&head.type, 1, 1, fp); Image_basicIO_write_I_short(fp, head.map_start); Image_basicIO_write_I_short(fp, head.map_length); fwrite (&head.map_bits, 1, 1, fp); Image_basicIO_write_I_short(fp, head.x_start); Image_basicIO_write_I_short(fp, head.y_start); Image_basicIO_write_I_short(fp, head.width); Image_basicIO_write_I_short(fp, head.height); fwrite (&head.bits, 1, 1, fp); fwrite (&head.o_flags, 1, 1, fp); /* if we have a comment, write it to the file */ if (ltxt) fwrite(img->comment, 1, ltxt, fp); /* now, populate the colormap */ for (foo=0; foo<256; foo++) { colormap[foo*3+0] = foo; colormap[foo*3+1] = foo; colormap[foo*3+2] = foo; } /* and write the colormap */ fwrite (colormap, 256, 3, fp); for (line=(img->height-1); line>=0; line--) { switch (channel) { case 'r': case 'R': fwrite(img->Rpix[line], 1, img->width, fp); break; case 'g': case 'G': fwrite(img->Gpix[line], 1, img->width, fp); break; case 'b': case 'B': fwrite(img->Bpix[line], 1, img->width, fp); break; } } fclose(fp); fprintf(stderr, "AIE! this func (tga save component as gray) is bogus\n"); return FUNC_NOT_FINISH; } /*::------------------------------------------------------------------::*/ /* new 16 fevrier 2009 - ave St Exupery */ static char * tgatype2txt(int type) { switch (type) { case 2: return "Uncomp RGB"; case 3: return "Uncomp Gray"; case 4: return "Uncomp RGB + alpha"; default: return "wtf?"; } return "Gni ?"; } /*::------------------------------------------------------------------::*/ /* new 17 mars 2007 */ int Image_TGA_print_header(Tga_file_header *ph) { if (NULL == ph) { fprintf(stderr, "null head ptr in '%s'\n", __func__); return 1; } printf("+-- TGA v %s header @ %p ---\n", LIBTGA_VERSION, ph); printf("| comment size %d\n", ph->text_size); printf("| is color map ? %d\n", ph->is_color_map); printf("| type %d %s\n", ph->type, tgatype2txt(ph->type)); printf("| map start %d\n", ph->map_start); printf("| map length %d\n", ph->map_length); printf("| map bits %d\n", ph->map_bits); printf("| x start %d\n", ph->x_start); printf("| y start %d\n", ph->y_start); printf("| width %d\n", ph->width); printf("| height %d\n", ph->height); printf("| nbr of bits %d\n", ph->bits); printf("| o_flags $%02X\n", ph->o_flags); printf("+---------------------------\n"); return 0; } /*::------------------------------------------------------------------::*/ /* new 13 mars 2007 */ int Image_TGA_read_header(FILE *fp, void *pvoid) { Tga_file_header *phead; int foo; phead = (Tga_file_header *)pvoid; foo = fread(&phead->text_size, 1, 1, fp); if (foo != 1) { fprintf(stderr, "TGA READ HEAD: err read first byte of header\n"); return VERY_STRANGE; } fread(&phead->is_color_map, 1, 1, fp); fread(&phead->type, 1, 1, fp); foo = Image_basicIO_read_I_short(fp, &phead->map_start); /* fprintf(stderr, "foo=%d\n", foo); */ Image_basicIO_read_I_short(fp, &phead->map_length); fread(&phead->map_bits, 1, 1, fp); Image_basicIO_read_I_short(fp, &phead->x_start); Image_basicIO_read_I_short(fp, &phead->y_start); phead->width = phead->height = -1; Image_basicIO_read_I_short(fp, &phead->width); Image_basicIO_read_I_short(fp, &phead->height); if ( (phead->width<1) || (phead->width<1) ) { fprintf(stderr, "HEAD TGA: dimension < 1: %d %d\n", phead->width, phead->height); return VERY_STRANGE; } fread(&phead->bits, 1, 1, fp); if ( fread(&phead->o_flags, 1, 1, fp) != 1) { fprintf(stderr, "HEAD TGA: end of file before time...\n"); return VERY_STRANGE; } #if DEBUG_LEVEL > 1 Image_TGA_print_header(phead); #endif return OLL_KORRECT; } /*::------------------------------------------------------------------::*/ /* new 1er mai 2007 (la france qui bosse tout le temps) */ int Image_TGA_show_header(char *filename, int flag) { FILE *fp; Tga_file_header header; int foo; #if DEBUG_LEVEL fprintf(stderr, "%s ( %p 0x%04x )\n", __func__, filename, flag); #endif if ( NULL == (fp=fopen(filename, "rb")) ) { fprintf(stderr, "can't open %s\n", filename); return FILE_NOT_FOUND; } foo = Image_TGA_read_header(fp, &header); if (foo) { fprintf(stderr, "lecture header -> %d\n", foo); } foo = Image_TGA_print_header(&header); if (foo) { ; } return FUNC_NOT_FINISH; } /*::------------------------------------------------------------------::*/ /* new 13 mars 2007 */ int Image_TGA_get_dims(char *path, int *pwidth, int *pheight) { FILE *fp; Tga_file_header head; int foo; *pwidth = *pheight = 0; if ( (fp=fopen(path, "rb")) == NULL ) { fprintf(stderr, "%s: file '%s' is not readable\n", __func__, path); return FILE_NOT_FOUND; } foo = Image_TGA_read_header(fp, &head); fclose(fp); if (foo) { fprintf(stderr, "%s: lecture header '%s' -> %d\n", __func__, path, foo); } if (OLL_KORRECT != foo) { *pwidth = *pheight = 0; return foo; } /* verification de coerence du header lu */ if (head.is_color_map!=0 && head.is_color_map!=1) { fprintf(stderr, "suspicious 'is_color_map' 0x%02x\n", head.is_color_map); } *pwidth = (int)head.width; *pheight = (int)head.height; #if DEBUG_LEVEL fprintf(stderr, "%s --> %d x %d\n", path, (int)head.width, (int)head.height); #endif return OLL_KORRECT; } /*::------------------------------------------------------------------::*/ /* * pour le moment, cette fonction ne charge * que les images RGB. il manque le GRIS !!! * ! il manque aussi la decompression ! */ Image_Desc * Image_TGA_alloc_load(char *nom) { Tga_file_header head; FILE *fp; Image_Desc *image = NULL; uint8_t *buffer; int ligne, ligne2, col, foo, alpha; int imagetype; if ( (fp=fopen(nom, "rb")) == NULL ) { fprintf(stderr, "%s : file '%s' not readable\n", __func__, nom); return NULL; } memset(&head, 0, sizeof(Tga_file_header)); foo = Image_TGA_read_header(fp, &head); #if DEBUG_LEVEL > 1 printf("TGA ALLOC LOAD: read header -> %d\n", foo); printf("comment size %d\n", head.text_size); printf("is color map ? %d\n", head.is_color_map); printf("type %d\n", head.type); printf("map start %d\n", head.map_start); printf("map length %d\n", head.map_length); printf("map bits %d\n", head.map_bits); printf("x start %d\n", head.x_start); printf("y start %d\n", head.y_start); printf("width %d\n", head.width); printf("height %d\n", head.height); printf("nbr of bits %d\n", head.bits); #endif switch (head.type) { case 2: #if DEBUG_LEVEL > 1 fprintf(stderr, "load file %s, type 2, %d bits\n", nom, head.map_bits); #endif if (head.bits == 24) { alpha = 0; imagetype = IMAGE_RGB; } else { alpha = 1; imagetype = IMAGE_RGBA; } if ( (image=Image_alloc(head.width, head.height, 3+alpha))==NULL) fatal_error("no memory for picture in 'alloc_load'\n"); if ( (buffer=(uint8_t *)malloc(4*head.width))==NULL) fatal_error("no memory for buffer in 'alloc_load'\n"); #if DEBUG_LEVEL > 1 fprintf(stderr, " ... mem allocated\n"); if (head.o_flags & 0x20) fprintf(stderr, "flags= 0x%02x Upside-Down ?\n", head.o_flags); if (head.o_flags & 0x10) fprintf(stderr, "flags= 0x%02x Left-Right ?\n", head.o_flags); #endif if (head.text_size) { #if DEBUG_LEVEL > 1 fprintf(stderr,"%s: skipping %d bytes of comment\n", nom, head.text_size); #endif fseek(fp, (long)head.text_size, SEEK_CUR); } for (ligne=0; ligneBpix[ligne2])[col] = buffer[ col*(3+alpha) ]; (image->Gpix[ligne2])[col] = buffer[ col*(3+alpha) + 1 ]; (image->Rpix[ligne2])[col] = buffer[ col*(3+alpha) + 2 ]; if (alpha) (image->Apix[ligne2])[col] = buffer[ col*4 + 3 ]; } } #if DEBUG_LEVEL > 1 fprintf(stderr, " ... image loaded\n\n"); #endif break; default: fprintf(stderr, "%s: file '%s' type %d not supported\n\n", __func__, nom, head.type); fclose (fp); return NULL; } free(buffer); /* added 17 May 2001 :) */ fclose(fp); /*-------- image is in memory... return @ of descriptor to caller. */ return image; } /*::------------------------------------------------------------------::*/ /* c'est la fin... drink a beer :) */