summaryrefslogtreecommitdiff
path: root/src/im_format_ras.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/im_format_ras.cpp')
-rw-r--r--src/im_format_ras.cpp598
1 files changed, 598 insertions, 0 deletions
diff --git a/src/im_format_ras.cpp b/src/im_format_ras.cpp
new file mode 100644
index 0000000..bab074a
--- /dev/null
+++ b/src/im_format_ras.cpp
@@ -0,0 +1,598 @@
+/** \file
+ * \brief RAS - Sun Raster File
+ *
+ * See Copyright Notice in im_lib.h
+ * $Id: im_format_ras.cpp,v 1.1 2008/10/17 06:10:16 scuri Exp $
+ */
+
+#include "im_format.h"
+#include "im_format_all.h"
+#include "im_util.h"
+#include "im_counter.h"
+
+#include "im_binfile.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+
+/* File Header Structure. */
+/* 4 Magic; magic number */
+/* 4 BufferSize; width (pixels) of image */
+/* 4 Height; height (pixels) of image */
+/* 4 Depth; depth (1, 8, 24, or 32) of pixel */
+/* 4 Length; length (bytes) of image */
+/* 4 Type; type of file; see RT_OLD below */
+/* 4 MapType; type of colormap; see RAS_NONE below */
+/* 4 MapLength; length (bytes) of following map */
+/* 32 */
+
+#define RAS_ID 0x59A66A95
+
+/* Sun supported ras_type's */
+#define RAS_OLD 0 /* Raw pixrect image in 68000 byte order */
+#define RAS_STANDARD 1 /* Raw pixrect image in 68000 byte order */
+#define RAS_BYTE_ENCODED 2 /* Run-length compression of bytes */
+#define RAS_EXPERIMENTAL 0xffff /* Reserved for testing */
+
+#define RAS_ESCAPE 0x80
+
+/* Sun supported ras_maptype's */
+#define RAS_NONE 0 /* ras_maplength is expected to be 0 */
+#define RAS_EQUAL_RGB 1 /* red[ras_maplength/3],green[],blue[] */
+#define RAS_RAW 2 /* Sun registered ras_maptype's */
+
+
+/* NOTES:
+ * Each line of the image is rounded out to a multiple of 16 bits.
+ * This corresponds to the rounding convention used by the memory pixrect
+ * package (/usr/include/pixrect/memvar.h) of the SunWindows system.
+ * The ras_encoding field (always set to 0 by Sun's supported software)
+ * was renamed to ras_length in release 2.0. As a result, rasterfiles
+ * of type 0 generated by the old software claim to have 0 length; for
+ * compatibility, code reading rasterfiles must be prepared to compute the
+ * true length from the width, height, and depth fields. */
+
+static int iRASDecodeScanLine(imBinFile* handle, unsigned char* DecodedBuffer, int BufferSize)
+{
+ int index = 0;
+ unsigned char count = 0;
+ unsigned char value = 0;
+
+ while (index < BufferSize)
+ {
+ imBinFileRead(handle, &value, 1, 1);
+
+ if (value == RAS_ESCAPE)
+ {
+ imBinFileRead(handle, &count, 1, 1);
+
+ if (count != 0)
+ {
+ imBinFileRead(handle, &value, 1, 1);
+
+ count++;
+ while (count-- && index < BufferSize)
+ {
+ *DecodedBuffer++ = value;
+ index++;
+ }
+ }
+ else
+ {
+ *DecodedBuffer++ = RAS_ESCAPE;
+ index++;
+ }
+ }
+ else
+ {
+ *DecodedBuffer++ = value;
+ index++;
+ }
+
+ if (imBinFileError(handle))
+ return IM_ERR_ACCESS;
+ }
+
+ return IM_ERR_NONE;
+}
+
+static int iRASEncodeScanLine(imbyte* EncodedBuffer, const imbyte* DecodedBuffer, int BufferSize)
+{
+ int index = 0; /* Index into uncompressed data buffer */
+ int scanindex = 0; /* Index into compressed data buffer */
+ int runcount; /* Length of encoded pixel run */
+ unsigned char runvalue; /* Value of encoded pixel run */
+
+ while (index < BufferSize)
+ {
+ for (runcount = 1, runvalue = DecodedBuffer[index];
+ index + runcount < BufferSize && runvalue == DecodedBuffer[index + runcount] && runcount < 256;
+ runcount++);
+
+ if (runcount > 2) /* Multiple pixel run */
+ {
+ EncodedBuffer[scanindex++] = RAS_ESCAPE;
+ EncodedBuffer[scanindex++] = (imbyte)(runcount-1);
+ EncodedBuffer[scanindex++] = runvalue;
+ }
+ else if (runcount == 2)
+ {
+ if (runvalue == RAS_ESCAPE) /* Two Escapes */
+ {
+ EncodedBuffer[scanindex++] = RAS_ESCAPE;
+ EncodedBuffer[scanindex++] = 1;
+ EncodedBuffer[scanindex++] = RAS_ESCAPE;
+ }
+ else /* Two Single runs */
+ {
+ EncodedBuffer[scanindex++] = runvalue;
+ EncodedBuffer[scanindex++] = runvalue;
+ }
+ }
+ else /* Single run */
+ {
+ if (runvalue == RAS_ESCAPE)
+ {
+ EncodedBuffer[scanindex++] = RAS_ESCAPE;
+ EncodedBuffer[scanindex++] = 0;
+ }
+ else
+ EncodedBuffer[scanindex++] = runvalue;
+ }
+
+ index += runcount; /* Jump ahead to next pixel run value */
+ }
+
+ return scanindex; /* Return the number of unsigned chars written to buffer */
+}
+
+static const char* iRASCompTable[2] =
+{
+ "NONE",
+ "RLE"
+};
+
+class imFormatRAS: public imFormat
+{
+ imBinFile* handle; /* the binary file handle */
+ unsigned int bpp, /* number of bits per pixel */
+ comp_type, /* ras compression information */
+ map_type, /* palette information */
+ line_raw_size; /* line buffer size */
+
+ int ReadPalette();
+ int WritePalette();
+ void FixRGB();
+
+public:
+ imFormatRAS()
+ :imFormat("RAS",
+ "Sun Raster File",
+ "*.ras;",
+ iRASCompTable,
+ 2,
+ 0)
+ {}
+ ~imFormatRAS() {}
+
+ 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 imFormatRegisterRAS(void)
+{
+ imFormatRegister(new imFormatRAS());
+}
+
+int imFormatRAS::Open(const char* file_name)
+{
+ unsigned int dword_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 RAS format identifier */
+ imBinFileRead(handle, &dword_value, 1, 4);
+ if (imBinFileError(handle))
+ {
+ imBinFileClose(handle);
+ return IM_ERR_ACCESS;
+ }
+
+ if (dword_value != RAS_ID)
+ {
+ imBinFileClose(handle);
+ return IM_ERR_FORMAT;
+ }
+
+ /* reads the compression information */
+ imBinFileSeekOffset(handle, 16);
+
+ imBinFileRead(handle, &this->comp_type, 1, 4);
+ if (this->comp_type == RAS_BYTE_ENCODED)
+ strcpy(this->compression, "RLE");
+ else if (this->comp_type == RAS_OLD || this->comp_type == RAS_STANDARD)
+ strcpy(this->compression, "NONE");
+ else
+ {
+ imBinFileClose(handle);
+ return IM_ERR_COMPRESS;
+ }
+
+ imBinFileSeekOffset(handle, -20);
+
+ this->image_count = 1;
+
+ return IM_ERR_NONE;
+}
+
+int imFormatRAS::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->image_count = 1;
+
+ return IM_ERR_NONE;
+}
+
+void imFormatRAS::Close()
+{
+ imBinFileClose(handle);
+}
+
+void* imFormatRAS::Handle(int index)
+{
+ if (index == 0)
+ return (void*)this->handle;
+ else
+ return NULL;
+}
+
+int imFormatRAS::ReadImageInfo(int index)
+{
+ (void)index;
+ unsigned int dword_value;
+
+ this->file_data_type = IM_BYTE;
+
+ /* reads the image width */
+ imBinFileRead(handle, &dword_value, 1, 4);
+ this->width = (int)dword_value;
+
+ /* reads the image height */
+ imBinFileRead(handle, &dword_value, 1, 4);
+ this->height = (int)dword_value;
+
+ /* reads the number of bits per pixel */
+ imBinFileRead(handle, &this->bpp, 1, 4);
+
+ if (imBinFileError(handle))
+ return IM_ERR_ACCESS;
+
+ // sanity check
+ if (this->bpp != 1 && this->bpp != 8 &&
+ this->bpp != 24 && this->bpp != 32)
+ return IM_ERR_DATA;
+
+ if (this->bpp > 8)
+ {
+ this->file_color_mode = IM_RGB;
+ this->file_color_mode |= IM_PACKED;
+
+ if (this->bpp == 32)
+ this->file_color_mode |= IM_ALPHA;
+ }
+ else
+ {
+ this->file_color_mode = IM_MAP;
+
+ if (this->bpp == 1)
+ {
+ this->convert_bpp = 1;
+ this->palette_count = 2;
+ }
+ }
+
+ this->file_color_mode |= IM_TOPDOWN;
+
+ this->line_raw_size = imFileLineSizeAligned(this->width, this->bpp, 2);
+ this->line_buffer_extra = 2; // room enough for padding
+
+ /* jump 8 bytes (Length+Compression) */
+ imBinFileSeekOffset(handle, 8);
+
+ /* reads the palette information */
+ imBinFileRead(handle, &this->map_type, 1, 4);
+
+ /* reads the palette size */
+ imBinFileRead(handle, &dword_value, 1, 4);
+
+ if (imBinFileError(handle))
+ return IM_ERR_ACCESS;
+
+ /* updates the pal_size based on the palette size */
+ if (this->bpp <= 8 && this->map_type != RAS_NONE)
+ {
+ this->palette_count = dword_value / 3;
+ return ReadPalette();
+ }
+
+ if (this->bpp <= 8 && this->map_type == RAS_NONE)
+ {
+ if (this->bpp == 1)
+ this->file_color_mode = IM_BINARY;
+ else
+ this->file_color_mode = IM_GRAY;
+
+ this->file_color_mode |= IM_TOPDOWN;
+ }
+
+ return IM_ERR_NONE;
+}
+
+int imFormatRAS::WriteImageInfo()
+{
+ this->file_data_type = IM_BYTE;
+ this->file_color_mode = imColorModeSpace(this->user_color_mode);
+
+ if (imStrEqual(this->compression, "RLE"))
+ this->comp_type = RAS_BYTE_ENCODED;
+ else
+ this->comp_type = RAS_STANDARD;
+
+ // Force the palette, even for Binary and Gray.
+ this->map_type = RAS_EQUAL_RGB;
+
+ if (this->file_color_mode == IM_BINARY)
+ {
+ this->bpp = 1;
+ this->convert_bpp = 1;
+ }
+ else if (this->file_color_mode == IM_RGB)
+ {
+ this->file_color_mode |= IM_PACKED;
+ this->map_type = RAS_NONE;
+ this->bpp = 24;
+
+ if (imColorModeHasAlpha(this->user_color_mode))
+ {
+ this->file_color_mode |= IM_ALPHA;
+ this->bpp = 32;
+ }
+ }
+ else
+ this->bpp = 8;
+
+ this->file_color_mode |= IM_TOPDOWN;
+
+ this->line_raw_size = imFileLineSizeAligned(this->width, this->bpp, 2);
+ this->line_buffer_extra = 2; // room enough for padding
+
+ if (this->comp_type == RAS_BYTE_ENCODED)
+ {
+ // allocates more than enough since compression algoritm can be ineficient
+ this->line_buffer_extra += 2*this->line_raw_size;
+ }
+
+ /* writes the RAS file header */
+
+ unsigned int dword_value = RAS_ID;
+ imBinFileWrite(handle, &dword_value, 1, 4); /* identifier */
+ dword_value = this->width;
+ imBinFileWrite(handle, &dword_value, 1, 4); /* image width */
+ dword_value = this->height;
+ imBinFileWrite(handle, &dword_value, 1, 4); /* image height */
+ dword_value = this->bpp;
+ imBinFileWrite(handle, &dword_value, 1, 4); /* bits per pixel */
+ dword_value = this->height * this->line_raw_size;
+ imBinFileWrite(handle, &dword_value, 1, 4); /* image lenght */
+ dword_value = this->comp_type;
+ imBinFileWrite(handle, &dword_value, 1, 4); /* compression information */
+ dword_value = this->map_type;
+ imBinFileWrite(handle, &dword_value, 1, 4); /* palette information */
+ dword_value = (this->map_type == RAS_NONE)? 0: this->palette_count * 3;
+ imBinFileWrite(handle, &dword_value, 1, 4); /* palette lenght */
+
+ /* tests if everything was ok */
+ if (imBinFileError(handle))
+ return IM_ERR_ACCESS;
+
+ if (this->map_type != RAS_NONE)
+ return WritePalette();
+
+ return IM_ERR_NONE;
+}
+
+int imFormatRAS::ReadPalette()
+{
+ unsigned char ras_colors[256 * 3];
+
+ /* reads the color palette */
+ imBinFileRead(handle, ras_colors, this->palette_count * 3, 1);
+
+ if (imBinFileError(handle))
+ return IM_ERR_ACCESS;
+
+ /* convert the color map to the IM format */
+ for (int c = 0; c < this->palette_count; c++)
+ {
+ if (this->map_type == RAS_RAW)
+ {
+ int i = c * 3;
+ this->palette[c] = imColorEncode(ras_colors[i],
+ ras_colors[i+1],
+ ras_colors[i+2]);
+ }
+ else
+ {
+ this->palette[c] = imColorEncode(ras_colors[c],
+ ras_colors[c+this->palette_count],
+ ras_colors[c+2*this->palette_count]);
+ }
+ }
+
+ return IM_ERR_NONE;
+}
+
+int imFormatRAS::WritePalette()
+{
+ int c;
+ unsigned char ras_colors[256 * 3];
+
+ /* convert the color map to the IM format */
+ for (c = 0; c < this->palette_count; c++)
+ {
+ imColorDecode(&ras_colors[c], &ras_colors[c+this->palette_count], &ras_colors[c+2*this->palette_count], this->palette[c]);
+ }
+
+ /* writes the color palette */
+ imBinFileWrite(handle, ras_colors, this->palette_count * 3, 1);
+
+ if (imBinFileError(handle))
+ return IM_ERR_ACCESS;
+
+ return IM_ERR_NONE;
+}
+
+void imFormatRAS::FixRGB()
+{
+ int x;
+ imbyte* byte_data = (imbyte*)this->line_buffer;
+
+ switch (this->bpp)
+ {
+ case 32:
+ {
+ // convert ABGR <-> RGBA
+ for (x = 0; x < this->width; x++)
+ {
+ int c = x*4;
+ imbyte temp = byte_data[c]; // swap R and A
+ byte_data[c] = byte_data[c+3];
+ byte_data[c+3] = temp;
+
+ temp = byte_data[c+1]; // swap G and B
+ byte_data[c+1] = byte_data[c+2];
+ byte_data[c+2] = temp;
+ }
+ }
+ break;
+ default: // 24
+ {
+ // convert BGR <-> RGB
+ for (x = 0; x < this->width; x++)
+ {
+ int c = x*3;
+ imbyte temp = byte_data[c]; // swap R and B
+ byte_data[c] = byte_data[c+2];
+ byte_data[c+2] = temp;
+ }
+ }
+ break;
+ }
+}
+
+int imFormatRAS::ReadImageData(void* data)
+{
+ imCounterTotal(this->counter, this->height, "Reading RAS...");
+
+ for (int row = 0; row < this->height; row++)
+ {
+ /* read and decompress the data */
+ if (this->comp_type != RAS_BYTE_ENCODED)
+ {
+ imBinFileRead(handle, this->line_buffer, this->line_raw_size, 1);
+
+ if (imBinFileError(handle))
+ return IM_ERR_ACCESS;
+ }
+ else
+ {
+ if (iRASDecodeScanLine(handle, (imbyte*)this->line_buffer, this->line_raw_size) == IM_ERR_ACCESS)
+ return IM_ERR_ACCESS;
+ }
+
+ if (this->bpp > 8)
+ FixRGB();
+
+ imFileLineBufferRead(this, data, row, 0);
+
+ if (!imCounterInc(this->counter))
+ return IM_ERR_COUNTER;
+ }
+
+ return IM_ERR_NONE;
+}
+
+int imFormatRAS::WriteImageData(void* data)
+{
+ imCounterTotal(this->counter, this->height, "Writing RAS...");
+
+ imbyte* compressed_buffer = NULL;
+ if (this->comp_type == RAS_BYTE_ENCODED) // point to the extra buffer
+ compressed_buffer = (imbyte*)this->line_buffer + this->line_buffer_size+2;
+
+ for (int row = 0; row < this->height; row++)
+ {
+ imFileLineBufferWrite(this, data, row, 0);
+
+ if (this->bpp > 8)
+ FixRGB();
+
+ if (this->comp_type == RAS_BYTE_ENCODED)
+ {
+ int compressed_size = iRASEncodeScanLine(compressed_buffer, (imbyte*)this->line_buffer, this->line_raw_size);
+ imBinFileWrite(handle, compressed_buffer, compressed_size, 1);
+ }
+ else
+ {
+ imBinFileWrite(handle, this->line_buffer, this->line_raw_size, 1);
+ }
+
+ if (imBinFileError(handle))
+ return IM_ERR_ACCESS;
+
+ if (!imCounterInc(this->counter))
+ return IM_ERR_COUNTER;
+ }
+
+ return IM_ERR_NONE;
+}
+
+int imFormatRAS::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)
+ return IM_ERR_DATA;
+
+ if (data_type != IM_BYTE)
+ 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;
+}