summaryrefslogtreecommitdiff
path: root/DevIL/src-IL/src/il_fits.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'DevIL/src-IL/src/il_fits.cpp')
-rw-r--r--DevIL/src-IL/src/il_fits.cpp472
1 files changed, 472 insertions, 0 deletions
diff --git a/DevIL/src-IL/src/il_fits.cpp b/DevIL/src-IL/src/il_fits.cpp
new file mode 100644
index 00000000..bd3c04a2
--- /dev/null
+++ b/DevIL/src-IL/src/il_fits.cpp
@@ -0,0 +1,472 @@
+//-----------------------------------------------------------------------------
+//
+// ImageLib Sources
+// Copyright (C) 2000-2009 by Denton Woods
+// Last modified: 02/14/2009
+//
+// Filename: src-IL/src/il_fits.c
+//
+// Description: Reads from a Flexible Image Transport System (.fits) file.
+// Specifications were found at
+// http://www.fileformat.info/format/fits.
+//
+//-----------------------------------------------------------------------------
+
+
+#include "il_internal.h"
+#ifndef IL_NO_FITS
+
+typedef struct FITSHEAD
+{
+ ILboolean IsSimple;
+ ILint BitsPixel;
+ ILint NumAxes; // Number of dimensions / axes
+ ILint Width;
+ ILint Height;
+ ILint Depth;
+ ILint NumChans;
+
+ // Not in the header, but it keeps everything together.
+ ILenum Type;
+ ILenum Format;
+
+} FITSHEAD;
+
+enum {
+ CARD_READ_FAIL = -1,
+ CARD_END = 1,
+ CARD_SIMPLE,
+ CARD_NOT_SIMPLE,
+ CARD_BITPIX,
+ CARD_NUMAXES,
+ CARD_AXIS,
+ CARD_SKIP
+};
+
+ILboolean iIsValidFits(void);
+ILboolean iCheckFits(FITSHEAD *Header);
+ILboolean iLoadFitsInternal(void);
+ILenum GetCardImage(FITSHEAD *Header);
+ILboolean GetCardInt(char *Buffer, ILint *Val);
+
+//! Checks if the file specified in FileName is a valid FITS file.
+ILboolean ilIsValidFits(ILconst_string FileName)
+{
+ ILHANDLE FitsFile;
+ ILboolean bFits = IL_FALSE;
+
+ if (!iCheckExtension(FileName, IL_TEXT("fits")) && !iCheckExtension(FileName, IL_TEXT("fit"))) {
+ ilSetError(IL_INVALID_EXTENSION);
+ return bFits;
+ }
+
+ FitsFile = iopenr(FileName);
+ if (FitsFile == NULL) {
+ ilSetError(IL_COULD_NOT_OPEN_FILE);
+ return bFits;
+ }
+
+ bFits = ilIsValidFitsF(FitsFile);
+ icloser(FitsFile);
+
+ return bFits;
+}
+
+
+//! Checks if the ILHANDLE contains a valid FITS file at the current position.
+ILboolean ilIsValidFitsF(ILHANDLE File)
+{
+ ILuint FirstPos;
+ ILboolean bRet;
+
+ iSetInputFile(File);
+ FirstPos = itell();
+ bRet = iIsValidFits();
+ iseek(FirstPos, IL_SEEK_SET);
+
+ return bRet;
+}
+
+
+//! Checks if Lump is a valid FITS lump.
+ILboolean ilIsValidFitsL(const void *Lump, ILuint Size)
+{
+ iSetInputLump(Lump, Size);
+ return iIsValidFits();
+}
+
+
+// Internal function used to get the FITS header from the current file.
+ILboolean iGetFitsHead(FITSHEAD *Header)
+{
+ ILenum CardKey;
+
+//@TODO: Use something other than memset?
+ memset(Header, 0, sizeof(Header)); // Clear the header to all 0s first.
+
+ do {
+ CardKey = GetCardImage(Header);
+ if (CardKey == CARD_END) // End of the header
+ break;
+ if (CardKey == CARD_READ_FAIL)
+ return IL_FALSE;
+ if (CardKey == CARD_NOT_SIMPLE)
+ return IL_FALSE;
+ } while (!ieof());
+
+ // The header should never reach the end of the file.
+ if (ieof())
+ return IL_FALSE; // Error needed?
+
+ // The header must always be a multiple of 2880, so we skip the padding bytes (spaces).
+ iseek((2880 - (itell() % 2880)) % 2880, IL_SEEK_CUR);
+
+ switch (Header->BitsPixel)
+ {
+ case 8:
+ Header->Type = IL_UNSIGNED_BYTE;
+ break;
+ case 16:
+ Header->Type = IL_SHORT;
+ break;
+ case 32:
+ Header->Type = IL_INT;
+ break;
+ case -32:
+ Header->Type = IL_FLOAT;
+ break;
+ case -64:
+ Header->Type = IL_DOUBLE;
+ break;
+ default:
+ ilSetError(IL_INVALID_FILE_HEADER);
+ return IL_FALSE;
+ }
+
+ switch (Header->NumAxes)
+ {
+ case 1: // Just a 1D image
+ Header->Format = IL_LUMINANCE;
+ Header->Height = 1;
+ Header->Depth = 1;
+ Header->NumChans = 1;
+ break;
+
+ case 2: // Assuming it is a 2D image (width+height)
+ Header->Format = IL_LUMINANCE;
+ Header->Depth = 1;
+ Header->NumChans = 1;
+ break;
+
+ case 3:
+ // We cannot deal with more than 3 channels in an image.
+ Header->Format = IL_LUMINANCE;
+ Header->NumChans = 1;
+ break;
+
+ default:
+ ilSetError(IL_INVALID_FILE_HEADER);
+ return IL_FALSE;
+ }
+
+ return IL_TRUE;
+}
+
+
+// Internal function to get the header and check it.
+ILboolean iIsValidFits(void)
+{
+ FITSHEAD Header;
+ ILuint Pos = itell();
+
+ if (!iGetFitsHead(&Header))
+ return IL_FALSE;
+ // The length of the header varies, so we just go back to the original position.
+ iseek(Pos, IL_SEEK_CUR);
+
+ return iCheckFits(&Header);
+}
+
+
+// Internal function used to check if the HEADER is a valid FITS header.
+ILboolean iCheckFits(FITSHEAD *Header)
+{
+ switch (Header->BitsPixel)
+ {
+ case 8: // These are the only values accepted.
+ case 16:
+ case 32:
+ case -32:
+ case -64:
+ break;
+ default:
+ return IL_FALSE;
+ }
+
+ switch (Header->NumAxes)
+ {
+ case 1: // Just a 1D image
+ case 2: // Assuming it is a 2D image (width+height)
+ case 3: // 3D image (with depth)
+ break;
+ default:
+ return IL_FALSE;
+ }
+
+ // Possibility that one of these values is returned as <= 0 by atoi, which we cannot use.
+ if (Header->Width <= 0 || Header->Height <= 0 || Header->Depth <= 0) {
+ ilSetError(IL_INVALID_FILE_HEADER);
+ return IL_FALSE;
+ }
+
+ return IL_TRUE;
+}
+
+
+//! Reads a FITS file
+ILboolean ilLoadFits(ILconst_string FileName)
+{
+ ILHANDLE FitsFile;
+ ILboolean bFits = IL_FALSE;
+
+ FitsFile = iopenr(FileName);
+ if (FitsFile == NULL) {
+ ilSetError(IL_COULD_NOT_OPEN_FILE);
+ return bFits;
+ }
+
+ bFits = ilLoadFitsF(FitsFile);
+ icloser(FitsFile);
+
+ return bFits;
+}
+
+
+//! Reads an already-opened FITS file
+ILboolean ilLoadFitsF(ILHANDLE File)
+{
+ ILuint FirstPos;
+ ILboolean bRet;
+
+ iSetInputFile(File);
+ FirstPos = itell();
+ bRet = iLoadFitsInternal();
+ iseek(FirstPos, IL_SEEK_SET);
+
+ return bRet;
+}
+
+
+//! Reads from a memory "lump" that contains a FITS
+ILboolean ilLoadFitsL(const void *Lump, ILuint Size)
+{
+ iSetInputLump(Lump, Size);
+ return iLoadFitsInternal();
+}
+
+
+// Internal function used to load the FITS.
+ILboolean iLoadFitsInternal(void)
+{
+ FITSHEAD Header;
+ ILuint i, NumPix;
+ ILfloat MaxF = 0.0f;
+ ILdouble MaxD = 0.0f;
+
+ if (iCurImage == NULL) {
+ ilSetError(IL_ILLEGAL_OPERATION);
+ return IL_FALSE;
+ }
+
+ if (!iGetFitsHead(&Header))
+ return IL_FALSE;
+ if (!iCheckFits(&Header))
+ return IL_FALSE;
+
+ if (!ilTexImage(Header.Width, Header.Height, Header.Depth, Header.NumChans, Header.Format, Header.Type, NULL))
+ return IL_FALSE;
+
+ /*if (iread(iCurImage->Data, 1, iCurImage->SizeOfData) != iCurImage->SizeOfData)
+ return IL_FALSE;*/
+
+ NumPix = Header.Width * Header.Height * Header.Depth;
+//@TODO: Do some checks while reading to see if we have hit the end of the file.
+ switch (Header.Type)
+ {
+ case IL_UNSIGNED_BYTE:
+ if (iread(iCurImage->Data, 1, iCurImage->SizeOfData) != iCurImage->SizeOfData)
+ return IL_FALSE;
+ break;
+ case IL_SHORT:
+ for (i = 0; i < NumPix; i++) {
+ ((ILshort*)iCurImage->Data)[i] = GetBigShort();
+ }
+ break;
+ case IL_INT:
+ for (i = 0; i < NumPix; i++) {
+ ((ILint*)iCurImage->Data)[i] = GetBigInt();
+ }
+ break;
+ case IL_FLOAT:
+ for (i = 0; i < NumPix; i++) {
+ ((ILfloat*)iCurImage->Data)[i] = GetBigFloat();
+ if (((ILfloat*)iCurImage->Data)[i] > MaxF)
+ MaxF = ((ILfloat*)iCurImage->Data)[i];
+ }
+
+ // Renormalize to [0..1].
+ for (i = 0; i < NumPix; i++) {
+ // Change all negative numbers to 0.
+ if (((ILfloat*)iCurImage->Data)[i] < 0.0f)
+ ((ILfloat*)iCurImage->Data)[i] = 0.0f;
+ // Do the renormalization now, dividing by the maximum value.
+ ((ILfloat*)iCurImage->Data)[i] = ((ILfloat*)iCurImage->Data)[i] / MaxF;
+ }
+ break;
+ case IL_DOUBLE:
+ for (i = 0; i < NumPix; i++) {
+ ((ILdouble*)iCurImage->Data)[i] = GetBigDouble();
+ if (((ILdouble*)iCurImage->Data)[i] > MaxD)
+ MaxD = ((ILdouble*)iCurImage->Data)[i];
+ }
+
+ // Renormalize to [0..1].
+ for (i = 0; i < NumPix; i++) {
+ // Change all negative numbers to 0.
+ if (((ILdouble*)iCurImage->Data)[i] < 0.0f)
+ ((ILdouble*)iCurImage->Data)[i] = 0.0f;
+ // Do the renormalization now, dividing by the maximum value.
+ ((ILdouble*)iCurImage->Data)[i] = ((ILdouble*)iCurImage->Data)[i] / MaxD;
+ } break;
+ }
+
+ return ilFixImage();
+}
+
+
+//@TODO: NAXISx have to come in order. Check this!
+ILenum GetCardImage(FITSHEAD *Header)
+{
+ char Buffer[80];
+
+ if (iread(Buffer, 1, 80) != 80) // Each card image is exactly 80 bytes long.
+ return CARD_READ_FAIL;
+
+//@TODO: Use something other than !strncmp?
+ if (!strncmp(Buffer, "END ", 4))
+ return CARD_END;
+
+ else if (!strncmp(Buffer, "SIMPLE ", 7)) {
+ // The true value 'T' is always in the 30th position.
+ if (Buffer[29] != 'T') {
+ // We cannot support FITS files that do not correspond to the standard.
+ Header->IsSimple = IL_FALSE; //@TODO: Does this even need to be set? Should exit loading anyway.
+ ilSetError(IL_FORMAT_NOT_SUPPORTED);
+ return CARD_NOT_SIMPLE;
+ }
+ Header->IsSimple = IL_TRUE;
+ return CARD_SIMPLE;
+ }
+
+ else if (!strncmp(Buffer, "BITPIX ", 7)) {
+ // The specs state that BITPIX has to come after SIMPLE.
+ if (Header->IsSimple != IL_TRUE) {
+ ilSetError(IL_INVALID_FILE_HEADER);
+ return CARD_READ_FAIL;
+ }
+ if (GetCardInt(Buffer, &Header->BitsPixel) != IL_TRUE)
+ return CARD_READ_FAIL;
+//@TODO: Should I do this check from the calling function? Does it really matter?
+ if (Header->BitsPixel == 0) {
+ ilSetError(IL_FORMAT_NOT_SUPPORTED);
+ return CARD_READ_FAIL;
+ }
+ return CARD_BITPIX;
+ }
+
+ // Needs the space after NAXIS so that it does not get this confused with NAXIS1, NAXIS2, etc.
+ else if (!strncmp(Buffer, "NAXIS ", 6)) {
+ if (GetCardInt(Buffer, &Header->NumAxes) != IL_TRUE)
+ return CARD_READ_FAIL;
+//@TODO: Should I do this check from the calling function? Does it really matter?
+ if (Header->NumAxes < 1 || Header->NumAxes > 3) {
+ ilSetError(IL_FORMAT_NOT_SUPPORTED);
+ return CARD_READ_FAIL;
+ }
+ return CARD_NUMAXES;
+ }
+
+ else if (!strncmp(Buffer, "NAXIS1 ", 7)) {
+ if (Header->NumAxes == 0) { // Has not been initialized, and it has to come first.
+ ilSetError(IL_INVALID_FILE_HEADER);
+ return CARD_READ_FAIL;
+ }
+ // First one will always be the width.
+ if (GetCardInt(Buffer, &Header->Width) != IL_TRUE)
+ return CARD_READ_FAIL;
+ return CARD_AXIS;
+ }
+
+ else if (!strncmp(Buffer, "NAXIS2 ", 7)) {
+ if (Header->NumAxes == 0) { // Has not been initialized, and it has to come first.
+ ilSetError(IL_INVALID_FILE_HEADER);
+ return CARD_READ_FAIL;
+ }
+ // Cannot have a 2nd axis for 0 or 1.
+ if (Header->NumAxes == 0 || Header->NumAxes == 1) {
+ ilSetError(IL_INVALID_FILE_HEADER);
+ return CARD_READ_FAIL;
+ }
+
+//@TODO: We are assuming that this is the height right now. Could it just be a
+// 1D image with multiple bytes per pixel?
+ if (GetCardInt(Buffer, &Header->Height) != IL_TRUE)
+ return CARD_READ_FAIL;
+ return CARD_AXIS;
+ }
+
+ else if (!strncmp(Buffer, "NAXIS3 ", 7)) {
+ if (Header->NumAxes == 0) { // Has not been initialized, and it has to come first.
+ ilSetError(IL_INVALID_FILE_HEADER);
+ return CARD_READ_FAIL;
+ }
+ // Cannot have a 3rd axis for 0, 1 and 2.
+ if (Header->NumAxes < 3) {
+ ilSetError(IL_INVALID_FILE_HEADER);
+ return CARD_READ_FAIL;
+ }
+
+ if (GetCardInt(Buffer, &Header->Depth) != IL_TRUE)
+ return CARD_READ_FAIL;
+ return CARD_AXIS;
+ }
+
+ return CARD_SKIP; // This is a card that we do not recognize, so skip it.
+}
+
+
+ILboolean GetCardInt(char *Buffer, ILint *Val)
+{
+ ILuint i;
+ char ValString[22];
+
+ if (Buffer[7] != '=' && Buffer[8] != '=')
+ return IL_FALSE;
+ for (i = 9; i < 30; i++) {
+ if (Buffer[i] != ' ' && Buffer[i] != 0) // Right-aligned with ' ' or 0, so skip.
+ break;
+ }
+ if (i == 30) // Did not find the integer in positions 10 - 30.
+ return IL_FALSE;
+
+ //@TODO: Safest way to do this?
+ memcpy(ValString, &Buffer[i], 30-i);
+ ValString[30-i] = 0; // Terminate the string.
+
+ //@TODO: Check the return somehow?
+ *Val = atoi(ValString);
+
+ return IL_TRUE;
+}
+
+#endif//IL_NO_FITS