From 8298fd2b0a976cbf064c04f6a248dbfe124d2cf3 Mon Sep 17 00:00:00 2001 From: Francois-Olivier Devaux Date: Mon, 20 Aug 2007 15:20:42 +0000 Subject: [PATCH] Added support for the TGA file format in the codec --- ChangeLog | 3 + codec/compat/getopt.c | 2 +- codec/convert.c | 294 ++++++++++++++++++++++++++++++++++++++++++ codec/convert.h | 10 +- codec/image_to_j2k.c | 24 +++- codec/j2k_to_image.c | 32 +++-- 6 files changed, 345 insertions(+), 20 deletions(-) diff --git a/ChangeLog b/ChangeLog index a54be224..5832d563 100644 --- a/ChangeLog +++ b/ChangeLog @@ -5,6 +5,9 @@ What's New for OpenJPEG ! : changed + : added +August 20, 2008 ++ [FOD] Added support for the TGA file format in the codec + August 08, 2008 * [Parvatha] Fixed the DCinema filesize allocation. It now includes the SOT marker size diff --git a/codec/compat/getopt.c b/codec/compat/getopt.c index 1491bf03..23270e6a 100644 --- a/codec/compat/getopt.c +++ b/codec/compat/getopt.c @@ -251,7 +251,7 @@ found: }// end of single character }//end '-' - fprintf(stderr,"Invalid option %s\n"); + fprintf(stderr,"Invalid option\n"); ++optind; return (BADCH);; }//end function diff --git a/codec/convert.c b/codec/convert.c index 81b34dc9..d074909f 100644 --- a/codec/convert.c +++ b/codec/convert.c @@ -67,6 +67,300 @@ static int int_ceildiv(int a, int b) { return (a + b - 1) / b; } + +/* -->> -->> -->> -->> + + TGA IMAGE FORMAT + + <<-- <<-- <<-- <<-- */ + +// TGA header definition. +#pragma pack(push,1) // Pack structure byte aligned +typedef struct tga_header +{ + uint8 id_length; /* Image id field length */ + uint8 colour_map_type; /* Colour map type */ + uint8 image_type; /* Image type */ + /* + ** Colour map specification + */ + uint16 colour_map_index; /* First entry index */ + uint16 colour_map_length; /* Colour map length */ + uint8 colour_map_entry_size; /* Colour map entry size */ + /* + ** Image specification + */ + uint16 x_origin; /* x origin of image */ + uint16 y_origin; /* u origin of image */ + uint16 image_width; /* Image width */ + uint16 image_height; /* Image height */ + uint8 pixel_depth; /* Pixel depth */ + uint8 image_desc; /* Image descriptor */ +} tga_header; +#pragma pack(pop) // Return to normal structure packing alignment. + +int tga_readheader(FILE *fp, int *bits_per_pixel, int *width, int *height, int *flip_image) +{ + int palette_size; + tga_header tga ; + + if (!bits_per_pixel || !width || !height || !flip_image) + return 0; + + // Read TGA header + fread((uint8*)&tga, sizeof(tga_header), 1, fp); + + *bits_per_pixel = tga.pixel_depth; + + *width = tga.image_width; + *height = tga.image_height ; + + // Ignore tga identifier, if present ... + if (tga.id_length) + { + uint8 *id = (uint8 *) malloc(tga.id_length); + fread(id, tga.id_length, 1, fp); + free(id); + } + + // Test for compressed formats ... not yet supported ... + // Note :- 9 - RLE encoded palettized. + // 10 - RLE encoded RGB. + if (tga.image_type > 8) + { + fprintf(stderr, "Sorry, compressed tga files are not currently supported.\n"); + return 0 ; + } + + *flip_image = !(tga.image_desc & 32); + + // Palettized formats are not yet supported, skip over the palette, if present ... + palette_size = tga.colour_map_length * (tga.colour_map_entry_size/8); + + if (palette_size>0) + { + fprintf(stderr, "File contains a palette - not yet supported."); + fseek(fp, palette_size, SEEK_CUR); + } + return 1; +} + +int tga_writeheader(FILE *fp, int bits_per_pixel, int width, int height, bool flip_image) +{ + tga_header tga; + + if (!bits_per_pixel || !width || !height) + return 0; + + memset(&tga, 0, sizeof(tga_header)); + + tga.pixel_depth = bits_per_pixel; + tga.image_width = width; + tga.image_height = height; + tga.image_type = 2; // Uncompressed. + tga.image_desc = 8; // 8 bits per component. + + if (flip_image) + tga.image_desc |= 32; + + // Write TGA header + fwrite((uint8*)&tga, sizeof(tga_header), 1, fp); + + return 1; +} + +opj_image_t* tgatoimage(const char *filename, opj_cparameters_t *parameters) { + FILE *f; + opj_image_t *image; + uint32 image_width, image_height, pixel_bit_depth; + uint32 x, y; + int flip_image=0; + opj_image_cmptparm_t cmptparm[4]; /* maximum 4 components */ + int numcomps; + OPJ_COLOR_SPACE color_space; + bool mono ; + bool save_alpha; + int subsampling_dx, subsampling_dy; + int i; + + f = fopen(filename, "rb"); + if (!f) { + fprintf(stderr, "Failed to open %s for reading !!\n", filename); + return 0; + } + + if (!tga_readheader(f, &pixel_bit_depth, &image_width, &image_height, &flip_image)) + return NULL; + + // We currently only support 24 & 32 bit tga's ... + if (!((pixel_bit_depth == 24) || (pixel_bit_depth == 32))) + return NULL; + + /* initialize image components */ + memset(&cmptparm[0], 0, 4 * sizeof(opj_image_cmptparm_t)); + + mono = (pixel_bit_depth == 8) || (pixel_bit_depth == 16); // Mono with & without alpha. + save_alpha = (pixel_bit_depth == 16) || (pixel_bit_depth == 32); // Mono with alpha, or RGB with alpha + + if (mono) { + color_space = CLRSPC_GRAY; + numcomps = save_alpha ? 2 : 1; + } + else { + numcomps = save_alpha ? 4 : 3; + color_space = CLRSPC_SRGB; + } + + subsampling_dx = parameters->subsampling_dx; + subsampling_dy = parameters->subsampling_dy; + + for (i = 0; i < numcomps; i++) { + cmptparm[i].prec = 8; + cmptparm[i].bpp = 8; + cmptparm[i].sgnd = 0; + cmptparm[i].dx = subsampling_dx; + cmptparm[i].dy = subsampling_dy; + cmptparm[i].w = image_width; + cmptparm[i].h = image_height; + } + + /* create the image */ + image = opj_image_create(numcomps, &cmptparm[0], color_space); + + if (!image) + return NULL; + + /* set image offset and reference grid */ + image->x0 = parameters->image_offset_x0; + image->y0 = parameters->image_offset_y0; + image->x1 = !image->x0 ? (image_width - 1) * subsampling_dx + 1 : image->x0 + (image_width - 1) * subsampling_dx + 1; + image->y1 = !image->y0 ? (image_height - 1) * subsampling_dy + 1 : image->y0 + (image_height - 1) * subsampling_dy + 1; + + /* set image data */ + for (y=0; y < image_height; y++) + { + int index; + + if (flip_image) + index = (image_height-y-1)*image_width; + else + index = y*image_width; + + if (numcomps==3) + { + for (x=0;xcomps[0].data[index]=r; + image->comps[1].data[index]=g; + image->comps[2].data[index]=b; + index++; + } + } + else if (numcomps==4) + { + for (x=0;xcomps[0].data[index]=r; + image->comps[1].data[index]=g; + image->comps[2].data[index]=b; + image->comps[3].data[index]=a; + index++; + } + } + else { + fprintf(stderr, "Currently unsupported bit depth : %s\n", filename); + } + } + return image; +} + +int imagetotga(opj_image_t * image, const char *outfile) { + int width, height, bpp, x, y; + bool write_alpha; + int i; + uint32 alpha_channel; + float r,g,b,a; + uint8 value; + float scale; + FILE *fdest; + + fdest = fopen(outfile, "wb"); + if (!fdest) { + fprintf(stderr, "ERROR -> failed to open %s for writing\n", outfile); + return 1; + } + + for (i = 0; i < image->numcomps-1; i++) { + if ((image->comps[0].dx != image->comps[i+1].dx) + ||(image->comps[0].dy != image->comps[i+1].dy) + ||(image->comps[0].prec != image->comps[i+1].prec)) { + fprintf(stderr, "Unable to create a tga file with such J2K image charateristics."); + return 1; + } + } + + width = int_ceildiv(image->x1-image->x0, image->comps[0].dx); + height = int_ceildiv(image->y1-image->y0, image->comps[0].dy); + + // Mono with alpha, or RGB with alpha. + write_alpha = (image->numcomps==2) || (image->numcomps==4); + + // Write TGA header + bpp = write_alpha ? 32 : 24; + if (!tga_writeheader(fdest, bpp, width, height, true)) + return 1; + + alpha_channel = image->numcomps-1; + + scale = 255.0f / (float)((1<comps[0].prec)-1); + + for (y=0; y < height; y++) { + uint32 index=y*width; + + for (x=0; x < width; x++, index++) { + r = (float)(image->comps[0].data[index]); + + if (image->numcomps>2) { + g = (float)(image->comps[1].data[index]); + b = (float)(image->comps[2].data[index]); + } + else {// Greyscale ... + g = r; + b = r; + } + + // TGA format writes BGR ... + value = (uint8)(b*scale); + fwrite(&value,1,1,fdest); + + value = (uint8)(g*scale); + fwrite(&value,1,1,fdest); + + value = (uint8)(r*scale); + fwrite(&value,1,1,fdest); + + if (write_alpha) { + a = (float)(image->comps[alpha_channel].data[index]); + value = (uint8)(a*scale); + fwrite(&value,1,1,fdest); + } + } + } + + return 0; +} + /* -->> -->> -->> -->> BMP IMAGE FORMAT diff --git a/codec/convert.h b/codec/convert.h index b2bc16de..98611c66 100644 --- a/codec/convert.h +++ b/codec/convert.h @@ -47,8 +47,12 @@ typedef struct raw_cparameters { /*@}*/ } raw_cparameters_t; -opj_image_t* bmptoimage(const char *filename, opj_cparameters_t *parameters); +/* TGA conversion */ +opj_image_t* tgatoimage(const char *filename, opj_cparameters_t *parameters); +int imagetotga(opj_image_t * image, const char *outfile); +/* BMP conversion */ +opj_image_t* bmptoimage(const char *filename, opj_cparameters_t *parameters); int imagetobmp(opj_image_t *image, const char *outfile); /* TIFF to image conversion*/ @@ -61,15 +65,13 @@ Load a single image component encoded in PGX file format @return Returns a greyscale image if successful, returns NULL otherwise */ opj_image_t* pgxtoimage(const char *filename, opj_cparameters_t *parameters); - int imagetopgx(opj_image_t *image, const char *outfile); opj_image_t* pnmtoimage(const char *filename, opj_cparameters_t *parameters); - int imagetopnm(opj_image_t *image, const char *outfile); +/* RAW conversion */ int imagetoraw(opj_image_t * image, const char *outfile); - opj_image_t* rawtoimage(const char *filename, opj_cparameters_t *parameters, raw_cparameters_t *raw_cp); #endif /* __J2K_CONVERT_H */ diff --git a/codec/image_to_j2k.c b/codec/image_to_j2k.c index 2e146853..44747ee0 100644 --- a/codec/image_to_j2k.c +++ b/codec/image_to_j2k.c @@ -55,6 +55,7 @@ #define YUV_DFMT 13 #define TIF_DFMT 14 #define RAW_DFMT 15 +#define TGA_DFMT 16 /* ----------------------------------------------------------------------- */ #define CINEMA_24_CS 1302083 /*Codestream length for 24fps*/ @@ -134,9 +135,9 @@ void encode_help_display() { fprintf(stdout,"-OutFor \n"); fprintf(stdout," REQUIRED only if -ImgDir is used\n"); fprintf(stdout," Need to specify only format without filename \n"); - fprintf(stdout," Currently accepts PGM, PPM, PNM, PGX, BMP format\n"); + fprintf(stdout," Currently accepts PGM, PPM, PNM, PGX, BMP, TIF, RAW and TGA formats\n"); fprintf(stdout,"\n"); - fprintf(stdout,"-i : source file (-i source.pnm also *.pgm, *.ppm, *.bmp, *.tif, *.raw) \n"); + fprintf(stdout,"-i : source file (-i source.pnm also *.pgm, *.ppm, *.bmp, *.tif, *.raw, *.tga) \n"); fprintf(stdout," When using this option -o must be used\n"); fprintf(stdout,"\n"); fprintf(stdout,"-o : destination file (-o dest.j2k or .jp2) \n"); @@ -369,17 +370,17 @@ int load_images(dircnt_t *dirptr, char *imgdirpath){ int get_file_format(char *filename) { unsigned int i; static const char *extension[] = { - "pgx", "pnm", "pgm", "ppm", "bmp","tif", "raw", "j2k", "jp2", "j2c" + "pgx", "pnm", "pgm", "ppm", "bmp", "tif", "raw", "tga", "j2k", "jp2", "j2c" }; static const int format[] = { - PGX_DFMT, PXM_DFMT, PXM_DFMT, PXM_DFMT, BMP_DFMT, TIF_DFMT, RAW_DFMT, J2K_CFMT, JP2_CFMT, J2K_CFMT + PGX_DFMT, PXM_DFMT, PXM_DFMT, PXM_DFMT, BMP_DFMT, TIF_DFMT, RAW_DFMT, TGA_DFMT, J2K_CFMT, JP2_CFMT, J2K_CFMT }; char * ext = strrchr(filename, '.'); if (ext == NULL) return -1; ext++; for(i = 0; i < sizeof(format)/sizeof(*format); i++) { - if(strnicmp(ext, extension[i], 3) == 0) { + if(_strnicmp(ext, extension[i], 3) == 0) { return format[i]; } } @@ -583,11 +584,12 @@ int parse_cmdline_encoder(int argc, char **argv, opj_cparameters_t *parameters, case BMP_DFMT: case TIF_DFMT: case RAW_DFMT: + case TGA_DFMT: break; default: fprintf(stderr, "!! Unrecognized format for infile : %s " - "[accept only *.pnm, *.pgm, *.ppm, *.pgx, *.bmp, *.tif or *.raw] !!\n\n", + "[accept only *.pnm, *.pgm, *.ppm, *.pgx, *.bmp, *.tif, *.raw or *.tga] !!\n\n", infile); return 1; } @@ -1549,6 +1551,8 @@ int main(int argc, char **argv) { break; case RAW_DFMT: break; + case TGA_DFMT: + break; default: fprintf(stderr,"skipping file...\n"); continue; @@ -1597,6 +1601,14 @@ int main(int argc, char **argv) { return 1; } break; + + case TGA_DFMT: + image = tgatoimage(parameters.infile, ¶meters); + if (!image) { + fprintf(stderr, "Unable to load tga file\n"); + return 1; + } + break; } /* Decide if MCT should be used */ parameters.tcp_mct = image->numcomps == 3 ? 1 : 0; diff --git a/codec/j2k_to_image.c b/codec/j2k_to_image.c index 977b1d03..18f478ac 100644 --- a/codec/j2k_to_image.c +++ b/codec/j2k_to_image.c @@ -55,6 +55,7 @@ #define YUV_DFMT 13 #define TIF_DFMT 14 #define RAW_DFMT 15 +#define TGA_DFMT 16 /* ----------------------------------------------------------------------- */ @@ -96,14 +97,14 @@ void decode_help_display() { fprintf(stdout," -OutFor \n"); fprintf(stdout," REQUIRED only if -ImgDir is used\n"); fprintf(stdout," Need to specify only format without filename \n"); - fprintf(stdout," Currently accepts PGM, PPM, PNM, PGX, BMP, TIF and RAW formats\n"); + fprintf(stdout," Currently accepts PGM, PPM, PNM, PGX, BMP, TIF, RAW and TGA formats\n"); fprintf(stdout," -i \n"); fprintf(stdout," REQUIRED only if an Input image directory not specified\n"); fprintf(stdout," Currently accepts J2K-files, JP2-files and JPT-files. The file type\n"); fprintf(stdout," is identified based on its suffix.\n"); fprintf(stdout," -o \n"); fprintf(stdout," REQUIRED\n"); - fprintf(stdout," Currently accepts PGM, PPM, PNM, PGX, BMP, TIF and RAW files\n"); + fprintf(stdout," Currently accepts PGM, PPM, PNM, PGX, BMP, TIF, RAW and TGA files\n"); fprintf(stdout," Binary data is written to the file (not ascii). If a PGX\n"); fprintf(stdout," filename is given, there will be as many output files as there are\n"); fprintf(stdout," components: an indice starting from 0 will then be appended to the\n"); @@ -182,15 +183,15 @@ int load_images(dircnt_t *dirptr, char *imgdirpath){ int get_file_format(char *filename) { unsigned int i; - static const char *extension[] = {"pgx", "pnm", "pgm", "ppm", "bmp","tif", "raw", "j2k", "jp2", "jpt", "j2c" }; - static const int format[] = { PGX_DFMT, PXM_DFMT, PXM_DFMT, PXM_DFMT, BMP_DFMT, TIF_DFMT, RAW_DFMT, J2K_CFMT, JP2_CFMT, JPT_CFMT, J2K_CFMT }; + static const char *extension[] = {"pgx", "pnm", "pgm", "ppm", "bmp","tif", "raw", "tga", "j2k", "jp2", "jpt", "j2c" }; + static const int format[] = { PGX_DFMT, PXM_DFMT, PXM_DFMT, PXM_DFMT, BMP_DFMT, TIF_DFMT, RAW_DFMT, TGA_DFMT, J2K_CFMT, JP2_CFMT, JPT_CFMT, J2K_CFMT }; char * ext = strrchr(filename, '.'); if (ext == NULL) return -1; ext++; if(ext) { for(i = 0; i < sizeof(format)/sizeof(*format); i++) { - if(strnicmp(ext, extension[i], 3) == 0) { + if(_strnicmp(ext, extension[i], 3) == 0) { return format[i]; } } @@ -276,9 +277,10 @@ int parse_cmdline_decoder(int argc, char **argv, opj_dparameters_t *parameters,i case BMP_DFMT: case TIF_DFMT: case RAW_DFMT: + case TGA_DFMT: break; default: - fprintf(stderr, "Unknown output format image %s [only *.pnm, *.pgm, *.ppm, *.pgx, *.bmp, *.tif or *.raw]!! \n", outfile); + fprintf(stderr, "Unknown output format image %s [only *.pnm, *.pgm, *.ppm, *.pgx, *.bmp, *.tif, *.raw or *.tga]!! \n", outfile); return 1; } strncpy(parameters->outfile, outfile, sizeof(parameters->outfile)-1); @@ -310,8 +312,11 @@ int parse_cmdline_decoder(int argc, char **argv, opj_dparameters_t *parameters,i case RAW_DFMT: img_fol->out_format = "raw"; break; + case TGA_DFMT: + img_fol->out_format = "raw"; + break; default: - fprintf(stderr, "Unknown output format image %s [only *.pnm, *.pgm, *.ppm, *.pgx, *.bmp, *.tif or *.raw]!! \n"); + fprintf(stderr, "Unknown output format image %s [only *.pnm, *.pgm, *.ppm, *.pgx, *.bmp, *.tif, *.raw or *.tga]!! \n", outformat); return 1; break; } @@ -439,7 +444,7 @@ int parse_cmdline_decoder(int argc, char **argv, opj_dparameters_t *parameters,i } if(img_fol->set_out_format == 0){ fprintf(stderr, "Error: When -ImgDir is used, -OutFor must be used !!\n"); - fprintf(stderr, "Only one format allowed! Valid format PGM, PPM, PNM, PGX, BMP, TIF, RAW!!\n"); + fprintf(stderr, "Only one format allowed! Valid format PGM, PPM, PNM, PGX, BMP, TIF, RAW and TGA!!\n"); return 1; } if(!((parameters->outfile[0] == 0))){ @@ -450,7 +455,7 @@ int parse_cmdline_decoder(int argc, char **argv, opj_dparameters_t *parameters,i if((parameters->infile[0] == 0) || (parameters->outfile[0] == 0)) { fprintf(stderr, "Error: One of the options -i or -ImgDir must be specified\n"); fprintf(stderr, "Error: When using -i, -o must be used\n"); - fprintf(stderr, "usage: image_to_j2k -i *.j2k/jp2/j2c -o *.pgm/ppm/pnm/pgx/bmp/tif/raw(+ options)\n"); + fprintf(stderr, "usage: image_to_j2k -i *.j2k/jp2/j2c -o *.pgm/ppm/pnm/pgx/bmp/tif/raw/tga(+ options)\n"); return 1; } } @@ -722,6 +727,15 @@ int main(int argc, char **argv) { fprintf(stdout,"Successfully generated Outfile %s\n",parameters.outfile); } break; + + case TGA_DFMT: /* TGA */ + if(imagetotga(image, parameters.outfile)){ + fprintf(stdout,"Error generating tga file. Outfile %s not generated\n",parameters.outfile); + } + else { + fprintf(stdout,"Successfully generated Outfile %s\n",parameters.outfile); + } + break; } /* free remaining structures */