summaryrefslogtreecommitdiff
path: root/src/im_format_pcx.cpp
diff options
context:
space:
mode:
authorscuri <scuri>2008-10-17 06:10:15 +0000
committerscuri <scuri>2008-10-17 06:10:15 +0000
commit5a422aba704c375a307a902bafe658342e209906 (patch)
tree5005011e086bb863d8fb587ad3319bbec59b2447 /src/im_format_pcx.cpp
First commit - moving from LuaForge to SourceForge
Diffstat (limited to 'src/im_format_pcx.cpp')
-rw-r--r--src/im_format_pcx.cpp701
1 files changed, 701 insertions, 0 deletions
diff --git a/src/im_format_pcx.cpp b/src/im_format_pcx.cpp
new file mode 100644
index 0000000..32487bc
--- /dev/null
+++ b/src/im_format_pcx.cpp
@@ -0,0 +1,701 @@
+/** \file
+ * \brief PCX - ZSoft Picture
+ *
+ * See Copyright Notice in im_lib.h
+ * $Id: im_format_pcx.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>
+
+#define PCX_ID 0x0A
+
+
+/* PCX file header */
+/* 1 Id; Manufacturer ID */
+/* 1 Version; Version */
+/* 1 Encoding; Encoding Scheme */
+/* 1 BitsPerPixel; Bits/Pixel/Plane */
+/* 2 Xmin; X Start (upper left) */
+/* 2 Ymin; Y Start (top) */
+/* 2 Xmax; X End (lower right) */
+/* 2 Ymax; Y End (bottom) */
+/* 2 Hdpi; Horizontal Resolution */
+/* 2 Vdpi; Vertical Resolution */
+/* 3*16 Colormap; 16-Color EGA Palette */
+/* 1 Reserved; Reserved */
+/* 1 NPlanes; Number of Color Planes */
+/* 2 BytesPerLine; Bytes/Line/Plane */
+/* 2 PaletteInfo; Palette Interpretation */
+/* 2 HScreenSize; Horizontal Screen Size */
+/* 2 VScreenSize; Vertical Screen Size */
+/* 54 Filler; Reserved */
+/* 128 */
+
+/* Default 16 color VGA palette */
+static unsigned char iPCXDefaultPalette[3*16] =
+{
+ 0, 0, 0,
+ 0, 0, 255,
+ 0, 255, 0,
+ 0, 255, 255,
+ 255, 0, 0,
+ 255, 0, 255,
+ 255, 255, 0,
+ 255, 255, 255,
+ 85, 85, 255,
+ 85, 85, 85,
+ 0, 170, 0,
+ 170, 0, 0,
+ 85, 255, 255,
+ 255, 85, 255,
+ 255, 255, 85,
+ 255, 255, 255
+};
+
+static int iPCXEncodeScanLine(unsigned char* EncodedBuffer, const unsigned char* DecodedBuffer, int BufferSize)
+{
+ int index = 0; /* Index into uncompressed data buffer */
+ int scanindex = 0; /* Index into compressed data buffer */
+ unsigned char runcount; /* Length of encoded pixel run */
+ unsigned char runvalue; /* Value of encoded pixel run */
+
+ while (index < BufferSize)
+ {
+ /** Get the run count of the next pixel value run.
+ ** Pixel value runs are encoded until a different pixel value
+ ** is encountered, the end of the scan line is reached, or 63
+ ** pixel values have been counted. */
+ for (runcount = 1, runvalue = DecodedBuffer[index];
+ index + runcount < BufferSize && runvalue == DecodedBuffer[index + runcount] && runcount < 63;
+ runcount++);
+
+ /** Encode the run into a one or two-unsigned char code.
+ ** Multiple pixel runs are stored in two-unsigned char codes. If a single
+ ** pixel run has a value of less than 64 then it is stored in a
+ ** one-unsigned char code. If a single pixel run has a value of 64 to 255
+ ** then it is stored in a two-unsigned char code. */
+
+ if (runcount > 1) /* Multiple pixel run */
+ {
+ EncodedBuffer[scanindex++] = (unsigned char)(runcount | 0xC0);
+ EncodedBuffer[scanindex++] = runvalue;
+ }
+ else /* Single pixel run */
+ {
+ if (DecodedBuffer[index] < 64) /* Value is 0 to 63 */
+ EncodedBuffer[scanindex++] = runvalue;
+ else /* Value is 64 to 255 */
+ {
+ EncodedBuffer[scanindex++] = (unsigned char)(runcount | 0xC0);
+ EncodedBuffer[scanindex++] = runvalue;
+ }
+ }
+
+ index += runcount; /* Jump ahead to next pixel run value */
+ }
+
+ return scanindex; /* Return the number of unsigned chars written to buffer */
+}
+
+static int iPCXDecodeScanLine(imBinFile* handle, unsigned char* DecodedBuffer, int BufferSize)
+{
+ int index = 0; /* Index into compressed scan line buffer */
+ unsigned char data; /* Data byte read from PCX file */
+ unsigned char runcount = 0; /* Length of decoded pixel run */
+ unsigned char runvalue = 0; /* Value of decoded pixel run */
+
+ while (index < BufferSize) /* Read until the end of the buffer */
+ {
+ imBinFileRead(handle, &data, 1, 1);
+
+ if ((data & 0xC0) == 0xC0) /* Two-unsigned char code */
+ {
+ runcount = (unsigned char)(data & 0x3F); /* Get run count */
+ imBinFileRead(handle, &runvalue, 1, 1);
+ }
+ else /* One unsigned char code */
+ {
+ runcount = 1; /* Run count is one */
+ runvalue = data; /* Pixel value */
+ }
+
+ if (imBinFileError(handle))
+ return IM_ERR_ACCESS;
+
+ /* Write the pixel run to the buffer */
+ for (;runcount && (index < BufferSize); runcount--, index++)
+ DecodedBuffer[index] = runvalue; /* Assign value to buffer */
+ }
+
+ return IM_ERR_NONE;
+}
+
+static const char* iPCXCompTable[2] =
+{
+ "NONE",
+ "RLE"
+};
+
+class imFormatPCX: public imFormat
+{
+ imBinFile* handle; /* the binary file handle */
+ int bpp; /* number of bits per pixel */
+ unsigned char version; /* format version */
+ unsigned char comp_type; /* PCX compression information */
+ int line_raw_size; /* bytes per line per plane */
+
+ int ReadPalette();
+ int WritePalette();
+ void Expand4bpp();
+ void Pack24bpp();
+ void Unpack24bpp();
+
+public:
+ imFormatPCX()
+ :imFormat("PCX",
+ "ZSoft Picture",
+ "*.pcx;",
+ iPCXCompTable,
+ 2,
+ 0)
+ {}
+ ~imFormatPCX() {}
+
+ 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 imFormatRegisterPCX(void)
+{
+ imFormatRegister(new imFormatPCX());
+}
+
+int imFormatPCX::Open(const char* file_name)
+{
+ unsigned char id;
+
+ /* opens the binary file for reading with intel unsigned char order */
+ handle = imBinFileOpen(file_name);
+ if (!handle)
+ return IM_ERR_OPEN;
+
+ imBinFileByteOrder(handle, IM_LITTLEENDIAN);
+
+ /* reads the PCX format identifier */
+ imBinFileRead(handle, &id, 1, 1);
+ if (imBinFileError(handle))
+ {
+ imBinFileClose(handle);
+ return IM_ERR_ACCESS;
+ }
+
+ if (id != PCX_ID)
+ {
+ imBinFileClose(handle);
+ return IM_ERR_FORMAT;
+ }
+
+ /* reads the format version */
+ imBinFileRead(handle, &this->version, 1, 1);
+
+ /* reads the compression comp_type */
+ imBinFileRead(handle, &this->comp_type, 1, 1);
+ if (this->comp_type)
+ strcpy(this->compression, "RLE");
+ else
+ strcpy(this->compression, "NONE");
+
+ this->image_count = 1;
+
+ return IM_ERR_NONE;
+}
+
+int imFormatPCX::New(const char* file_name)
+{
+ /* opens the binary file for writing with intel byte order */
+ handle = imBinFileNew(file_name);
+ if (!handle)
+ return IM_ERR_OPEN;
+
+ imBinFileByteOrder(handle, IM_LITTLEENDIAN);
+
+ return IM_ERR_NONE;
+}
+
+void imFormatPCX::Close()
+{
+ imBinFileClose(handle);
+}
+
+void* imFormatPCX::Handle(int index)
+{
+ if (index == 0)
+ return (void*)this->handle;
+ else
+ return NULL;
+}
+
+int imFormatPCX::ReadImageInfo(int index)
+{
+ unsigned char bppp, planes;
+ unsigned short xmin, xmax, ymax, ymin, word, bplp;
+ (void)index;
+
+ this->file_data_type = IM_BYTE;
+
+ /* reads the Number of bits/pixel per plane */
+ imBinFileRead(handle, &bppp, 1, 1);
+
+ /* reads the image width and height */
+ imBinFileRead(handle, &xmin, 1, 2);
+ imBinFileRead(handle, &ymin, 1, 2);
+ imBinFileRead(handle, &xmax, 1, 2);
+ imBinFileRead(handle, &ymax, 1, 2);
+ this->width = xmax - xmin + 1;
+ this->height = ymax - ymin + 1;
+
+ imAttribTable* attrib_table = AttribTable();
+
+ if (xmin && ymin)
+ {
+ attrib_table->Set("XScreen", IM_USHORT, 1, &xmin);
+ attrib_table->Set("YScreen", IM_USHORT, 1, &ymin);
+ }
+
+ /* read the x resolution */
+ imBinFileRead(handle, &word, 1, 2);
+ float xres = word;
+
+ /* read the y resolution */
+ imBinFileRead(handle, &word, 1, 2);
+ float yres = word;
+
+ if (xres && yres)
+ {
+ attrib_table->Set("XResolution", IM_FLOAT, 1, &xres);
+ attrib_table->Set("YResolution", IM_FLOAT, 1, &yres);
+ attrib_table->Set("ResolutionUnit", IM_BYTE, 4, "DPI");
+ }
+
+ /* jump 3*16+1 bytes (colormap + reserved) */
+ imBinFileSeekOffset(handle, 3*16+1);
+
+ /* reads the Number of color planes */
+ imBinFileRead(handle, &planes, 1, 1);
+ this->bpp = bppp * planes;
+
+ /* reads the Number of bytes per scan line per color planes */
+ imBinFileRead(handle, &bplp, 1, 2);
+ this->line_raw_size = bplp * planes;
+ this->line_buffer_extra = 2; // room enough for padding
+
+ if (imBinFileError(handle))
+ return IM_ERR_ACCESS;
+
+ // sanity check
+ if (this->bpp != 1 && this->bpp != 4 &&
+ this->bpp != 8 && this->bpp != 24)
+ return IM_ERR_DATA;
+
+ if (this->bpp > 8)
+ {
+ this->file_color_mode = IM_RGB;
+ this->file_color_mode |= IM_PACKED;
+ this->line_buffer_extra += 3*this->width; // room for 24 bpp packing
+ }
+ else
+ {
+ this->file_color_mode = IM_MAP;
+ this->palette_count = 1 << this->bpp;
+
+ if (this->bpp == 1) // only 1 bpp, 4 bpp will be expanded here
+ this->convert_bpp = 1;
+
+ if (this->bpp == 4)
+ this->line_buffer_extra += this->width; // room for 4 bpp expansion
+ }
+
+ this->file_color_mode |= IM_TOPDOWN;
+
+ if (this->bpp <= 8)
+ return ReadPalette();
+
+ return IM_ERR_NONE;
+}
+
+int imFormatPCX::WriteImageInfo()
+{
+ unsigned short word_value, bplp;
+ unsigned char byte_value, filler[54+3*2];
+
+ this->file_data_type = IM_BYTE;
+
+ this->file_color_mode = imColorModeSpace(this->user_color_mode);
+
+ if (imStrEqual(this->compression, "NONE"))
+ this->comp_type = (unsigned char)0;
+ else
+ this->comp_type = (unsigned char)1;
+
+ if (this->file_color_mode == IM_BINARY)
+ {
+ this->bpp = 1;
+ this->convert_bpp = 1;
+ }
+ else if (this->file_color_mode == IM_RGB)
+ {
+ this->bpp = 24;
+ this->file_color_mode |= IM_PACKED;
+ }
+ else
+ this->bpp = 8;
+
+ this->file_color_mode |= IM_TOPDOWN;
+
+ int planes = imColorModeDepth(this->file_color_mode);
+ bplp = (unsigned short)imFileLineSizeAligned(this->width, this->bpp/planes, 2);
+ this->line_raw_size = bplp * planes;
+ this->line_buffer_extra = 2; // room enough for padding
+
+ if (this->comp_type || this->bpp == 24)
+ {
+ // allocates room for 24 bpp packing/unpacking and/or compression
+ // allocates more than enough since compression algoritm can be ineficient
+ this->line_buffer_extra += 2*this->line_raw_size;
+ }
+
+ this->version = 5;
+
+ imAttribTable* attrib_table = AttribTable();
+ /* writes the PCX file header */
+
+ unsigned short xmin = 0, ymin = 0;
+ const void* attrib_data = attrib_table->Get("XScreen");
+ if (attrib_data) xmin = *(unsigned short*)attrib_data;
+ attrib_data = attrib_table->Get("YScreen");
+ if (attrib_data) ymin = *(unsigned short*)attrib_data;
+
+ byte_value = PCX_ID;
+ imBinFileWrite(handle, &byte_value, 1, 1); /* identifier */
+ imBinFileWrite(handle, &this->version, 1, 1); /* format version */
+ imBinFileWrite(handle, &this->comp_type, 1, 1); /* compression comp_type */
+ byte_value = (imbyte)(this->bpp/planes);
+ imBinFileWrite(handle, &byte_value, 1, 1); /* bits/pixel/plane */
+ word_value = xmin;
+ imBinFileWrite(handle, &word_value, 1, 2); /* xmin */
+ word_value = ymin;
+ imBinFileWrite(handle, &word_value, 1, 2); /* ymin */
+ word_value = (unsigned short)(this->width - 1) + xmin;
+ imBinFileWrite(handle, &word_value, 1, 2); /* xmax */
+ word_value = (unsigned short)(this->height - 1) + ymin;
+ imBinFileWrite(handle, &word_value, 1, 2); /* ymax */
+
+ unsigned short hdpi = 0, vdpi = 0;
+ attrib_data = attrib_table->Get("ResolutionUnit");
+ if (attrib_data)
+ {
+ char* res_unit = (char*)attrib_data;
+
+ float* xres = (float*)attrib_table->Get("XResolution");
+ float* yres = (float*)attrib_table->Get("YResolution");
+
+ if (imStrEqual(res_unit, "DPC"))
+ {
+ hdpi = (unsigned short)(*xres * 2.54);
+ vdpi = (unsigned short)(*yres * 2.54);
+ }
+ }
+
+ /* write the x resolution */
+ word_value = hdpi;
+ imBinFileWrite(handle, &word_value, 1, 2); /* hdpi */
+
+ /* write the y resolution */
+ word_value = vdpi;
+ imBinFileWrite(handle, &word_value, 1, 2); /* vdpi */
+
+ imBinFileWrite(handle, iPCXDefaultPalette, 3*16, 1); /* 16 colors palette */
+ byte_value = 0;
+ imBinFileWrite(handle, &byte_value, 1, 1); /* reserved */
+ byte_value = (imbyte)planes;
+ imBinFileWrite(handle, &byte_value, 1, 1); /* planes */
+ word_value = bplp;
+ imBinFileWrite(handle, &word_value, 1, 2); /* bytes per line per plane */
+ memset(filler, 0, 54+3*2);
+ imBinFileWrite(handle, filler, 54+3*2, 1); /* palette info, hscreen size, vscreen size, filler */
+
+ /* tests if everything was ok */
+ if (imBinFileError(handle))
+ return IM_ERR_ACCESS;
+
+ return IM_ERR_NONE;
+}
+
+int imFormatPCX::ReadPalette()
+{
+ unsigned char pcx_colors[256 * 3];
+
+ if (this->version == 5 && this->bpp == 1)
+ {
+ pcx_colors[0] = 0; pcx_colors[1] = 0; pcx_colors[2] = 0;
+ pcx_colors[3] = 255; pcx_colors[4] = 255; pcx_colors[5] = 255;
+ }
+ else if (this->version == 5 && this->bpp == 8)
+ {
+ unsigned char ExtPal;
+
+ /* jump to the end of file minus the palette data */
+ imBinFileSeekFrom(handle, -769);
+
+ /* reads palette identifier */
+ imBinFileRead(handle, &ExtPal, 1, 1);
+
+ if (ExtPal != 12)
+ return IM_ERR_ACCESS;
+
+ /* reads palette colors */
+ imBinFileRead(handle, pcx_colors, 768, 1);
+ }
+ else if (this->version == 3)
+ {
+ memcpy(pcx_colors, iPCXDefaultPalette, this->palette_count * 3);
+ }
+ else
+ {
+ /* jump to the begining of the file at the start of the palette data */
+ imBinFileSeekTo(handle, 4+6*2);
+
+ /* reads palette colors */
+ imBinFileRead(handle, pcx_colors, 3 * 16, 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++)
+ {
+ int i = c * 3;
+ this->palette[c] = imColorEncode(pcx_colors[i], pcx_colors[i+1], pcx_colors[i+2]);
+ }
+
+ return IM_ERR_NONE;
+}
+
+int imFormatPCX::WritePalette()
+{
+ unsigned char ExtPal = (unsigned char)12;
+ unsigned char pcx_colors[256 * 3];
+
+ /* convert the color map to the IM format */
+ for (int c = 0; c < this->palette_count; c++)
+ {
+ int i = c * 3;
+ imColorDecode(&pcx_colors[i], &pcx_colors[i+1], &pcx_colors[i+2], this->palette[c]);
+ }
+
+ /* writes the palette identifier */
+ imBinFileWrite(handle, &ExtPal, 1, 1);
+
+ /* writes the color palette */
+ imBinFileWrite(handle, pcx_colors, 256 * 3, 1);
+
+ if (imBinFileError(handle))
+ return IM_ERR_ACCESS;
+
+ return IM_ERR_NONE;
+}
+
+void imFormatPCX::Expand4bpp()
+{
+ int num_bits = 8, WidthDiv8 = (this->width + 7) / 8;
+
+ int line_plane_size = this->line_raw_size / 4;
+ imbyte *in_data = (unsigned char*)this->line_buffer;
+ imbyte *out_data = in_data + this->line_buffer_size+2;
+
+ for (int x = 0; x < WidthDiv8; x++)
+ {
+ imbyte b1 = in_data[x];
+ imbyte b2 = (in_data + line_plane_size)[x];
+ imbyte b3 = (in_data + 2 * line_plane_size)[x];
+ imbyte b4 = (in_data + 3 * line_plane_size)[x];
+
+ if (x == WidthDiv8-1)
+ num_bits = this->width % 8;
+
+ for (int b = 0; b < num_bits; b++)
+ {
+ imbyte byte_value = 0;
+
+ /* If the most significant bit is set... */
+ /* Set the appropriate bit in the higher order nibble */
+ if (b1 & '\x80') byte_value |= 0x01;
+ if (b2 & '\x80') byte_value |= 0x02;
+ if (b3 & '\x80') byte_value |= 0x04;
+ if (b4 & '\x80') byte_value |= 0x08;
+ b1<<=1; b2<<=1; b3<<=1; b4<<=1;
+
+ *out_data++ = byte_value;
+ }
+ }
+
+ memcpy(this->line_buffer, in_data + this->line_buffer_size+2, this->width);
+}
+
+void imFormatPCX::Pack24bpp()
+{
+ imbyte *in_data = (unsigned char*)this->line_buffer;
+ imbyte *out_data = in_data + this->line_buffer_size+2;
+
+ int line_plane_size = this->line_raw_size / 3;
+
+ imbyte *red = in_data;
+ imbyte *green = in_data + line_plane_size;
+ imbyte *blue = in_data + 2*line_plane_size;
+
+ for (int i = 0; i < this->width; i++)
+ {
+ *out_data++ = *red++;
+ *out_data++ = *green++;
+ *out_data++ = *blue++;
+ }
+
+ memcpy(in_data, in_data + this->line_buffer_size+2, this->line_raw_size);
+}
+
+void imFormatPCX::Unpack24bpp()
+{
+ imbyte *in_data = (unsigned char*)this->line_buffer;
+ imbyte *out_data = in_data + this->line_buffer_size+2;
+
+ int line_plane_size = this->line_raw_size / 3;
+
+ imbyte *red = out_data;
+ imbyte *green = out_data + line_plane_size;
+ imbyte *blue = out_data + 2*line_plane_size;
+
+ for (int i = 0; i < this->width; i++)
+ {
+ *red++ = *in_data++;
+ *green++ = *in_data++;
+ *blue++ = *in_data++;
+ }
+
+ memcpy(out_data - (this->line_buffer_size+2), out_data, this->line_raw_size);
+}
+
+int imFormatPCX::ReadImageData(void* data)
+{
+ imCounterTotal(this->counter, this->height, "Reading PCX...");
+
+ imBinFileSeekTo(handle, 128);
+
+ for (int row = 0; row < this->height; row++)
+ {
+ /* read and decompress the data */
+ if (this->comp_type)
+ {
+ if (iPCXDecodeScanLine(handle, (imbyte*)this->line_buffer, this->line_raw_size) == IM_ERR_ACCESS)
+ return IM_ERR_ACCESS;
+ }
+ else
+ {
+ imBinFileRead(handle, this->line_buffer, this->line_raw_size, 1);
+ if (imBinFileError(handle))
+ return IM_ERR_ACCESS;
+ }
+
+ if (this->bpp == 4)
+ Expand4bpp();
+
+ if (this->bpp == 24)
+ Pack24bpp();
+
+ imFileLineBufferRead(this, data, row, 0);
+
+ if (!imCounterInc(this->counter))
+ return IM_ERR_COUNTER;
+ }
+
+ return IM_ERR_NONE;
+}
+
+int imFormatPCX::WriteImageData(void* data)
+{
+ imCounterTotal(this->counter, this->height, "Writing PCX...");
+
+ imBinFileSeekTo(handle, 128);
+
+ imbyte* compressed_buffer = NULL;
+ if (this->comp_type) // 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 == 24)
+ Unpack24bpp();
+
+ /* compress and writes the data */
+ /* the compressed buffer size will probably be diferent from the uncompressed buffer size */
+ if (this->comp_type)
+ {
+ int compressed_size = iPCXEncodeScanLine(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;
+ }
+
+ if (this->bpp == 8)
+ return WritePalette();
+
+ return IM_ERR_NONE;
+}
+
+int imFormatPCX::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;
+}