diff options
Diffstat (limited to 'converter/ppm/tgatoppm.c')
-rw-r--r-- | converter/ppm/tgatoppm.c | 462 |
1 files changed, 462 insertions, 0 deletions
diff --git a/converter/ppm/tgatoppm.c b/converter/ppm/tgatoppm.c new file mode 100644 index 00000000..9f2bc4c0 --- /dev/null +++ b/converter/ppm/tgatoppm.c @@ -0,0 +1,462 @@ +/* tgatoppm.c - read a TrueVision Targa file and write a portable pixmap +** +** Partially based on tga2rast, version 1.0, by Ian MacPhedran. +** +** Copyright (C) 1989 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#define _BSD_SOURCE 1 /* Make sure strdup() is in string.h */ +#define _XOPEN_SOURCE 500 /* Make sure strdup() is in string.h */ + +#include <string.h> +#include "ppm.h" +#include "tga.h" +#include "shhopt.h" +#include "nstring.h" + +#define MAXCOLORS 16384 + +static int mapped, rlencoded; + +static pixel ColorMap[MAXCOLORS]; +static gray AlphaMap[MAXCOLORS]; +static int RLE_count = 0, RLE_flag = 0; + +struct cmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char *input_filename; + unsigned int headerdump; + const char *alpha_filename; + unsigned int alpha_stdout; +}; + + + +static void +parseCommandLine(int argc, char ** argv, + struct cmdlineInfo *cmdlineP) { +/*---------------------------------------------------------------------------- + Note that many of the strings that this function returns in the + *cmdlineP structure are actually in the supplied argv array. And + sometimes, one of these strings is actually just a suffix of an entry + in argv! +-----------------------------------------------------------------------------*/ + optEntry *option_def = malloc(100*sizeof(optEntry)); + /* Instructions to OptParseOptions2 on how to parse our options. + */ + optStruct3 opt; + + unsigned int option_def_index; + + unsigned int alpha_spec; + + option_def_index = 0; /* incremented by OPTENTRY */ + OPTENT3(0, "headerdump", OPT_FLAG, NULL, &cmdlineP->headerdump, 0); + OPTENT3(0, "debug", OPT_FLAG, NULL, &cmdlineP->headerdump, 0); + OPTENT3(0, "alphaout", OPT_STRING, &cmdlineP->alpha_filename, + &alpha_spec, 0); + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ + + optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + if (argc - 1 == 0) + cmdlineP->input_filename = "="; /* he wants stdin */ + else if (argc - 1 == 1) + cmdlineP->input_filename = strdup(argv[1]); + else + pm_error("Too many arguments. The only argument accepted " + "is the input file specification"); + + if (alpha_spec && + STREQ(cmdlineP->alpha_filename, "-")) + cmdlineP->alpha_stdout = 1; + else + cmdlineP->alpha_stdout = 0; + + if (!alpha_spec) + cmdlineP->alpha_filename = NULL; +} + + + + +static unsigned char +getbyte(FILE * const ifP) { + + unsigned char c; + + if ( fread( (char*) &c, 1, 1, ifP ) != 1 ) + pm_error( "EOF / read error" ); + + return c; +} + + + +static void +get_pixel(FILE * const ifP, pixel * dest, int Size, gray *alpha_p) { + + static pixval Red, Grn, Blu; + static pixval Alpha; + unsigned char j, k; + static unsigned int l; + + /* Check if run length encoded. */ + if ( rlencoded ) + { + if ( RLE_count == 0 ) + { /* Have to restart run. */ + unsigned char i; + i = getbyte( ifP ); + RLE_flag = ( i & 0x80 ); + if ( RLE_flag == 0 ) + /* Stream of unencoded pixels. */ + RLE_count = i + 1; + else + /* Single pixel replicated. */ + RLE_count = i - 127; + /* Decrement count & get pixel. */ + --RLE_count; + } + else + { /* Have already read count & (at least) first pixel. */ + --RLE_count; + if ( RLE_flag != 0 ) + /* Replicated pixels. */ + goto PixEncode; + } + } + /* Read appropriate number of bytes, break into RGB. */ + switch ( Size ) + { + case 8: /* Grayscale, read and triplicate. */ + Red = Grn = Blu = l = getbyte( ifP ); + Alpha = 0; + break; + + case 16: /* 5 bits each of red green and blue. */ + case 15: /* Watch byte order. */ + j = getbyte( ifP ); + k = getbyte( ifP ); + l = ( (unsigned int) k << 8 ) + j; + Red = ( k & 0x7C ) >> 2; + Grn = ( ( k & 0x03 ) << 3 ) + ( ( j & 0xE0 ) >> 5 ); + Blu = j & 0x1F; + Alpha = 0; + break; + + case 32: /* 8 bits each of blue, green, red, and alpha */ + case 24: /* 8 bits each of blue, green, and red. */ + Blu = getbyte( ifP ); + Grn = getbyte( ifP ); + Red = getbyte( ifP ); + if ( Size == 32 ) + Alpha = getbyte( ifP ); + else + Alpha = 0; + l = 0; + break; + + default: + pm_error( "unknown pixel size (#2) - %d", Size ); + } + +PixEncode: + if ( mapped ) { + *dest = ColorMap[l]; + *alpha_p = AlphaMap[l]; + } else { + PPM_ASSIGN( *dest, Red, Grn, Blu ); + *alpha_p = Alpha; + } + } + + + +static void +readtga(FILE * const ifP, struct ImageHeader * tgaP) { + + unsigned char flags; + ImageIDField junk; + + tgaP->IdLength = getbyte( ifP ); + tgaP->CoMapType = getbyte( ifP ); + tgaP->ImgType = getbyte( ifP ); + tgaP->Index_lo = getbyte( ifP ); + tgaP->Index_hi = getbyte( ifP ); + tgaP->Length_lo = getbyte( ifP ); + tgaP->Length_hi = getbyte( ifP ); + tgaP->CoSize = getbyte( ifP ); + tgaP->X_org_lo = getbyte( ifP ); + tgaP->X_org_hi = getbyte( ifP ); + tgaP->Y_org_lo = getbyte( ifP ); + tgaP->Y_org_hi = getbyte( ifP ); + tgaP->Width_lo = getbyte( ifP ); + tgaP->Width_hi = getbyte( ifP ); + tgaP->Height_lo = getbyte( ifP ); + tgaP->Height_hi = getbyte( ifP ); + tgaP->PixelSize = getbyte( ifP ); + flags = getbyte( ifP ); + tgaP->AttBits = flags & 0xf; + tgaP->Rsrvd = ( flags & 0x10 ) >> 4; + tgaP->OrgBit = ( flags & 0x20 ) >> 5; + tgaP->IntrLve = ( flags & 0xc0 ) >> 6; + + if ( tgaP->IdLength != 0 ) + fread( junk, 1, (int) tgaP->IdLength, ifP ); +} + + + +static void +get_map_entry(FILE * const ifP, pixel * Value, int Size, gray * Alpha) { + + unsigned char j, k, r, g, b, a; + + /* Read appropriate number of bytes, break into rgb & put in map. */ + switch ( Size ) + { + case 8: /* Grayscale, read and triplicate. */ + r = g = b = getbyte( ifP ); + a = 0; + break; + + case 16: /* 5 bits each of red green and blue. */ + case 15: /* Watch for byte order. */ + j = getbyte( ifP ); + k = getbyte( ifP ); + r = ( k & 0x7C ) >> 2; + g = ( ( k & 0x03 ) << 3 ) + ( ( j & 0xE0 ) >> 5 ); + b = j & 0x1F; + a = 0; + break; + + case 32: /* 8 bits each of blue, green, red, and alpha */ + case 24: /* 8 bits each of blue green and red. */ + b = getbyte( ifP ); + g = getbyte( ifP ); + r = getbyte( ifP ); + if ( Size == 32 ) + a = getbyte( ifP ); + else + a = 0; + break; + + default: + pm_error( "unknown colormap pixel size (#2) - %d", Size ); + } + PPM_ASSIGN( *Value, r, g, b ); + *Alpha = a; +} + + + +static void +dumpHeader(struct ImageHeader const tga_head) { + const char * imgTypeName; + switch(tga_head.ImgType) { + case TGA_Map: imgTypeName = "TGA_Map"; break; + case TGA_RGB: imgTypeName = "TGA_RGB"; break; + case TGA_Mono: imgTypeName = "TGA_Mono"; break; + case TGA_RLEMap: imgTypeName = "TGA_RLEMap"; break; + case TGA_RLERGB: imgTypeName = "TGA_RLERGB"; break; + case TGA_RLEMono: imgTypeName = "TGA_RLEMono"; break; + case TGA_CompMap: imgTypeName = "TGA_CompMap"; break; + case TGA_CompMap4: imgTypeName = "TGA_CompMap4"; break; + default: imgTypeName = "unknown"; + } + pm_message( "IdLength = %d", (int) tga_head.IdLength ); + pm_message( "CoMapType = %d", (int) tga_head.CoMapType ); + pm_message( "ImgType = %d (%s)", (int) tga_head.ImgType, imgTypeName ); + pm_message( "Index_lo = %d", (int) tga_head.Index_lo ); + pm_message( "Index_hi = %d", (int) tga_head.Index_hi ); + pm_message( "Length_lo = %d", (int) tga_head.Length_lo ); + pm_message( "Length_hi = %d", (int) tga_head.Length_hi ); + pm_message( "CoSize = %d", (int) tga_head.CoSize ); + pm_message( "X_org_lo = %d", (int) tga_head.X_org_lo ); + pm_message( "X_org_hi = %d", (int) tga_head.X_org_hi ); + pm_message( "Y_org_lo = %d", (int) tga_head.Y_org_lo ); + pm_message( "Y_org_hi = %d", (int) tga_head.Y_org_hi ); + pm_message( "Width_lo = %d", (int) tga_head.Width_lo ); + pm_message( "Width_hi = %d", (int) tga_head.Width_hi ); + pm_message( "Height_lo = %d", (int) tga_head.Height_lo ); + pm_message( "Height_hi = %d", (int) tga_head.Height_hi ); + pm_message( "PixelSize = %d", (int) tga_head.PixelSize ); + pm_message( "AttBits = %d", (int) tga_head.AttBits ); + pm_message( "Rsrvd = %d", (int) tga_head.Rsrvd ); + pm_message( "OrgBit = %d", (int) tga_head.OrgBit ); + pm_message( "IntrLve = %d", (int) tga_head.IntrLve ); +} + + + +int +main(int argc, char * argv[]) { + + struct cmdlineInfo cmdline; + struct ImageHeader tga_head; + FILE* ifP; + FILE *imageout_file, *alpha_file; + int rows, cols, row, realrow, truerow, baserow; + int maxval; + pixel** pixels; /* The image array in ppm format */ + gray** alpha; /* The alpha channel array in pgm format */ + + ppm_init(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + ifP = pm_openr(cmdline.input_filename); + + if (cmdline.alpha_stdout) + alpha_file = stdout; + else if (cmdline.alpha_filename == NULL) + alpha_file = NULL; + else + alpha_file = pm_openw(cmdline.alpha_filename); + + if (cmdline.alpha_stdout) + imageout_file = NULL; + else + imageout_file = stdout; + + /* Read the Targa file header. */ + readtga(ifP, &tga_head); + + if (cmdline.headerdump) + dumpHeader(tga_head); + + rows = ((int) tga_head.Height_lo) + ((int) tga_head.Height_hi) * 256; + cols = ((int) tga_head.Width_lo) + ((int) tga_head.Width_hi) * 256; + + switch (tga_head.ImgType) { + case TGA_Map: + case TGA_RGB: + case TGA_Mono: + case TGA_RLEMap: + case TGA_RLERGB: + case TGA_RLEMono: + break; + default: + pm_error("unknown Targa image type %d", tga_head.ImgType); + } + + if (tga_head.ImgType == TGA_Map || + tga_head.ImgType == TGA_RLEMap || + tga_head.ImgType == TGA_CompMap || + tga_head.ImgType == TGA_CompMap4) + { /* Color-mapped image */ + if (tga_head.CoMapType != 1) + pm_error( + "mapped image (type %d) with color map type != 1", + tga_head.ImgType ); + mapped = true; + /* Figure maxval from CoSize. */ + switch (tga_head.CoSize) { + case 8: + case 24: + case 32: + maxval = 255; + break; + + case 15: + case 16: + maxval = 31; + break; + + default: + pm_error( + "unknown colormap pixel size - %d", tga_head.CoSize ); + } + } else { + /* Not colormap, so figure maxval from PixelSize. */ + mapped = false; + switch ( tga_head.PixelSize ) { + case 8: + case 24: + case 32: + maxval = 255; + break; + + case 15: + case 16: + maxval = 31; + break; + + default: + pm_error("unknown pixel size - %d", tga_head.PixelSize); + } + } + + /* If required, read the color map information. */ + if ( tga_head.CoMapType != 0 ) { + unsigned int i; + unsigned int temp1, temp2; + + temp1 = tga_head.Index_lo + tga_head.Index_hi * 256; + temp2 = tga_head.Length_lo + tga_head.Length_hi * 256; + if ((temp1 + temp2 + 1) >= MAXCOLORS) + pm_error("too many colors - %d", (temp1 + temp2 + 1)); + for (i = temp1; i < (temp1 + temp2); ++i) + get_map_entry(ifP, &ColorMap[i], (int) tga_head.CoSize, + &AlphaMap[i]); + } + + /* Check run-length encoding. */ + if (tga_head.ImgType == TGA_RLEMap || + tga_head.ImgType == TGA_RLERGB || + tga_head.ImgType == TGA_RLEMono) + rlencoded = 1; + else + rlencoded = 0; + + /* Read the Targa file body and convert to portable format. */ + pixels = ppm_allocarray( cols, rows ); + alpha = pgm_allocarray( cols, rows ); + truerow = 0; + baserow = 0; + for (row = 0; row < rows; ++row) { + unsigned int col; + + realrow = truerow; + if (tga_head.OrgBit == 0) + realrow = rows - realrow - 1; + + for (col = 0; col < cols; ++col) + get_pixel(ifP, &(pixels[realrow][col]), (int) tga_head.PixelSize, + &(alpha[realrow][col])); + if (tga_head.IntrLve == TGA_IL_Four) + truerow += 4; + else if (tga_head.IntrLve == TGA_IL_Two) + truerow += 2; + else + ++truerow; + if (truerow >= rows) + truerow = ++baserow; + } + pm_close(ifP); + + if (imageout_file) + ppm_writeppm(imageout_file, pixels, cols, rows, (pixval) maxval, 0); + if (alpha_file) + pgm_writepgm(alpha_file, alpha, cols, rows, (pixval) maxval, 0); + if (imageout_file) + pm_close(imageout_file); + if (alpha_file) + pm_close(alpha_file); + + return 0; +} |