/** \file * \brief RAS - Sun Raster File * * See Copyright Notice in im_lib.h * $Id: im_format_ras.cpp,v 1.2 2008/12/03 15:45:34 scuri Exp $ */ #include "im_format.h" #include "im_format_all.h" #include "im_util.h" #include "im_counter.h" #include "im_binfile.h" #include #include #include /* 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 imFileFormatRAS: public imFileFormatBase { 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: imFileFormatRAS(const imFormat* _iformat): imFileFormatBase(_iformat) {} ~imFileFormatRAS() {} 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); }; class imFormatRAS: public imFormat { public: imFormatRAS() :imFormat("RAS", "Sun Raster File", "*.ras;", iRASCompTable, 2, 0) {} ~imFormatRAS() {} imFileFormatBase* Create(void) const { return new imFileFormatRAS(this); } int CanWrite(const char* compression, int color_mode, int data_type) const; }; void imFormatRegisterRAS(void) { imFormatRegister(new imFormatRAS()); } int imFileFormatRAS::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 imFileFormatRAS::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 imFileFormatRAS::Close() { imBinFileClose(handle); } void* imFileFormatRAS::Handle(int index) { if (index == 0) return (void*)this->handle; else return NULL; } int imFileFormatRAS::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 imFileFormatRAS::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 imFileFormatRAS::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 imFileFormatRAS::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 imFileFormatRAS::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 imFileFormatRAS::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 imFileFormatRAS::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; }