//----------------------------------------------------------------------------- // // ImageLib Sources // Copyright (C) 2000-2009 by Denton Woods // Last modified: 03/07/2009 // // Filename: src-IL/src/il_sgi.c // // Description: Reads and writes Silicon Graphics Inc. (.sgi) files. // //----------------------------------------------------------------------------- #include "il_internal.h" #ifndef IL_NO_SGI #include "il_sgi.h" #include "il_manip.h" #include static char *FName = NULL; /*----------------------------------------------------------------------------*/ /*! Checks if the file specified in FileName is a valid .sgi file. */ ILboolean ilIsValidSgi(ILconst_string FileName) { ILHANDLE SgiFile; ILboolean bSgi = IL_FALSE; if (!iCheckExtension(FileName, IL_TEXT("sgi"))) { ilSetError(IL_INVALID_EXTENSION); return bSgi; } FName = (char*)FileName; SgiFile = iopenr(FileName); if (SgiFile == NULL) { ilSetError(IL_COULD_NOT_OPEN_FILE); return bSgi; } bSgi = ilIsValidSgiF(SgiFile); icloser(SgiFile); return bSgi; } /*----------------------------------------------------------------------------*/ /*! Checks if the ILHANDLE contains a valid .sgi file at the current position.*/ ILboolean ilIsValidSgiF(ILHANDLE File) { ILuint FirstPos; ILboolean bRet; iSetInputFile(File); FirstPos = itell(); bRet = iIsValidSgi(); iseek(FirstPos, IL_SEEK_SET); return bRet; } /*----------------------------------------------------------------------------*/ //! Checks if Lump is a valid .sgi lump. ILboolean ilIsValidSgiL(const void *Lump, ILuint Size) { FName = NULL; iSetInputLump(Lump, Size); return iIsValidSgi(); } /*----------------------------------------------------------------------------*/ // Internal function used to get the .sgi header from the current file. ILboolean iGetSgiHead(iSgiHeader *Header) { Header->MagicNum = GetBigUShort(); Header->Storage = (ILbyte)igetc(); Header->Bpc = (ILbyte)igetc(); Header->Dim = GetBigUShort(); Header->XSize = GetBigUShort(); Header->YSize = GetBigUShort(); Header->ZSize = GetBigUShort(); Header->PixMin = GetBigInt(); Header->PixMax = GetBigInt(); Header->Dummy1 = GetBigInt(); iread(Header->Name, 1, 80); Header->ColMap = GetBigInt(); iread(Header->Dummy, 1, 404); return IL_TRUE; } /*----------------------------------------------------------------------------*/ /* Internal function to get the header and check it. */ ILboolean iIsValidSgi() { iSgiHeader Head; if (!iGetSgiHead(&Head)) return IL_FALSE; iseek(-(ILint)sizeof(iSgiHeader), IL_SEEK_CUR); // Go ahead and restore to previous state return iCheckSgi(&Head); } /*----------------------------------------------------------------------------*/ /* Internal function used to check if the HEADER is a valid .sgi header. */ ILboolean iCheckSgi(iSgiHeader *Header) { if (Header->MagicNum != SGI_MAGICNUM) return IL_FALSE; if (Header->Storage != SGI_RLE && Header->Storage != SGI_VERBATIM) return IL_FALSE; if (Header->Bpc == 0 || Header->Dim == 0) return IL_FALSE; if (Header->XSize == 0 || Header->YSize == 0 || Header->ZSize == 0) return IL_FALSE; return IL_TRUE; } /*----------------------------------------------------------------------------*/ /*! Reads a SGI file */ ILboolean ilLoadSgi(ILconst_string FileName) { ILHANDLE SgiFile; ILboolean bSgi = IL_FALSE; SgiFile = iopenr(FileName); if (SgiFile == NULL) { ilSetError(IL_COULD_NOT_OPEN_FILE); return bSgi; } bSgi = ilLoadSgiF(SgiFile); icloser(SgiFile); return bSgi; } /*----------------------------------------------------------------------------*/ /*! Reads an already-opened SGI file */ ILboolean ilLoadSgiF(ILHANDLE File) { ILuint FirstPos; ILboolean bRet; iSetInputFile(File); FirstPos = itell(); bRet = iLoadSgiInternal(); iseek(FirstPos, IL_SEEK_SET); return bRet; } /*----------------------------------------------------------------------------*/ /*! Reads from a memory "lump" that contains a SGI image */ ILboolean ilLoadSgiL(const void *Lump, ILuint Size) { iSetInputLump(Lump, Size); return iLoadSgiInternal(); } /*----------------------------------------------------------------------------*/ /* Internal function used to load the SGI image */ ILboolean iLoadSgiInternal() { iSgiHeader Header; ILboolean bSgi; if (iCurImage == NULL) { ilSetError(IL_ILLEGAL_OPERATION); return IL_FALSE; } if (!iGetSgiHead(&Header)) return IL_FALSE; if (!iCheckSgi(&Header)) { ilSetError(IL_INVALID_FILE_HEADER); return IL_FALSE; } // Bugfix for #1060946. // The ZSize should never really be 2 by the specifications. Some // application is outputting these, and it looks like the ZSize // should really be 1. if (Header.ZSize == 2) Header.ZSize = 1; if (Header.Storage == SGI_RLE) { // RLE bSgi = iReadRleSgi(&Header); } else { // Non-RLE //(Header.Storage == SGI_VERBATIM) bSgi = iReadNonRleSgi(&Header); } if (!bSgi) return IL_FALSE; return ilFixImage(); } /*----------------------------------------------------------------------------*/ ILboolean iReadRleSgi(iSgiHeader *Head) { #ifdef __LITTLE_ENDIAN__ ILuint ixTable; #endif ILuint ChanInt = 0; ILuint ixPlane, ixHeight,ixPixel, RleOff, RleLen; ILuint *OffTable=NULL, *LenTable=NULL, TableSize, Cur; ILubyte **TempData=NULL; if (!iNewSgi(Head)) return IL_FALSE; TableSize = Head->YSize * Head->ZSize; OffTable = (ILuint*)ialloc(TableSize * sizeof(ILuint)); LenTable = (ILuint*)ialloc(TableSize * sizeof(ILuint)); if (OffTable == NULL || LenTable == NULL) goto cleanup_error; if (iread(OffTable, TableSize * sizeof(ILuint), 1) != 1) goto cleanup_error; if (iread(LenTable, TableSize * sizeof(ILuint), 1) != 1) goto cleanup_error; #ifdef __LITTLE_ENDIAN__ // Fix the offset/len table (it's big endian format) for (ixTable = 0; ixTable < TableSize; ixTable++) { iSwapUInt(OffTable + ixTable); iSwapUInt(LenTable + ixTable); } #endif //__LITTLE_ENDIAN__ // We have to create a temporary buffer for the image, because SGI // images are plane-separated. TempData = (ILubyte**)ialloc(Head->ZSize * sizeof(ILubyte*)); if (TempData == NULL) goto cleanup_error; imemclear(TempData, Head->ZSize * sizeof(ILubyte*)); // Just in case ialloc fails then cleanup_error. for (ixPlane = 0; ixPlane < Head->ZSize; ixPlane++) { TempData[ixPlane] = (ILubyte*)ialloc(Head->XSize * Head->YSize * Head->Bpc); if (TempData[ixPlane] == NULL) goto cleanup_error; } // Read the Planes into the temporary memory for (ixPlane = 0; ixPlane < Head->ZSize; ixPlane++) { for (ixHeight = 0, Cur = 0; ixHeight < Head->YSize; ixHeight++, Cur += Head->XSize * Head->Bpc) { RleOff = OffTable[ixHeight + ixPlane * Head->YSize]; RleLen = LenTable[ixHeight + ixPlane * Head->YSize]; // Seeks to the offset table position iseek(RleOff, IL_SEEK_SET); if (iGetScanLine((TempData[ixPlane]) + (ixHeight * Head->XSize * Head->Bpc), Head, RleLen) != Head->XSize * Head->Bpc) { ilSetError(IL_ILLEGAL_FILE_VALUE); goto cleanup_error; } } } // DW: Removed on 05/25/2002. /*// Check if an alphaplane exists and invert it if (Head->ZSize == 4) { for (ixPixel=0; (ILint)ixPixelXSize * Head->YSize; ixPixel++) { TempData[3][ixPixel] = TempData[3][ixPixel] ^ 255; } }*/ // Assemble the image from its planes for (ixPixel = 0; ixPixel < iCurImage->SizeOfData; ixPixel += Head->ZSize * Head->Bpc, ChanInt += Head->Bpc) { for (ixPlane = 0; (ILint)ixPlane < Head->ZSize * Head->Bpc; ixPlane += Head->Bpc) { iCurImage->Data[ixPixel + ixPlane] = TempData[ixPlane][ChanInt]; if (Head->Bpc == 2) iCurImage->Data[ixPixel + ixPlane + 1] = TempData[ixPlane][ChanInt + 1]; } } #ifdef __LITTLE_ENDIAN__ if (Head->Bpc == 2) sgiSwitchData(iCurImage->Data, iCurImage->SizeOfData); #endif ifree(OffTable); ifree(LenTable); for (ixPlane = 0; ixPlane < Head->ZSize; ixPlane++) { ifree(TempData[ixPlane]); } ifree(TempData); return IL_TRUE; cleanup_error: ifree(OffTable); ifree(LenTable); if (TempData) { for (ixPlane = 0; ixPlane < Head->ZSize; ixPlane++) { ifree(TempData[ixPlane]); } ifree(TempData); } return IL_FALSE; } /*----------------------------------------------------------------------------*/ ILint iGetScanLine(ILubyte *ScanLine, iSgiHeader *Head, ILuint Length) { ILushort Pixel, Count; // For current pixel ILuint BppRead = 0, CurPos = 0, Bps = Head->XSize * Head->Bpc; while (BppRead < Length && CurPos < Bps) { Pixel = 0; if (iread(&Pixel, Head->Bpc, 1) != 1) return -1; #ifndef __LITTLE_ENDIAN__ iSwapUShort(&Pixel); #endif if (!(Count = (Pixel & 0x7f))) // If 0, line ends return CurPos; if (Pixel & 0x80) { // If top bit set, then it is a "run" if (iread(ScanLine, Head->Bpc, Count) != Count) return -1; BppRead += Head->Bpc * Count + Head->Bpc; ScanLine += Head->Bpc * Count; CurPos += Head->Bpc * Count; } else { if (iread(&Pixel, Head->Bpc, 1) != 1) return -1; #ifndef __LITTLE_ENDIAN__ iSwapUShort(&Pixel); #endif if (Head->Bpc == 1) { while (Count--) { *ScanLine = (ILubyte)Pixel; ScanLine++; CurPos++; } } else { while (Count--) { *(ILushort*)ScanLine = Pixel; ScanLine += 2; CurPos += 2; } } BppRead += Head->Bpc + Head->Bpc; } } return CurPos; } /*----------------------------------------------------------------------------*/ // Much easier to read - just assemble from planes, no decompression ILboolean iReadNonRleSgi(iSgiHeader *Head) { ILuint i, c; // ILint ChanInt = 0; Unused ILint ChanSize; ILboolean Cache = IL_FALSE; if (!iNewSgi(Head)) { return IL_FALSE; } if (iGetHint(IL_MEM_SPEED_HINT) == IL_FASTEST) { Cache = IL_TRUE; ChanSize = Head->XSize * Head->YSize * Head->Bpc; iPreCache(ChanSize); } for (c = 0; c < iCurImage->Bpp; c++) { for (i = c; i < iCurImage->SizeOfData; i += iCurImage->Bpp) { if (iread(iCurImage->Data + i, 1, 1) != 1) { if (Cache) iUnCache(); return IL_FALSE; } } } if (Cache) iUnCache(); return IL_TRUE; } /*----------------------------------------------------------------------------*/ void sgiSwitchData(ILubyte *Data, ILuint SizeOfData) { ILubyte Temp; ILuint i; #ifdef ALTIVEC_GCC i = 0; union { vector unsigned char vec; vector unsigned int load; }inversion_vector; inversion_vector.load = (vector unsigned int)\ {0x01000302,0x05040706,0x09080B0A,0x0D0C0F0E}; while( i <= SizeOfData-16 ) { vector unsigned char data = vec_ld(i,Data); vec_perm(data,data,inversion_vector.vec); vec_st(data,i,Data); i+=16; } SizeOfData -= i; #endif for (i = 0; i < SizeOfData; i += 2) { Temp = Data[i]; Data[i] = Data[i+1]; Data[i+1] = Temp; } return; } /*----------------------------------------------------------------------------*/ // Just an internal convenience function for reading SGI files ILboolean iNewSgi(iSgiHeader *Head) { if (!ilTexImage(Head->XSize, Head->YSize, Head->Bpc, (ILubyte)Head->ZSize, 0, IL_UNSIGNED_BYTE, NULL)) { return IL_FALSE; } iCurImage->Origin = IL_ORIGIN_LOWER_LEFT; switch (Head->ZSize) { case 1: iCurImage->Format = IL_LUMINANCE; break; /*case 2: iCurImage->Format = IL_LUMINANCE_ALPHA; break;*/ case 3: iCurImage->Format = IL_RGB; break; case 4: iCurImage->Format = IL_RGBA; break; default: ilSetError(IL_ILLEGAL_FILE_VALUE); return IL_FALSE; } switch (Head->Bpc) { case 1: if (Head->PixMin < 0) iCurImage->Type = IL_BYTE; else iCurImage->Type = IL_UNSIGNED_BYTE; break; case 2: if (Head->PixMin < 0) iCurImage->Type = IL_SHORT; else iCurImage->Type = IL_UNSIGNED_SHORT; break; default: ilSetError(IL_ILLEGAL_FILE_VALUE); return IL_FALSE; } iCurImage->Origin = IL_ORIGIN_LOWER_LEFT; return IL_TRUE; } /*----------------------------------------------------------------------------*/ //! Writes a SGI file ILboolean ilSaveSgi(const ILstring FileName) { ILHANDLE SgiFile; ILuint SgiSize; if (ilGetBoolean(IL_FILE_MODE) == IL_FALSE) { if (iFileExists(FileName)) { ilSetError(IL_FILE_ALREADY_EXISTS); return IL_FALSE; } } SgiFile = iopenw(FileName); if (SgiFile == NULL) { ilSetError(IL_COULD_NOT_OPEN_FILE); return IL_FALSE; } SgiSize = ilSaveSgiF(SgiFile); iclosew(SgiFile); if (SgiSize == 0) return IL_FALSE; return IL_TRUE; } //! Writes a Sgi to an already-opened file ILuint ilSaveSgiF(ILHANDLE File) { ILuint Pos; iSetOutputFile(File); Pos = itellw(); if (iSaveSgiInternal() == IL_FALSE) return 0; // Error occurred return itellw() - Pos; // Return the number of bytes written. } //! Writes a Sgi to a memory "lump" ILuint ilSaveSgiL(void *Lump, ILuint Size) { ILuint Pos; iSetOutputLump(Lump, Size); Pos = itellw(); if (iSaveSgiInternal() == IL_FALSE) return 0; // Error occurred return itellw() - Pos; // Return the number of bytes written. } ILenum DetermineSgiType(ILenum Type) { if (Type > IL_UNSIGNED_SHORT) { if (iCurImage->Type == IL_INT) return IL_SHORT; return IL_UNSIGNED_SHORT; } return Type; } /*----------------------------------------------------------------------------*/ // Rle does NOT work yet. // Internal function used to save the Sgi. ILboolean iSaveSgiInternal() { ILuint i, c; ILboolean Compress; ILimage *Temp = iCurImage; ILubyte *TempData; if (iCurImage == NULL) { ilSetError(IL_ILLEGAL_OPERATION); return IL_FALSE; } if (iCurImage->Format != IL_LUMINANCE //while the sgi spec doesn't directly forbid rgb files with 2 //channels, they are quite uncommon and most apps don't support //them. so convert lum_a images to rgba before writing. //&& iCurImage->Format != IL_LUMINANCE_ALPHA && iCurImage->Format != IL_RGB && iCurImage->Format != IL_RGBA) { if (iCurImage->Format == IL_BGRA || iCurImage->Format == IL_LUMINANCE_ALPHA) Temp = iConvertImage(iCurImage, IL_RGBA, DetermineSgiType(iCurImage->Type)); else Temp = iConvertImage(iCurImage, IL_RGB, DetermineSgiType(iCurImage->Type)); } else if (iCurImage->Type > IL_UNSIGNED_SHORT) { Temp = iConvertImage(iCurImage, iCurImage->Format, DetermineSgiType(iCurImage->Type)); } //compression of images with 2 bytes per channel doesn't work yet Compress = iGetInt(IL_SGI_RLE) && Temp->Bpc == 1; if (Temp == NULL) return IL_FALSE; SaveBigUShort(SGI_MAGICNUM); // 'Magic' number if (Compress) iputc(1); else iputc(0); if (Temp->Type == IL_UNSIGNED_BYTE) iputc(1); else if (Temp->Type == IL_UNSIGNED_SHORT) iputc(2); // Need to error here if not one of the two... if (Temp->Format == IL_LUMINANCE || Temp->Format == IL_COLOUR_INDEX) SaveBigUShort(2); else SaveBigUShort(3); SaveBigUShort((ILushort)Temp->Width); SaveBigUShort((ILushort)Temp->Height); SaveBigUShort((ILushort)Temp->Bpp); switch (Temp->Type) { case IL_BYTE: SaveBigInt(SCHAR_MIN); // Minimum pixel value SaveBigInt(SCHAR_MAX); // Maximum pixel value break; case IL_UNSIGNED_BYTE: SaveBigInt(0); // Minimum pixel value SaveBigInt(UCHAR_MAX); // Maximum pixel value break; case IL_SHORT: SaveBigInt(SHRT_MIN); // Minimum pixel value SaveBigInt(SHRT_MAX); // Maximum pixel value break; case IL_UNSIGNED_SHORT: SaveBigInt(0); // Minimum pixel value SaveBigInt(USHRT_MAX); // Maximum pixel value break; } SaveBigInt(0); // Dummy value if (FName) { c = ilCharStrLen(FName); c = c < 79 ? 79 : c; iwrite(FName, 1, c); c = 80 - c; for (i = 0; i < c; i++) { iputc(0); } } else { for (i = 0; i < 80; i++) { iputc(0); } } SaveBigUInt(0); // Colormap // Padding for (i = 0; i < 101; i++) { SaveLittleInt(0); } if (iCurImage->Origin == IL_ORIGIN_UPPER_LEFT) { TempData = iGetFlipped(Temp); if (TempData == NULL) { if (Temp!= iCurImage) ilCloseImage(Temp); return IL_FALSE; } } else { TempData = Temp->Data; } if (!Compress) { for (c = 0; c < Temp->Bpp; c++) { for (i = c; i < Temp->SizeOfData; i += Temp->Bpp) { iputc(TempData[i]); // Have to save each colour plane separately. } } } else { iSaveRleSgi(TempData, Temp->Width, Temp->Height, Temp->Bpp, Temp->Bps); } if (TempData != Temp->Data) ifree(TempData); if (Temp != iCurImage) ilCloseImage(Temp); return IL_TRUE; } /*----------------------------------------------------------------------------*/ ILboolean iSaveRleSgi(ILubyte *Data, ILuint w, ILuint h, ILuint numChannels, ILuint bps) { //works only for sgi files with only 1 bpc ILuint c, i, y, j; ILubyte *ScanLine = NULL, *CompLine = NULL; ILuint *StartTable = NULL, *LenTable = NULL; ILuint TableOff, DataOff = 0; ScanLine = (ILubyte*)ialloc(w); CompLine = (ILubyte*)ialloc(w * 2 + 1); // Absolute worst case. StartTable = (ILuint*)ialloc(h * numChannels * sizeof(ILuint)); LenTable = (ILuint*)icalloc(h * numChannels, sizeof(ILuint)); if (!ScanLine || !CompLine || !StartTable || !LenTable) { ifree(ScanLine); ifree(CompLine); ifree(StartTable); ifree(LenTable); return IL_FALSE; } // These just contain dummy values at this point. TableOff = itellw(); iwrite(StartTable, sizeof(ILuint), h * numChannels); iwrite(LenTable, sizeof(ILuint), h * numChannels); DataOff = itellw(); for (c = 0; c < numChannels; c++) { for (y = 0; y < h; y++) { i = y * bps + c; for (j = 0; j < w; j++, i += numChannels) { ScanLine[j] = Data[i]; } ilRleCompressLine(ScanLine, w, 1, CompLine, LenTable + h * c + y, IL_SGICOMP); iwrite(CompLine, 1, *(LenTable + h * c + y)); } } iseekw(TableOff, IL_SEEK_SET); j = h * numChannels; for (y = 0; y < j; y++) { StartTable[y] = DataOff; DataOff += LenTable[y]; #ifdef __LITTLE_ENDIAN__ iSwapUInt(&StartTable[y]); iSwapUInt(&LenTable[y]); #endif } iwrite(StartTable, sizeof(ILuint), h * numChannels); iwrite(LenTable, sizeof(ILuint), h * numChannels); ifree(ScanLine); ifree(CompLine); ifree(StartTable); ifree(LenTable); return IL_TRUE; } /*----------------------------------------------------------------------------*/ #endif//IL_NO_SGI