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