libtthimage/Lib/tga.c

614 lines
17 KiB
C

/*
tga.c
=====
7 Oct 2001: les problemes de boutisme sont regles.
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#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; foo<img->width; 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; foo<img->width; 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 (foo) {
fprintf(stderr, "%s: read error %d on header\n", __func__, foo);
fclose(fp);
return NULL;
}
#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; ligne<head.height; ligne++)
{
foo=fread(buffer, 3+alpha, head.width, fp);
if (!(head.o_flags & 0x20)) ligne2 = head.height - ligne - 1;
else ligne2 = ligne;
for (col=0; col<head.width; col++)
{
(image->Bpix[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 :) */