// (C) Antony Searle 1998 // Legal: Do whatever you like with this code! // I accept no responsibilty for damage or loss incurred by using this program. #include #include #include #include long Target; #define TARGET_NONE 0 #define TARGET_TGA 1 #define TARGET_SGI 2 #define TARGET_RGBA 3 #define TARGET_RGB 4 class iColour { // Integer colour representation (used for storage) public: unsigned char b, g, r, a; }; class Bitmap { // Bitmaps in B-G-R-A format, as in 32 bit TARGA public: unsigned long uMax, vMax; iColour *Pixel; int Create(unsigned long u, unsigned long v); int Destroy(void); int LoadTGA(char *Name); int SaveTGA(char *Name); int LoadRGBA(char *Name); int SaveRGBA(char *Name); int LoadRGB(char *Name); int SaveRGB(char *Name); }; int Bitmap::Create(unsigned long u, unsigned long v) { uMax = u; vMax = v; Pixel = new iColour[uMax * vMax]; if(Pixel == NULL) { printf("Bitmap::Create: Can't allocate %li bytes of memory for new bitmap (%li x %li)\n", 4 * uMax * vMax, uMax, vMax); return 0; } memset(Pixel, 0, 4 * uMax * vMax); return 1; } int Bitmap::Destroy(void) { uMax = 0; vMax = 0; delete Pixel; return 1; } int Bitmap::LoadTGA(char *Name) { FILE *Handle; unsigned char Header[18]; Handle = fopen(Name, "rb"); if(Handle == NULL) { printf("Bitmap::LoadTGA: Can't open %s\n", Name); return 0; } fseek(Handle, 0, 0); fread(Header, 1, 18, Handle); if(Header[2] != 2) { printf("Bitmap::LoadTGA: %s is not a valid TARGA file\n", Name); return 0; } if(Header[16] != 32) { printf("Bitmap::LoadTGA: %s is a %i bit file.\n Only 32 bit images (24 bit colour + 8 bit alpha channel) are supported.\n Please convert this file\n", Name, Header[16]); exit(EXIT_FAILURE); } uMax = ((unsigned long) Header[13] << 8) + Header[12]; vMax = ((unsigned long) Header[15] << 8) + Header[14]; Pixel = new iColour[uMax * vMax]; if(Pixel == NULL) { printf("Bitmap::LoadTGA: Can't allocate %li bytes of memory for %s (%li x %li)\n", 4 * uMax * vMax, Name, uMax, vMax); return 0; } fseek(Handle, 18, 0); fread(Pixel, 4, uMax*vMax, Handle); fclose(Handle); printf("Bitmap::LoadTGA: %s (%li x %li) successfully loaded\n", Name, uMax, vMax); return 1; } int Bitmap::SaveTGA(char *Name) { FILE *Handle; unsigned char Header[18]; Header[ 0] = 0; Header[ 1] = 0; Header[ 2] = 2; // Uncompressed, uninteresting Header[ 3] = 0; Header[ 4] = 0; Header[ 5] = 0; Header[ 6] = 0; Header[ 7] = 0; Header[ 8] = 0; Header[ 9] = 0; Header[10] = 0; Header[11] = 0; Header[12] = (unsigned char) uMax; // Dimensions Header[13] = (unsigned char) ((unsigned long) uMax >> 8); Header[14] = (unsigned char) vMax; Header[15] = (unsigned char) ((unsigned long) vMax >> 8); Header[16] = 32; // Bits per pixel Header[17] = 8; Handle = fopen(Name, "wb"); if(Handle == NULL) { printf("Bitmap::SaveTGA: Can't open %s\n", Name); return 0; } fseek(Handle, 0, 0); fwrite(Header, 1, 18, Handle); fseek(Handle, 18, 0); fwrite(Pixel, 4, uMax * vMax, Handle); fclose(Handle); printf("Bitmap::SaveTGA: %s (%li x %li) successfully saved\n", Name, uMax, vMax); return 1; } int Bitmap::LoadRGBA(char *Name) { // As storage and file layout differ, we have to use a different method. FILE *Handle; unsigned char Header[512]; unsigned char *Buffer; unsigned long i, size; Handle = fopen(Name, "rb"); if(Handle == NULL) { printf("Bitmap::LoadRGBA: Can't open file %s\n", Name); return 0; } fseek(Handle, 0, 0); fread(Header, 1, 512, Handle); if( (((unsigned short) Header[0] << 8) + Header[1] )!= 474 ) { printf("Bitmap::LoadRGBA: %s is not an SGI image file\n", Name); return 0; } if(Header[2] != 0) { printf("Bitmap::LoadRGBA: %s is RLE (compressed). \nOnly uncompressed images are supported.\n Please convert this file\n", Name); return 0; } if(Header[3] != 1) { printf("Bitmap::LoadRGBA: %s contains more than one byte per colour channel per pixel\n", Name); return 0; } if(Header[11] != 4) { printf("Bitmap::LoadRGBA: %s is not a four-channel (RGBA) image\n", Name); return 0; } uMax = ((unsigned long) Header[6] << 8) + Header[7]; vMax = ((unsigned long) Header[8] << 8) + Header[9]; Pixel = new iColour[uMax * vMax]; Buffer = new unsigned char[uMax * vMax * 4]; if(Pixel == NULL) { printf("LoadRGBA: Can't allocate %li bytes of memory for %s (%li x %li)\n", 4 * uMax * vMax, Name, uMax, vMax); return 0; } if(Buffer == NULL) { printf("LoadRGBA: Can't allocate %li bytes of memory to unpack %s (%li x %li)\n", 4 * uMax * vMax, Name, uMax, vMax); return 0; } fseek(Handle, 512, 0); fread(Buffer, 4, uMax*vMax, Handle); fclose(Handle); // Unfortunately, RGBA and TGA images store colour channels according to // different conventions, so we have to unpack: size = uMax*vMax; i = 0; do { Pixel[i].r = Buffer[i]; Pixel[i].g = Buffer[i + size]; Pixel[i].b = Buffer[i + (size * 2)]; Pixel[i].a = Buffer[i + (size * 3)]; } while(++i < size); delete Buffer; printf("Bitmap::LoadRGBA: %s (%li x %li) successfully loaded\n", Name, uMax, vMax); return 1; } int Bitmap::SaveRGBA(char *Name) { FILE *Handle; unsigned char Header[512]; unsigned char *Buffer; unsigned long i, size; memset(Header, 0, 512); Header[0] = 474 >> 8; // Magic number Header[1] = 474 & 255; // ... Header[2] = 0; // Verbatim Header[3] = 1; // BPC Header[4] = 0; // Dimension Header[5] = 3; // ... Header[6] = (unsigned char) ((unsigned long) uMax >> 8); // X size Header[7] = (unsigned char) ((unsigned long) uMax & 255); // ... Header[8] = (unsigned char) ((unsigned long) vMax >> 8); // Y size Header[9] = (unsigned char) ((unsigned long) vMax & 255); // ... Header[10] = 0; // Z Size (4 channel RGBA) Header[11] = 4; // ... // 4 byte pixel min 00 Header[16] = 0; // 4 byte pixel max FF Header[17] = 0; // ... Header[18] = 0; // ... Header[19] = 255; // ... // And the rest is irrelevant... Handle = fopen(Name, "wb"); if(Handle == NULL) { printf("Bitmap::SaveRGBA: Can't open %s\n", Name); return 0; } fseek(Handle, 0, 0); fwrite(Header, 1, 512, Handle); // Unfortunately, RGBA and TGA images store colour channels according to // different conventions, so we have to unpack: Buffer = new unsigned char[4*uMax*vMax]; if(Buffer == NULL) { printf("LoadRGBA: Can't allocate %li bytes of memory to pack %s (%li x %li)\n", 4 * uMax * vMax, Name, uMax, vMax); return 0; } size = uMax*vMax; i = 0; do { Buffer[i] = Pixel[i].r; Buffer[i + size] = Pixel[i].g; Buffer[i + (size * 2)] = Pixel[i].b; Buffer[i + (size * 3)] = Pixel[i].a; } while(++i < size); // Save new data fseek(Handle, 512, 0); fwrite(Buffer, 4, uMax*vMax, Handle); fclose(Handle); delete Buffer; printf("Bitmap::SaveRGBA: %s (%li x %li) successfully saved\n", Name, uMax, vMax); return 1; } int Bitmap::LoadRGB(char *Name) { // As storage and file layout differ, we have to use a different method. FILE *Handle; unsigned char Header[512]; unsigned char *Buffer; unsigned long i, size; Handle = fopen(Name, "rb"); if(Handle == NULL) { printf("Bitmap::LoadRGB: Can't open file %s\n", Name); return 0; } fseek(Handle, 0, 0); fread(Header, 1, 512, Handle); if( (((unsigned short) Header[0] << 8) + Header[1] )!= 474 ) { printf("Bitmap::LoadRGB: %s is not an SGI image file\n", Name); return 0; } if(Header[2] != 0) { printf("Bitmap::LoadRGB: %s is RLE (compressed). \nOnly uncompressed images are supported.\n Please convert this file\n", Name); return 0; } if(Header[3] != 1) { printf("Bitmap::LoadRGB: %s contains more than one byte per colour channel per pixel\n", Name); return 0; } if(Header[11] != 3) { printf("Bitmap::LoadRGB: %s is not a three-channel (RGB) image\n", Name); return 0; } uMax = ((unsigned long) Header[6] << 8) + Header[7]; vMax = ((unsigned long) Header[8] << 8) + Header[9]; Pixel = new iColour[uMax * vMax]; Buffer = new unsigned char[uMax * vMax * 3]; if(Pixel == NULL) { printf("LoadRGB: Can't allocate %li bytes of memory for %s (%li x %li)\n", 4 * uMax * vMax, Name, uMax, vMax); return 0; } if(Buffer == NULL) { printf("LoadRGB: Can't allocate %li bytes of memory to unpack %s (%li x %li)\n", 3 * uMax * vMax, Name, uMax, vMax); return 0; } fseek(Handle, 512, 0); fread(Buffer, 3, uMax*vMax, Handle); fclose(Handle); // Unfortunately, RGBA and TGA images store colour channels according to // different conventions, so we have to unpack: size = uMax*vMax; i = 0; do { Pixel[i].r = Buffer[i]; Pixel[i].g = Buffer[i + size]; Pixel[i].b = Buffer[i + (size * 2)]; Pixel[i].a = 0; // Dummy alpha } while(++i < size); delete Buffer; printf("Bitmap::LoadRGB: %s (%li x %li) successfully loaded\n", Name, uMax, vMax); return 1; } int Bitmap::SaveRGB(char *Name) { FILE *Handle; unsigned char Header[512]; unsigned char *Buffer; unsigned long i, size; memset(Header, 0, 512); Header[0] = 474 >> 8; // Magic number Header[1] = 474 & 255; // ... Header[2] = 0; // Verbatim Header[3] = 1; // BPC Header[4] = 0; // Dimension Header[5] = 3; // ... Header[6] = (unsigned char) ((unsigned long) uMax >> 8); // X size Header[7] = (unsigned char) ((unsigned long) uMax & 255); // ... Header[8] = (unsigned char) ((unsigned long) vMax >> 8); // Y size Header[9] = (unsigned char) ((unsigned long) vMax & 255); // ... Header[10] = 0; // Z Size (3 channel RGB) Header[11] = 3; // ... // 4 byte pixel min 00 Header[16] = 0; // 4 byte pixel max FF Header[17] = 0; // ... Header[18] = 0; // ... Header[19] = 255; // ... // And the rest is irrelevant... Handle = fopen(Name, "wb"); if(Handle == NULL) { printf("Bitmap::SaveRGB: Can't open %s\n", Name); return 0; } fseek(Handle, 0, 0); fwrite(Header, 1, 512, Handle); // Unfortunately, RGBA and TGA images store colour channels according to // different conventions, so we have to unpack: Buffer = new unsigned char[3*uMax*vMax]; if(Buffer == NULL) { printf("LoadRGB: Can't allocate %li bytes of memory to pack %s (%li x %li)\n", 3 * uMax * vMax, Name, uMax, vMax); return 0; } size = uMax*vMax; i = 0; do { Buffer[i] = Pixel[i].r; Buffer[i + size] = Pixel[i].g; Buffer[i + (size * 2)] = Pixel[i].b; } while(++i < size); // Save new data fseek(Handle, 512, 0); fwrite(Buffer, 3, uMax*vMax, Handle); fclose(Handle); delete Buffer; printf("Bitmap::SaveRGB: %s (%li x %li) successfully saved\n", Name, uMax, vMax); return 1; } int TypeImage(char *Name) { FILE *Handle; unsigned char Header[12]; Handle = fopen(Name, "rb"); if(Handle == NULL) { printf("Can't open %s - ignoring\n", Name); return 0; } fseek(Handle, 0, 0); fread(Header, 1, 12, Handle); fclose(Handle); switch(Header[0]) { case 0: // TGA return 1; case 1: // SGI switch(Header[11]) { case 3: // RGB return 2; case 4: // RGBA return 3; } default: printf("%s is not a convertable image\n", Name); return 0; } } char *MakeName(char *Name, long t) { long k, l; char *Write; k = 0; l = 0; while(Name[k] != 0) { if(Name[k] == '.') { l++ ; } k++; } if(l > 0) { // We have an extension, so prune it off while(Name[k] != '.') { k--; } // We are on the spot (literally) Name[k] = 0; Write = new char[k + 5]; if(Write == NULL) { printf("Out of memory\n"); exit(EXIT_FAILURE); } switch(t) { case TARGET_TGA: sprintf(Write, "%s.tga", Name); break; case TARGET_SGI: sprintf(Write, "%s.sgi", Name); break; } Name[k] = '.'; } else { // No extension, so add one Write = new char[k + 5]; if(Write == NULL) { printf("Out of memory\n"); exit(EXIT_FAILURE); } switch(t) { case TARGET_TGA: sprintf(Write, "%s.tga", Name); break; case TARGET_SGI: sprintf(Write, "%s.sgi", Name); break; } Name[k] = '.'; } return Write; } void main(int argc, char *argv[], char *envp[]) { long i, j; char *Write; Bitmap Image; printf("\n Converts TARGA and SGI images (c) Antony Searle 1998\n\n"); if(argc == 1) { // We have no input: give help printf("Syntax: Convert [options] [file1] [file2]...\n\n"); printf("Options:\n"); printf(" NONE Defaults to changing all TGA to (24 bit) SGI, and all SGI to TGA\n"); printf(" -tga Changes all SGI images to TGA \n"); printf(" -sgi Changes all TGA images to (24 bit) SGI images \n"); printf(" -rgb Changes all images to (24 bit) SGI images \n"); printf(" -rgba Changes all images to (32 bit) SGI images \n\n"); printf("Default behaviour will cause the loss of any TGA alpha channels\n"); printf("Files with incorrect extensions will be copied to their correct\n extensions (.tga, .sgi)\n\n"); return; } Target = TARGET_NONE; i = 1; while((argv[i])[0] == '-') { // We have an option, not a file switch((argv[i])[1]) { case 't': Target = TARGET_TGA; printf("Converting all files to TARGA (.tga)\n"); break; case 's': Target = TARGET_SGI; printf("Converting all TARGA files to (24 bit) SGI (.sgi)\n"); break; case 'r': switch((argv[i])[4]) { case 0: Target = TARGET_RGB; printf("Converting all files to (24 bit) SGI (.sgi)\n"); break; case 'a': Target = TARGET_RGBA; printf("Converting all files to (32 bit) SGI (.sgi)\n"); break; default: printf("Unknown option %s\n", argv[i]); return; } break; default: printf("Unknown option [%s]\n", argv[i]); return; } if(++i == argc) { printf("No files\n"); return; } } // Have parsed options: Now, we go to work on the images. do { // First, determine the type of the file: j = TypeImage(argv[i]); if(j != 0) { // We have a recognised format switch(Target) { case TARGET_NONE: // Take SGI->TGA, TGA->RGB switch(j) { case 1: //TGA->RGB Write = MakeName(argv[i], TARGET_SGI); if(Image.LoadTGA(argv[i])) { Image.SaveRGB(Write); Image.Destroy(); } delete Write; break; case 2: //RGB->TGA Write = MakeName(argv[i], TARGET_TGA); if(Image.LoadRGB(argv[i])) { Image.SaveTGA(Write); Image.Destroy(); } delete Write; break; case 3: //RGBA->TGA Write = MakeName(argv[i], TARGET_TGA); if(Image.LoadRGBA(argv[i])) { Image.SaveTGA(Write); Image.Destroy(); } delete Write; break; } break; case TARGET_TGA: // Take SGI->TGA switch(j) { case 1: //TGA->TGA Write = MakeName(argv[i], TARGET_TGA); if(Image.LoadTGA(argv[i])) { Image.SaveTGA(Write); Image.Destroy(); } delete Write; break; case 2: //RGB->TGA Write = MakeName(argv[i], TARGET_TGA); if(Image.LoadRGB(argv[i])) { Image.SaveTGA(Write); Image.Destroy(); } delete Write; break; case 3: //RGBA->TGA Write = MakeName(argv[i], TARGET_TGA); if(Image.LoadRGBA(argv[i])) { Image.SaveTGA(Write); Image.Destroy(); } delete Write; break; } break; case TARGET_SGI: // Take TGA->RGB switch(j) { case 1: //TGA->RGB Write = MakeName(argv[i], TARGET_SGI); if(Image.LoadTGA(argv[i])) { Image.SaveRGB(Write); Image.Destroy(); } delete Write; break; case 2: //RGB->RGB Write = MakeName(argv[i], TARGET_SGI); if(Image.LoadRGB(argv[i])) { Image.SaveRGB(Write); Image.Destroy(); } delete Write; break; case 3: //RGBA->RGBA Write = MakeName(argv[i], TARGET_SGI); if(Image.LoadRGBA(argv[i])) { Image.SaveRGBA(Write); Image.Destroy(); } delete Write; break; } break; case TARGET_RGB: // Take TGA->RGB, RGBA->RGB switch(j) { case 1: //TGA->RGB Write = MakeName(argv[i], TARGET_SGI); if(Image.LoadTGA(argv[i])) { Image.SaveRGB(Write); Image.Destroy(); } delete Write; break; case 2: //RGB->RGB Write = MakeName(argv[i], TARGET_SGI); if(Image.LoadRGB(argv[i])) { Image.SaveRGB(Write); Image.Destroy(); } delete Write; break; case 3: //RGBA->RGB Write = MakeName(argv[i], TARGET_SGI); if(Image.LoadRGBA(argv[i])) { Image.SaveRGB(Write); Image.Destroy(); } delete Write; break; } break; case TARGET_RGBA: // Take TGA->RGBA, RGB->RGBA switch(j) { case 1: //TGA->RGBA Write = MakeName(argv[i], TARGET_SGI); if(Image.LoadTGA(argv[i])) { Image.SaveRGBA(Write); Image.Destroy(); } delete Write; break; case 2: //RGB->RGBA Write = MakeName(argv[i], TARGET_SGI); if(Image.LoadRGB(argv[i])) { Image.SaveRGBA(Write); Image.Destroy(); } delete Write; break; case 3: //RGBA->RGB Write = MakeName(argv[i], TARGET_SGI); if(Image.LoadRGBA(argv[i])) { Image.SaveRGBA(Write); Image.Destroy(); } delete Write; break; } break; } } } while(++i < argc); }