summaryrefslogtreecommitdiff
path: root/src/im_format_sgi.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/im_format_sgi.cpp')
-rw-r--r--src/im_format_sgi.cpp607
1 files changed, 607 insertions, 0 deletions
diff --git a/src/im_format_sgi.cpp b/src/im_format_sgi.cpp
new file mode 100644
index 0000000..1b0ac0a
--- /dev/null
+++ b/src/im_format_sgi.cpp
@@ -0,0 +1,607 @@
+/** \file
+ * \brief SGI - Silicon Graphics Image File Format
+ *
+ * See Copyright Notice in im_lib.h
+ * $Id: im_format_sgi.cpp,v 1.1 2008/10/17 06:10:16 scuri Exp $
+ */
+
+#include "im_format.h"
+#include "im_util.h"
+#include "im_format_all.h"
+#include "im_counter.h"
+
+#include "im_binfile.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+
+/* File Header Structure. */
+/* 2 Magic; 474 */
+/* 1 Storage; 0 ou 1 Compression */
+/* 1 BPC; 1 ou 2 Bytes Per Pixel Component */
+/* 2 Dimension; 1 ou 2 ou 3 */
+/* 2 XSize; Width */
+/* 2 YSize; Height */
+/* 2 ZSize; Number of Channels. B/W=1, RGB=3, RGBA=4 */
+/* 4 PixMin; Minimum Pixel Value */
+/* 4 PixMax; Maximum Pixel Value */
+/* 4 Dummy1; */
+/* 80 ImageName;*/
+/* 4 ColorMap; 0 ou 1 ou 2 ou 3 */
+/* 404 Dummy2;*/
+/* 512 */
+
+#define SGI_ID 474
+
+/* Compression */
+#define SGI_VERBATIM 0
+#define SGI_RLE 1
+
+/* ColorMap Ids */
+#define SGI_NORMAL 0
+#define SGI_DITHERED 1
+#define SGI_SCREEN 2
+#define SGI_COLORMAP 3
+
+template <class T>
+static int iSGIDecodeScanLine(T *optr, const T *iptr, int width)
+{
+ T pixel;
+ int c = 0, count;
+
+ while (c < width)
+ {
+ pixel = *iptr++;
+
+ count = pixel & 0x7f;
+ if (!count)
+ break;
+
+ c += count;
+ if (c > width)
+ return IM_ERR_ACCESS;
+
+ if (pixel & 0x80)
+ {
+ while (count--)
+ *optr++ = *iptr++;
+ }
+ else
+ {
+ pixel = *iptr++;
+ while (count--)
+ *optr++ = pixel;
+ }
+ }
+
+ if (c < width)
+ return IM_ERR_ACCESS;
+
+ return IM_ERR_NONE;
+}
+
+template <class T>
+static int iSGIEncodeScanLine(T *optr, const T *iptr, int width)
+{
+ const T *ibufend = iptr + width,
+ *sptr;
+ T *start_optr = optr;
+ int todo, cc, count;
+
+ while(iptr < ibufend)
+ {
+ sptr = iptr;
+ iptr += 2;
+ while ((iptr < ibufend) &&
+ ((iptr[-2] != iptr[-1]) || (iptr[-1] != iptr[0])))
+ iptr++;
+ iptr -= 2;
+ count = iptr-sptr;
+
+ while (count)
+ {
+ todo = (count > 126) ? 126: count;
+ count -= todo;
+ *optr++ = (T)(0x80 | todo);
+ while(todo--)
+ *optr++ = *sptr++;
+ }
+ sptr = iptr;
+ cc = *iptr++;
+
+ while((iptr < ibufend) && (*iptr == cc))
+ iptr++;
+ count = iptr-sptr;
+
+ while(count)
+ {
+ todo = (count > 126)? 126: count;
+ count -= todo;
+ *optr++ = (T)todo;
+ *optr++ = (T)cc;
+ }
+ }
+ *optr++ = 0;
+
+ return optr-start_optr;
+}
+
+static const char* iSGICompTable[2] =
+{
+ "NONE",
+ "RLE"
+};
+
+class imFormatSGI: public imFormat
+{
+ imBinFile* handle; /* the binary file handle */
+ unsigned char comp_type, /* sgi compression information */
+ bpc; /* bytes per channels */
+ unsigned int *starttab, /* compression control buffer */
+ *lengthtab; /* compression control buffer */
+
+public:
+ imFormatSGI()
+ :imFormat("SGI",
+ "Silicon Graphics Image File Format",
+ "*.rgb;*.rgba;*.bw;*.sgi;",
+ iSGICompTable,
+ 2,
+ 0)
+ {}
+ ~imFormatSGI() {}
+
+ int Open(const char* file_name);
+ int New(const char* file_name);
+ void Close();
+ void* Handle(int index);
+ int ReadImageInfo(int index);
+ int ReadImageData(void* data);
+ int WriteImageInfo();
+ int WriteImageData(void* data);
+ int CanWrite(const char* compression, int color_mode, int data_type) const;
+};
+
+void imFormatRegisterSGI(void)
+{
+ imFormatRegister(new imFormatSGI());
+}
+
+int imFormatSGI::Open(const char* file_name)
+{
+ unsigned short word_value;
+
+ /* opens the binary file for reading with motorola byte order */
+ handle = imBinFileOpen(file_name);
+ if (!handle)
+ return IM_ERR_OPEN;
+
+ imBinFileByteOrder(handle, IM_BIGENDIAN);
+
+ /* reads the SGI format identifier */
+ imBinFileRead(handle, &word_value, 1, 2);
+ if (imBinFileError(handle))
+ {
+ imBinFileClose(handle);
+ return IM_ERR_ACCESS;
+ }
+
+ if (word_value != SGI_ID)
+ {
+ imBinFileClose(handle);
+ return IM_ERR_FORMAT;
+ }
+
+ /* reads the compression information */
+ imBinFileRead(handle, &this->comp_type, 1, 1);
+ if (this->comp_type == SGI_RLE)
+ strcpy(this->compression, "RLE");
+ else if (this->comp_type == SGI_VERBATIM)
+ strcpy(this->compression, "NONE");
+ else
+ {
+ imBinFileClose(handle);
+ return IM_ERR_COMPRESS;
+ }
+
+ this->starttab = NULL;
+ this->lengthtab = NULL;
+
+ this->image_count = 1;
+
+ return IM_ERR_NONE;
+}
+
+int imFormatSGI::New(const char* file_name)
+{
+ /* opens the binary file for writing with motorola byte order */
+ handle = imBinFileNew(file_name);
+ if (!handle)
+ return IM_ERR_OPEN;
+
+ imBinFileByteOrder(handle, IM_BIGENDIAN);
+
+ this->starttab = NULL;
+ this->lengthtab = NULL;
+
+ this->image_count = 1;
+
+ return IM_ERR_NONE;
+}
+
+void imFormatSGI::Close()
+{
+ if (this->starttab) free(this->starttab);
+ if (this->lengthtab) free(this->lengthtab);
+ imBinFileClose(handle);
+}
+
+void* imFormatSGI::Handle(int index)
+{
+ if (index == 0)
+ return (void*)this->handle;
+ else
+ return NULL;
+}
+
+int imFormatSGI::ReadImageInfo(int index)
+{
+ (void)index;
+ unsigned short word_value, dimension, depth;
+
+ /* reads the number of bits per channel */
+ imBinFileRead(handle, &this->bpc, 1, 1);
+
+ /* reads the number of dimensions */
+ imBinFileRead(handle, &dimension, 1, 2);
+
+ /* reads the image width */
+ imBinFileRead(handle, &word_value, 1, 2);
+ this->width = word_value;
+
+ /* reads the image height */
+ imBinFileRead(handle, &word_value, 1, 2);
+ this->height = word_value;
+
+ /* reads the number of channels */
+ imBinFileRead(handle, &depth, 1, 2);
+
+ /* jump 12 bytes (min, max, dummy) */
+ imBinFileSeekOffset(handle, 12);
+
+ /* reads the image name */
+ char image_name[80];
+ imBinFileRead(handle, image_name, 80, 1);
+
+ if (image_name[0] != 0)
+ AttribTable()->Set("Description", IM_BYTE, imStrNLen(image_name, 80)+1, image_name);
+
+ /* reads the color map information */
+ unsigned int color_map_id;
+ imBinFileRead(handle, &color_map_id, 1, 4);
+
+ if (imBinFileError(handle))
+ return IM_ERR_ACCESS;
+
+ this->file_data_type = IM_BYTE;
+ if (this->bpc == 2)
+ this->file_data_type = IM_USHORT;
+
+ switch (dimension)
+ {
+ case 1:
+ this->height = 1;
+ depth = 1;
+ case 2:
+ depth = 1;
+ break;
+ case 3:
+ break;
+ default:
+ return IM_ERR_DATA;
+ }
+
+ switch (color_map_id)
+ {
+ case SGI_NORMAL:
+ switch(depth)
+ {
+ case 1:
+ this->file_color_mode = IM_GRAY;
+ break;
+ case 3:
+ this->file_color_mode = IM_RGB;
+ break;
+ case 4:
+ this->file_color_mode = IM_RGB | IM_ALPHA;
+ break;
+ default:
+ return IM_ERR_DATA;
+ }
+ break;
+ case SGI_DITHERED:
+ this->file_color_mode = IM_MAP;
+ break;
+ case SGI_COLORMAP:
+ this->file_color_mode = IM_RGB;
+ break;
+ case SGI_SCREEN:
+ this->file_color_mode = IM_GRAY;
+ break;
+ default:
+ return IM_ERR_DATA;
+ }
+
+ /* jump 404 bytes (dummy) */
+ imBinFileSeekOffset(handle, 404);
+
+ if (this->comp_type == SGI_RLE)
+ {
+ int tablen = this->height * depth;
+ this->starttab = (unsigned int *)malloc(tablen * sizeof(int));
+ this->lengthtab = (unsigned int *)malloc(tablen * sizeof(int));
+
+ /* reads the compression control information */
+ imBinFileRead(handle, this->starttab, tablen, 4);
+ imBinFileRead(handle, this->lengthtab, tablen, 4);
+
+ // allocates more than enough since compression algoritm can be ineficient
+ this->line_buffer_extra = 2*imImageLineSize(this->width, this->file_color_mode, this->file_data_type);
+ }
+
+ if (imBinFileError(handle))
+ return IM_ERR_ACCESS;
+
+ if (color_map_id == SGI_DITHERED)
+ {
+ static int red[8] = {0, 36, 73, 109, 146, 182, 218, 255};
+ static int green[8] = {0, 36, 73, 109, 146, 182, 218, 255};
+ static int blue[4] = {0, 85, 170, 255};
+
+ int c = 0;
+ for (int b = 0; b < 4; b++)
+ {
+ for (int g = 0; g < 8; g++)
+ {
+ for (int r = 0; r < 8; r++)
+ {
+ this->palette[c] = imColorEncode((imbyte)red[r],
+ (imbyte)green[g],
+ (imbyte)blue[b]);
+ c++;
+ }
+ }
+ }
+ }
+
+ return IM_ERR_NONE;
+}
+
+int imFormatSGI::WriteImageInfo()
+{
+ unsigned int dword_value;
+ unsigned short word_value;
+ unsigned char dummy[404];
+ memset(dummy, 0, 404);
+
+ this->comp_type = SGI_VERBATIM;
+ if (imStrEqual(this->compression, "RLE"))
+ this->comp_type = SGI_RLE;
+
+ unsigned int color_map_id = SGI_NORMAL;
+
+ this->file_color_mode = imColorModeSpace(this->user_color_mode);
+
+ int dimension = 2;
+ if (this->file_color_mode == IM_BINARY)
+ this->convert_bpp = -1; // expand 1 to 255
+ else if (this->file_color_mode == IM_RGB)
+ {
+ dimension = 3;
+ if (imColorModeHasAlpha(this->user_color_mode))
+ this->file_color_mode |= IM_ALPHA;
+ }
+
+ this->file_data_type = this->user_data_type;
+
+ this->bpc = 1;
+ int max = 255;
+ if (this->file_data_type == IM_USHORT)
+ {
+ max = 65535;
+ this->bpc = 2;
+ }
+
+ this->starttab = NULL;
+ this->lengthtab = NULL;
+
+ /* writes the SGI file header */
+ word_value = SGI_ID;
+ imBinFileWrite(handle, &word_value, 1, 2); /* identifier */
+ imBinFileWrite(handle, &this->comp_type, 1, 1); /* storage */
+ imBinFileWrite(handle, &this->bpc, 1, 1); /* bpc */
+ word_value = (imushort)dimension;
+ imBinFileWrite(handle, &word_value, 1, 2); /* dimension */
+ word_value = (unsigned short)this->width;
+ imBinFileWrite(handle, &word_value, 1, 2); /* image width */
+ word_value = (unsigned short)this->height;
+ imBinFileWrite(handle, &word_value, 1, 2); /* image height */
+ word_value = (imushort)imColorModeDepth(this->file_color_mode);
+ imBinFileWrite(handle, &word_value, 1, 2); /* depth */
+ dword_value = 0;
+ imBinFileWrite(handle, &dword_value, 1, 4); /* min */
+ dword_value = max;
+ imBinFileWrite(handle, &dword_value, 1, 4); /* max */
+ imBinFileWrite(handle, dummy, 4, 1); /* dummy */
+
+ /* tests if everything was ok */
+ if (imBinFileError(handle))
+ return IM_ERR_ACCESS;
+
+ int size;
+ char* image_name = (char*)AttribTable()->Get("Description", NULL, &size);
+ if (image_name)
+ {
+ if (size < 80)
+ {
+ imBinFileWrite(handle, image_name, size, 1);
+ imBinFileWrite(handle, dummy, 80-size, 1);
+ }
+ else
+ {
+ imBinFileWrite(handle, image_name, 79, 1);
+ imBinFileWrite(handle, (void*)"\0", 1, 1);
+ }
+ }
+ else
+ imBinFileWrite(handle, dummy, 80, 1); /* empty image name */
+
+ dword_value = color_map_id;
+ imBinFileWrite(handle, &dword_value, 1, 4); /* color_map_id */
+ imBinFileWrite(handle, dummy, 404, 1); /* dummy */
+
+ /* tests if everything was ok */
+ if (imBinFileError(handle))
+ return IM_ERR_ACCESS;
+
+ if (this->comp_type == SGI_RLE)
+ {
+ int tablen = this->height * imColorModeDepth(this->file_color_mode);
+ this->starttab = (unsigned int *)malloc(tablen*4);
+ this->lengthtab = (unsigned int *)malloc(tablen*4);
+
+ /* writes the empty compression control information */
+ /* we will write again at the end */
+ imBinFileWrite(handle, this->starttab, tablen*4, 1);
+ imBinFileWrite(handle, this->lengthtab, tablen*4, 1);
+
+ // allocates more than enough since compression algoritm can be ineficient
+ this->line_buffer_extra = 2*imImageLineSize(this->width, this->file_color_mode, this->file_data_type);
+ }
+
+ /* tests if everything was ok */
+ if (imBinFileError(handle))
+ return IM_ERR_ACCESS;
+
+ return IM_ERR_NONE;
+}
+
+int imFormatSGI::ReadImageData(void* data)
+{
+ int count = imFileLineBufferCount(this);
+
+ imCounterTotal(this->counter, count, "Reading SGI...");
+
+ imbyte* compressed_buffer = NULL;
+ if (this->comp_type == SGI_RLE) // point to the extra buffer
+ compressed_buffer = (imbyte*)this->line_buffer + this->line_buffer_size;
+
+ int row = 0, plane = 0;
+ for (int i = 0; i < count; i++)
+ {
+ if (this->comp_type == SGI_VERBATIM)
+ {
+ imBinFileRead(handle, this->line_buffer, this->line_buffer_size/this->bpc, this->bpc);
+
+ if (imBinFileError(handle))
+ return IM_ERR_ACCESS;
+ }
+ else
+ {
+ int row_index = row + plane*this->height;
+ imBinFileSeekTo(handle, this->starttab[row_index]);
+ imBinFileRead(handle, compressed_buffer, this->lengthtab[row_index] / this->bpc, this->bpc);
+
+ if (imBinFileError(handle))
+ return IM_ERR_ACCESS;
+
+ if (this->bpc == 1)
+ iSGIDecodeScanLine((imbyte*)this->line_buffer, compressed_buffer, this->width);
+ else
+ iSGIDecodeScanLine((imushort*)this->line_buffer, (imushort*)compressed_buffer, this->width);
+ }
+
+ imFileLineBufferRead(this, data, row, plane);
+
+ if (!imCounterInc(this->counter))
+ return IM_ERR_COUNTER;
+
+ imFileLineBufferInc(this, &row, &plane);
+ }
+
+ return IM_ERR_NONE;
+}
+
+int imFormatSGI::WriteImageData(void* data)
+{
+ int count = imFileLineBufferCount(this);
+
+ imCounterTotal(this->counter, count, "Writing SGI...");
+
+ imbyte* compressed_buffer = NULL;
+ if (this->comp_type == SGI_RLE) // point to the extra buffer
+ compressed_buffer = (imbyte*)this->line_buffer + this->line_buffer_size;
+
+ int row = 0, plane = 0;
+ for (int i = 0; i < count; i++)
+ {
+ imFileLineBufferWrite(this, data, row, plane);
+
+ if (this->comp_type == SGI_VERBATIM)
+ imBinFileWrite(handle, this->line_buffer, this->line_buffer_size/this->bpc, this->bpc);
+ else
+ {
+ int length;
+ if (this->bpc == 1)
+ length = iSGIEncodeScanLine(compressed_buffer, (imbyte*)this->line_buffer, this->width);
+ else
+ length = iSGIEncodeScanLine((imushort*)compressed_buffer, (imushort*)this->line_buffer, this->width);
+
+ int row_index = row + plane*this->height;
+ this->starttab[row_index] = imBinFileTell(handle);
+ this->lengthtab[row_index] = length*this->bpc;
+
+ imBinFileWrite(handle, compressed_buffer, length, this->bpc);
+ }
+
+ if (imBinFileError(handle))
+ return IM_ERR_ACCESS;
+
+ if (!imCounterInc(this->counter))
+ return IM_ERR_COUNTER;
+
+ imFileLineBufferInc(this, &row, &plane);
+ }
+
+ if (this->comp_type == SGI_RLE)
+ {
+ imBinFileSeekTo(this->handle, 512);
+ int tablen = this->height * imColorModeDepth(this->file_color_mode);
+ imBinFileWrite(handle, this->starttab, tablen, 4);
+ imBinFileWrite(handle, this->lengthtab, tablen, 4);
+ }
+
+ return IM_ERR_NONE;
+}
+
+int imFormatSGI::CanWrite(const char* compression, int color_mode, int data_type) const
+{
+ int color_space = imColorModeSpace(color_mode);
+
+ if (color_space == IM_YCBCR || color_space == IM_LAB ||
+ color_space == IM_LUV || color_space == IM_XYZ ||
+ color_space == IM_CMYK || color_space == IM_MAP)
+ return IM_ERR_DATA;
+
+ if (data_type != IM_BYTE && data_type != IM_USHORT)
+ return IM_ERR_DATA;
+
+ if (!compression || compression[0] == 0)
+ return IM_ERR_NONE;
+
+ if (!imStrEqual(compression, "NONE") && !imStrEqual(compression, "RLE"))
+ return IM_ERR_COMPRESS;
+
+ return IM_ERR_NONE;
+}