//----------------------------------------------------------------------------- // // ImageLib Sources // Copyright (C) 2000-2002 by Denton Woods // Last modified: 05/22/2002 <--Y2K Compliant! =] // // Filename: src-IL/src/il_targa.c // // Description: Reads from and writes to a targa (.tga) file. // //----------------------------------------------------------------------------- #include "il_internal.h" #ifndef IL_NO_TGA #include "il_targa.h" //#include // for ilMakeString() #include #include "il_manip.h" #include "il_bits.h" #ifdef DJGPP #include #endif //! Checks if the file specified in FileName is a valid Targa file. ILboolean ilIsValidTga(ILconst_string FileName) { ILHANDLE TargaFile; ILboolean bTarga = IL_FALSE; if (!iCheckExtension(FileName, IL_TEXT("tga")) && !iCheckExtension(FileName, IL_TEXT("vda")) && !iCheckExtension(FileName, IL_TEXT("icb")) && !iCheckExtension(FileName, IL_TEXT("vst"))) { ilSetError(IL_INVALID_EXTENSION); return bTarga; } TargaFile = iopenr(FileName); if (TargaFile == NULL) { ilSetError(IL_COULD_NOT_OPEN_FILE); return bTarga; } bTarga = ilIsValidTgaF(TargaFile); icloser(TargaFile); return bTarga; } //! Checks if the ILHANDLE contains a valid Targa file at the current position. ILboolean ilIsValidTgaF(ILHANDLE File) { ILuint FirstPos; ILboolean bRet; iSetInputFile(File); FirstPos = itell(); bRet = iIsValidTarga(); iseek(FirstPos, IL_SEEK_SET); return bRet; } //! Checks if Lump is a valid Targa lump. ILboolean ilIsValidTgaL(const ILvoid *Lump, ILuint Size) { iSetInputLump(Lump, Size); return iIsValidTarga(); } // Internal function used to get the Targa header from the current file. ILboolean iGetTgaHead(TARGAHEAD *Header) { Header->IDLen = igetc(); Header->ColMapPresent = igetc(); Header->ImageType = igetc(); Header->FirstEntry = GetLittleShort(); Header->ColMapLen = GetLittleShort(); Header->ColMapEntSize = igetc(); Header->OriginX = GetLittleShort(); Header->OriginY = GetLittleShort(); Header->Width = GetLittleUShort(); Header->Height = GetLittleUShort(); Header->Bpp = igetc(); Header->ImageDesc = igetc(); return IL_TRUE; } // Internal function to get the header and check it. ILboolean iIsValidTarga() { TARGAHEAD Head; if (!iGetTgaHead(&Head)) return IL_FALSE; iseek(-(ILint)sizeof(TARGAHEAD), IL_SEEK_CUR); return iCheckTarga(&Head); } // Internal function used to check if the HEADER is a valid Targa header. ILboolean iCheckTarga(TARGAHEAD *Header) { if (Header->Width == 0 || Header->Height == 0) return IL_FALSE; if (Header->Bpp != 8 && Header->Bpp != 15 && Header->Bpp != 16 && Header->Bpp != 24 && Header->Bpp != 32) return IL_FALSE; if (Header->ImageDesc & BIT_4) // Supposed to be set to 0 return IL_FALSE; // check type (added 20040218) if(Header->ImageType != TGA_NO_DATA && Header->ImageType != TGA_COLMAP_UNCOMP && Header->ImageType != TGA_UNMAP_UNCOMP && Header->ImageType != TGA_BW_UNCOMP && Header->ImageType != TGA_COLMAP_COMP && Header->ImageType != TGA_UNMAP_COMP && Header->ImageType != TGA_BW_COMP) return IL_FALSE; // Doesn't work well with the bitshift so change it. if (Header->Bpp == 15) Header->Bpp = 16; return IL_TRUE; } //! Reads a Targa file ILboolean ilLoadTarga(ILconst_string FileName) { ILHANDLE TargaFile; ILboolean bTarga = IL_FALSE; TargaFile = iopenr(FileName); if (TargaFile == NULL) { ilSetError(IL_COULD_NOT_OPEN_FILE); return bTarga; } bTarga = ilLoadTargaF(TargaFile); icloser(TargaFile); return bTarga; } //! Reads an already-opened Targa file ILboolean ilLoadTargaF(ILHANDLE File) { ILuint FirstPos; ILboolean bRet; iSetInputFile(File); FirstPos = itell(); bRet = iLoadTargaInternal(); iseek(FirstPos, IL_SEEK_SET); return bRet; } //! Reads from a memory "lump" that contains a Targa ILboolean ilLoadTargaL(const ILvoid *Lump, ILuint Size) { iSetInputLump(Lump, Size); return iLoadTargaInternal(); } // Internal function used to load the Targa. ILboolean iLoadTargaInternal() { TARGAHEAD Header; ILboolean bTarga; ILenum iOrigin; if (iCurImage == NULL) { ilSetError(IL_ILLEGAL_OPERATION); return IL_FALSE; } if (!iGetTgaHead(&Header)) return IL_FALSE; if (!iCheckTarga(&Header)) { ilSetError(IL_INVALID_FILE_HEADER); return IL_FALSE; } switch (Header.ImageType) { case TGA_NO_DATA: ilSetError(IL_ILLEGAL_FILE_VALUE); return IL_FALSE; case TGA_COLMAP_UNCOMP: case TGA_COLMAP_COMP: bTarga = iReadColMapTga(&Header); break; case TGA_UNMAP_UNCOMP: case TGA_UNMAP_COMP: bTarga = iReadUnmapTga(&Header); break; case TGA_BW_UNCOMP: case TGA_BW_COMP: bTarga = iReadBwTga(&Header); break; default: ilSetError(IL_ILLEGAL_FILE_VALUE); return IL_FALSE; } // @JASON Extra Code to manipulate the image depending on // the Image Descriptor's origin bits. iOrigin = Header.ImageDesc & IMAGEDESC_ORIGIN_MASK; switch (iOrigin) { case IMAGEDESC_TOPLEFT: iCurImage->Origin = IL_ORIGIN_UPPER_LEFT; break; case IMAGEDESC_TOPRIGHT: iCurImage->Origin = IL_ORIGIN_UPPER_LEFT; iMirror(); break; case IMAGEDESC_BOTLEFT: iCurImage->Origin = IL_ORIGIN_LOWER_LEFT; break; case IMAGEDESC_BOTRIGHT: iCurImage->Origin = IL_ORIGIN_LOWER_LEFT; iMirror(); break; } ilFixImage(); return bTarga; } ILboolean iReadColMapTga(TARGAHEAD *Header) { char ID[255]; ILuint i; ILushort Pixel; if (iread(ID, 1, Header->IDLen) != Header->IDLen) return IL_FALSE; if (!ilTexImage(Header->Width, Header->Height, 1, (ILubyte)(Header->Bpp >> 3), 0, IL_UNSIGNED_BYTE, NULL)) { return IL_FALSE; } if (iCurImage->Pal.Palette && iCurImage->Pal.PalSize) ifree(iCurImage->Pal.Palette); iCurImage->Format = IL_COLOUR_INDEX; iCurImage->Pal.PalSize = Header->ColMapLen * (Header->ColMapEntSize >> 3); switch (Header->ColMapEntSize) { case 16: iCurImage->Pal.PalType = IL_PAL_BGRA32; iCurImage->Pal.PalSize = Header->ColMapLen * 4; break; case 24: iCurImage->Pal.PalType = IL_PAL_BGR24; break; case 32: iCurImage->Pal.PalType = IL_PAL_BGRA32; break; default: // Should *never* reach here ilSetError(IL_ILLEGAL_FILE_VALUE); return IL_FALSE; } iCurImage->Pal.Palette = (ILubyte*)ialloc(iCurImage->Pal.PalSize); if (iCurImage->Pal.Palette == NULL) { return IL_FALSE; } // Do we need to do something with FirstEntry? Like maybe: // iread(Image->Pal + Targa->FirstEntry, 1, Image->Pal.PalSize); ?? if (Header->ColMapEntSize != 16) { if (iread(iCurImage->Pal.Palette, 1, iCurImage->Pal.PalSize) != iCurImage->Pal.PalSize) return IL_FALSE; } else { // 16 bit palette, so we have to break it up. for (i = 0; i < iCurImage->Pal.PalSize; i += 4) { Pixel = GetBigUShort(); if (ieof()) return IL_FALSE; iCurImage->Pal.Palette[3] = (Pixel & 0x8000) >> 12; iCurImage->Pal.Palette[0] = (Pixel & 0xFC00) >> 7; iCurImage->Pal.Palette[1] = (Pixel & 0x03E0) >> 2; iCurImage->Pal.Palette[2] = (Pixel & 0x001F) << 3; } } if (Header->ImageType == TGA_COLMAP_COMP) { if (!iUncompressTgaData(iCurImage)) { return IL_FALSE; } } else { if (iread(iCurImage->Data, 1, iCurImage->SizeOfData) != iCurImage->SizeOfData) { return IL_FALSE; } } return IL_TRUE; } ILboolean iReadUnmapTga(TARGAHEAD *Header) { ILubyte Bpp; char ID[255]; if (iread(ID, 1, Header->IDLen) != Header->IDLen) return IL_FALSE; /*if (Header->Bpp == 16) Bpp = 3; else*/ Bpp = (ILubyte)(Header->Bpp >> 3); if (!ilTexImage(Header->Width, Header->Height, 1, Bpp, 0, IL_UNSIGNED_BYTE, NULL)) { return IL_FALSE; } switch (iCurImage->Bpp) { case 1: iCurImage->Format = IL_COLOUR_INDEX; // wtf? How is this possible? break; case 2: // 16-bit is not supported directly! //iCurImage->Format = IL_RGB5_A1; /*iCurImage->Format = IL_RGBA; iCurImage->Type = IL_UNSIGNED_SHORT_5_5_5_1_EXT;*/ //iCurImage->Type = IL_UNSIGNED_SHORT_5_6_5_REV; // Remove? //ilCloseImage(iCurImage); //ilSetError(IL_FORMAT_NOT_SUPPORTED); //return IL_FALSE; /*iCurImage->Bpp = 4; iCurImage->Format = IL_BGRA; iCurImage->Type = IL_UNSIGNED_SHORT_1_5_5_5_REV;*/ iCurImage->Format = IL_BGR; break; case 3: iCurImage->Format = IL_BGR; break; case 4: iCurImage->Format = IL_BGRA; break; default: ilSetError(IL_INVALID_VALUE); return IL_FALSE; } // @TODO: Determine this: // We assume that no palette is present, but it's possible... // Should we mess with it or not? if (Header->ImageType == TGA_UNMAP_COMP) { if (!iUncompressTgaData(iCurImage)) { return IL_FALSE; } } else { if (iread(iCurImage->Data, 1, iCurImage->SizeOfData) != iCurImage->SizeOfData) { return IL_FALSE; } } // Go ahead and expand it to 24-bit. if (Header->Bpp == 16) { if (!i16BitTarga(iCurImage)) return IL_FALSE; return IL_TRUE; } return IL_TRUE; } ILboolean iReadBwTga(TARGAHEAD *Header) { char ID[255]; if (iread(ID, 1, Header->IDLen) != Header->IDLen) return IL_FALSE; // We assume that no palette is present, but it's possible... // Should we mess with it or not? if (!ilTexImage(Header->Width, Header->Height, 1, (ILubyte)(Header->Bpp >> 3), IL_LUMINANCE, IL_UNSIGNED_BYTE, NULL)) { return IL_FALSE; } if (Header->ImageType == TGA_BW_COMP) { if (!iUncompressTgaData(iCurImage)) { return IL_FALSE; } } else { if (iread(iCurImage->Data, 1, iCurImage->SizeOfData) != iCurImage->SizeOfData) { return IL_FALSE; } } return IL_TRUE; } ILboolean iUncompressTgaData(ILimage *Image) { ILuint BytesRead = 0, Size, RunLen, i; ILubyte Header, Color[4]; ILint c; Size = Image->Width * Image->Height * Image->Depth * Image->Bpp; if (iGetHint(IL_MEM_SPEED_HINT) == IL_FASTEST) iPreCache(iCurImage->SizeOfData / 2); while (BytesRead < Size) { Header = igetc(); if (Header & BIT_7) { ClearBits(Header, BIT_7); if (iread(Color, 1, Image->Bpp) != Image->Bpp) { iUnCache(); return IL_FALSE; } RunLen = (Header+1) * Image->Bpp; for (i = 0; i < RunLen; i += Image->Bpp) { for (c = 0; c < Image->Bpp; c++) { Image->Data[BytesRead+i+c] = Color[c]; } } BytesRead += RunLen; } else { RunLen = (Header+1) * Image->Bpp; if (iread(Image->Data + BytesRead, 1, RunLen) != RunLen) { iUnCache(); return IL_FALSE; } BytesRead += RunLen; } } iUnCache(); return IL_TRUE; } // Pretty damn unoptimized ILboolean i16BitTarga(ILimage *Image) { ILushort *Temp1; ILubyte *Data, *Temp2; ILuint x, PixSize = Image->Width * Image->Height; Data = (ILubyte*)ialloc(Image->Width * Image->Height * 3); Temp1 = (ILushort*)Image->Data; Temp2 = Data; if (Data == NULL) return IL_FALSE; for (x = 0; x < PixSize; x++) { *Temp2++ = (*Temp1 & 0x001F) << 3; // Blue *Temp2++ = (*Temp1 & 0x03E0) >> 2; // Green *Temp2++ = (*Temp1 & 0x7C00) >> 7; // Red Temp1++; /*s = *Temp; s = SwapShort(s); a = !!(s & BIT_15); s = s << 1; //if (a) { SetBits(s, BIT_0); //} //SetBits(s, BIT_15); *Temp++ = s;*/ } if (!ilTexImage(Image->Width, Image->Height, 1, 3, IL_BGR, IL_UNSIGNED_BYTE, Data)) { ifree(Data); return IL_FALSE; } ifree(Data); return IL_TRUE; } //! Writes a Targa file ILboolean ilSaveTarga(ILconst_string FileName) { ILHANDLE TargaFile; ILboolean bTarga = IL_FALSE; if (ilGetBoolean(IL_FILE_MODE) == IL_FALSE) { if (iFileExists(FileName)) { ilSetError(IL_FILE_ALREADY_EXISTS); return IL_FALSE; } } TargaFile = iopenw(FileName); if (TargaFile == NULL) { ilSetError(IL_COULD_NOT_OPEN_FILE); return bTarga; } bTarga = ilSaveTargaF(TargaFile); iclosew(TargaFile); return bTarga; } //! Writes a Targa to an already-opened file ILboolean ilSaveTargaF(ILHANDLE File) { iSetOutputFile(File); return iSaveTargaInternal(); } //! Writes a Targa to a memory "lump" ILboolean ilSaveTargaL(ILvoid *Lump, ILuint Size) { iSetOutputLump(Lump, Size); return iSaveTargaInternal(); } // Internal function used to save the Targa. ILboolean iSaveTargaInternal() { char *ID = iGetString(IL_TGA_ID_STRING); char *AuthName = iGetString(IL_TGA_AUTHNAME_STRING); char *AuthComment = iGetString(IL_TGA_AUTHCOMMENT_STRING); ILubyte IDLen = 0, UsePal, Type, PalEntSize; ILshort ColMapStart = 0, PalSize; ILubyte Temp; ILenum Format; ILboolean Compress; ILuint RleLen; ILubyte *Rle; ILpal *TempPal = NULL; ILimage *TempImage = NULL; ILuint ExtOffset, i; char *Footer = "TRUEVISION-XFILE.\0"; char *idString = "Developer's Image Library (DevIL)"; ILuint Day, Month, Year, Hour, Minute, Second; char *TempData; if (iCurImage == NULL) { ilSetError(IL_ILLEGAL_OPERATION); return IL_FALSE; } if (iGetInt(IL_TGA_RLE) == IL_TRUE) Compress = IL_TRUE; else Compress = IL_FALSE; if (ID) IDLen = (ILubyte)strlen(ID); if (iCurImage->Pal.Palette && iCurImage->Pal.PalSize && iCurImage->Pal.PalType != IL_PAL_NONE) UsePal = IL_TRUE; else UsePal = IL_FALSE; iwrite(&IDLen, sizeof(ILubyte), 1); iwrite(&UsePal, sizeof(ILubyte), 1); Format = iCurImage->Format; switch (Format) { case IL_COLOUR_INDEX: if (Compress) Type = 9; else Type = 1; break; case IL_BGR: case IL_BGRA: if (Compress) Type = 10; else Type = 2; break; case IL_RGB: case IL_RGBA: ilSwapColours(); if (Compress) Type = 10; else Type = 2; break; case IL_LUMINANCE: if (Compress) Type = 11; else Type = 3; break; default: // Should convert the types here... ilSetError(IL_INVALID_VALUE); ifree(ID); ifree(AuthName); ifree(AuthComment); return IL_FALSE; } iwrite(&Type, sizeof(ILubyte), 1); SaveLittleShort(ColMapStart); switch (iCurImage->Pal.PalType) { case IL_PAL_NONE: PalSize = 0; PalEntSize = 0; break; case IL_PAL_BGR24: PalSize = iCurImage->Pal.PalSize / 3; PalEntSize = 24; TempPal = &iCurImage->Pal; break; case IL_PAL_RGB24: case IL_PAL_RGB32: case IL_PAL_RGBA32: case IL_PAL_BGR32: case IL_PAL_BGRA32: TempPal = iConvertPal(&iCurImage->Pal, IL_PAL_BGR24); if (TempPal == NULL) return IL_FALSE; PalSize = TempPal->PalSize / 3; PalEntSize = 24; break; default: ilSetError(IL_INVALID_VALUE); ifree(ID); ifree(AuthName); ifree(AuthComment); PalSize = 0; PalEntSize = 0; return IL_FALSE; } SaveLittleShort(PalSize); iwrite(&PalEntSize, sizeof(ILubyte), 1); if (iCurImage->Bpc > 1) { TempImage = iConvertImage(iCurImage, iCurImage->Format, IL_UNSIGNED_BYTE); if (TempImage == NULL) { ifree(ID); ifree(AuthName); ifree(AuthComment); return IL_FALSE; } } else { TempImage = iCurImage; } if (TempImage->Origin != IL_ORIGIN_LOWER_LEFT) TempData = (char*)iGetFlipped(TempImage); else TempData = (char*)TempImage->Data; // Write out the origin stuff. Temp = 0; iwrite(&Temp, sizeof(ILshort), 1); iwrite(&Temp, sizeof(ILshort), 1); Temp = iCurImage->Bpp << 3; // Changes to bits per pixel SaveLittleUShort((ILushort)iCurImage->Width); SaveLittleUShort((ILushort)iCurImage->Height); iwrite(&Temp, sizeof(ILubyte), 1); // Still don't know what exactly this is for... Temp = 0; iwrite(&Temp, sizeof(ILubyte), 1); iwrite(ID, sizeof(char), IDLen); ifree(ID); //iwrite(ID, sizeof(ILbyte), IDLen - sizeof(ILuint)); //iwrite(&iCurImage->Depth, sizeof(ILuint), 1); // Write out the colormap if (UsePal) iwrite(TempPal->Palette, sizeof(ILubyte), TempPal->PalSize); // else do nothing if (!Compress) iwrite(TempData, sizeof(ILubyte), TempImage->SizeOfData); else { Rle = (ILubyte*)ialloc(TempImage->SizeOfData + TempImage->SizeOfData / 2 + 1); // max if (Rle == NULL) { ifree(AuthName); ifree(AuthComment); return IL_FALSE; } RleLen = ilRleCompress((unsigned char*)TempData, TempImage->Width, TempImage->Height, TempImage->Depth, TempImage->Bpp, Rle, IL_TGACOMP, NULL); iwrite(Rle, 1, RleLen); ifree(Rle); } // Write the extension area. ExtOffset = itellw(); SaveLittleUShort(495); // Number of bytes in the extension area (TGA 2.0 spec) iwrite(AuthName, 1, ilStrLen(AuthName)); ipad(41 - ilStrLen(AuthName)); iwrite(AuthComment, 1, ilStrLen(AuthComment)); ipad(324 - ilStrLen(AuthComment)); ifree(AuthName); ifree(AuthComment); // Write time/date iGetDateTime(&Month, &Day, &Year, &Hour, &Minute, &Second); SaveLittleUShort((ILushort)Month); SaveLittleUShort((ILushort)Day); SaveLittleUShort((ILushort)Year); SaveLittleUShort((ILushort)Hour); SaveLittleUShort((ILushort)Minute); SaveLittleUShort((ILushort)Second); for (i = 0; i < 6; i++) { // Time created SaveLittleUShort(0); } for (i = 0; i < 41; i++) { // Job name/ID iputc(0); } for (i = 0; i < 3; i++) { // Job time SaveLittleUShort(0); } iwrite(idString, 1, strlen(idString)); // Software ID for (i = 0; i < 41 - strlen(idString); i++) { iputc(0); } SaveLittleUShort(IL_VERSION); // Software version iputc(' '); // Release letter (not beta anymore, so use a space) SaveLittleUInt(0); // Key colour SaveLittleUInt(0); // Pixel aspect ratio SaveLittleUInt(0); // Gamma correction offset SaveLittleUInt(0); // Colour correction offset SaveLittleUInt(0); // Postage stamp offset SaveLittleUInt(0); // Scan line offset iputc(3); // Attributes type // Write the footer. SaveLittleUInt(ExtOffset); // No extension area SaveLittleUInt(0); // No developer directory iwrite(Footer, 1, strlen(Footer)); if (TempImage->Origin != IL_ORIGIN_LOWER_LEFT) { ifree(TempData); } if (Format == IL_RGB || Format == IL_RGBA) { ilSwapColours(); } if (TempPal != &iCurImage->Pal && TempPal != NULL) { ifree(TempPal->Palette); ifree(TempPal); } if (TempImage != iCurImage) ilCloseImage(TempImage); return IL_TRUE; } /*// Makes a neat string to go into the id field of the .tga ILvoid iMakeString(char *Str) { char *PSG = "Generated by Developer's Image Library: "; char TimeStr[255]; time_t Time; struct tm *CurTime; time(&Time); #ifdef _WIN32 _tzset(); #endif CurTime = localtime(&Time); strftime(TimeStr, 255 - strlen(PSG), "%#c (%z)", CurTime); //strftime(TimeStr, 255 - strlen(PSG), "%C (%Z)", CurTime); sprintf(Str, "%s%s", PSG, TimeStr); return; }*/ //changed name to iGetDateTime on 20031221 to fix bug 830196 ILvoid iGetDateTime(ILuint *Month, ILuint *Day, ILuint *Yr, ILuint *Hr, ILuint *Min, ILuint *Sec) { #ifdef DJGPP struct date day; struct time curtime; gettime(&curtime); getdate(&day); *Month = day.da_mon; *Day = day.da_day; *Yr = day.da_year; *Hr = curtime.ti_hour; *Min = curtime.ti_min; *Sec = curtime.ti_sec; return; #else #ifdef _WIN32 SYSTEMTIME Time; GetSystemTime(&Time); *Month = Time.wMonth; *Day = Time.wDay; *Yr = Time.wYear; *Hr = Time.wHour; *Min = Time.wMinute; *Sec = Time.wSecond; return; #else *Month = 0; *Day = 0; *Yr = 0; *Hr = 0; *Min = 0; *Sec = 0; return; #endif #endif } #endif//IL_NO_TGA