diff options
Diffstat (limited to 'src/im_format_sgi.cpp')
-rw-r--r-- | src/im_format_sgi.cpp | 607 |
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; +} |