610 lines
17 KiB
C
610 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 <20>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 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 :) */
|