diff options
Diffstat (limited to 'src/im_format_pnm.cpp')
-rw-r--r-- | src/im_format_pnm.cpp | 502 |
1 files changed, 502 insertions, 0 deletions
diff --git a/src/im_format_pnm.cpp b/src/im_format_pnm.cpp new file mode 100644 index 0000000..1c7832a --- /dev/null +++ b/src/im_format_pnm.cpp @@ -0,0 +1,502 @@ +/** \file + * \brief PNM - Netpbm Portable Image Map + * + * See Copyright Notice in im_lib.h + * $Id: im_format_pnm.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> + +static int iPNMReadNextInteger(imBinFile* handle, int *value) +{ + int c = 0, found = 0; + static char buffer[10]; + + while (!found) + { + imBinFileRead(handle, &buffer[c], 1, 1); + + /* if it's a number increments the number of characters readed */ + if (buffer[c] >= (int)'0' && buffer[c] <= (int)'9') + c++; + else + { + /* if it's not a number and we readed some characters convert them to an integer */ + if (c > 0) + { + buffer[c] = 0; + *value = atoi(buffer); + found = 1; + } + } + + if (imBinFileError(handle) || c > 10) + return 0; + } + + return 1; +} + +/* comments start with '#' after the first \n */ +static int iPNMReadComment(imBinFile* handle, char* comment, int *size) +{ + imbyte byte_value = 0; + + // find the first \n + while(byte_value != '\n') + { + imBinFileRead(handle, &byte_value, 1, 1); + if (imBinFileError(handle)) + return 0; + } + + *size = 0; + + imBinFileRead(handle, &byte_value, 1, 1); + if (imBinFileError(handle)) + return 0; + + if (byte_value == '#') + { + while(byte_value != '\n') + { + imBinFileRead(handle, &byte_value, 1, 1); + if (imBinFileError(handle)) + return 0; + + if (byte_value != '\r') + { + comment[*size] = byte_value; + (*size)++; + } + } + } + else + imBinFileSeekOffset(handle, -1); + + if (*size != 0) + { + comment[*size] = 0; + (*size)++; + } + + return 1; +} + +static const char* iPNMCompTable[2] = +{ + "NONE", + "ASCII" +}; + +class imFormatPNM: public imFormat +{ + imBinFile* handle; /* the binary file handle */ + unsigned char image_type; + + void FixBinary(); + +public: + imFormatPNM() + :imFormat("PNM", + "Netpbm Portable Image Map", + "*.pnm;*.pbm;*.ppm;*.pgm;", + iPNMCompTable, + 2, + 1) + {} + ~imFormatPNM() {} + + 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 imFormatRegisterPNM(void) +{ + imFormatRegister(new imFormatPNM()); +} + +int imFormatPNM::Open(const char* file_name) +{ + unsigned char sig[2]; + + /* opens the binary file for reading */ + handle = imBinFileOpen(file_name); + if (!handle) + return IM_ERR_OPEN; + + /* reads the PNM format identifier */ + imBinFileRead(handle, sig, 2, 1); + if (imBinFileError(handle)) + { + imBinFileClose(handle); + return IM_ERR_ACCESS; + } + + if (sig[0] != 'P' || (sig[1] != '1' && sig[1] != '2' && + sig[1] != '3' && sig[1] != '4' && + sig[1] != '5' && sig[1] != '6')) + { + imBinFileClose(handle); + return IM_ERR_FORMAT; + } + + this->image_type = sig[1]; + this->image_count = 1; // increment this if found image after data + + if (this->image_type == '1' || this->image_type == '2' || this->image_type == '3') + strcpy(this->compression, "ASCII"); + else + strcpy(this->compression, "NONE"); + + return IM_ERR_NONE; +} + +int imFormatPNM::New(const char* file_name) +{ + /* opens the binary file for writing */ + handle = imBinFileNew(file_name); + if (!handle) + return IM_ERR_OPEN; + + this->image_count = 1; + + return IM_ERR_NONE; +} + +void imFormatPNM::Close() +{ + imBinFileClose(handle); +} + +void* imFormatPNM::Handle(int index) +{ + if (index == 0) + return (void*)this->handle; + else + return NULL; +} + +int imFormatPNM::ReadImageInfo(int index) +{ + (void)index; + + switch (this->image_type) + { + case '4': + this->convert_bpp = 1; + case '1': + this->file_color_mode = IM_BINARY; + break; + case '2': + case '5': + this->file_color_mode = IM_GRAY; + break; + case '3': + case '6': + this->file_color_mode = IM_RGB | IM_PACKED; + break; + } + + this->file_color_mode |= IM_TOPDOWN; + + imAttribTable* attrib_table = AttribTable(); + + char comment[4096]; + int size; + if (!iPNMReadComment(handle, comment, &size)) + return IM_ERR_ACCESS; + + if (size) + attrib_table->Set("Description", IM_BYTE, size, comment); + + if (!iPNMReadNextInteger(handle, &this->width)) + return IM_ERR_ACCESS; + + if (!iPNMReadNextInteger(handle, &this->height)) + return IM_ERR_ACCESS; + + if (this->height <= 0 || this->width <= 0) + return IM_ERR_DATA; + + int max_val = 255; + if (this->image_type != '4' && this->image_type != '1') + { + if (!iPNMReadNextInteger(handle, &max_val)) + return IM_ERR_ACCESS; + } + + this->file_data_type = IM_BYTE; + if (max_val > 255) + this->file_data_type = IM_USHORT; + + return IM_ERR_NONE; +} + +int imFormatPNM::WriteImageInfo() +{ + this->file_data_type = this->user_data_type; + this->file_color_mode = imColorModeSpace(this->user_color_mode); + + int plain; + if (imStrEqual(this->compression, "ASCII")) + plain = 1; + else + plain = 0; + + switch (this->file_color_mode) + { + case IM_BINARY: + if (plain) + this->image_type = '1'; + else + { + this->image_type = '4'; + this->convert_bpp = 1; + } + break; + case IM_GRAY: + if (plain) + this->image_type = '2'; + else + this->image_type = '5'; + break; + case IM_RGB: + if (plain) + this->image_type = '3'; + else + this->image_type = '6'; + this->file_color_mode |= IM_PACKED; + break; + } + + this->file_color_mode |= IM_TOPDOWN; + + imBinFilePrintf(handle, "P%c\n", (int)this->image_type); + + if (imBinFileError(handle)) + return IM_ERR_ACCESS; + + imAttribTable* attrib_table = AttribTable(); + + int attrib_size; + const void* attrib_data = attrib_table->Get("Description", NULL, &attrib_size); + if (attrib_data) + { + char* desc = (char*)attrib_data; + int size = 0; + while(size < (attrib_size-1) && (desc[size] != '\r' && desc[size] != '\n')) + size++; + + imBinFileWrite(handle, (void*)"#", 1, 1); + imBinFileWrite(handle, desc, size, 1); + imBinFileWrite(handle, (void*)"\n", 1, 1); + } + + imBinFilePrintf(handle, "%d\n", this->width); + imBinFilePrintf(handle, "%d\n", this->height); + + if (this->image_type != '4' && this->image_type != '1') + { + int max_val = 255; + if (this->file_data_type == IM_USHORT) + max_val = 65535; + + imBinFilePrintf(handle, "%d\n", max_val); + } + + /* tests if everything was ok */ + if (imBinFileError(handle)) + return IM_ERR_ACCESS; + + return IM_ERR_NONE; +} + +void imFormatPNM::FixBinary() +{ + unsigned char* buf = (unsigned char*)this->line_buffer; + for (int b = 0; b < this->line_buffer_size; b++) + { + *buf = ~(*buf); + buf++; + } +} + +int imFormatPNM::ReadImageData(void* data) +{ + imCounterTotal(this->counter, this->height, "Reading PNM..."); + + int line_count = imImageLineCount(this->width, this->file_color_mode); + + int line_raw_size; + if (this->image_type == '4') + line_raw_size = imFileLineSizeAligned(this->width, 1, 1); + else + line_raw_size = imImageLineSize(this->width, this->file_color_mode, this->file_data_type); + + int plain = 0; + if (this->image_type == '1' || this->image_type == '2' || this->image_type == '3') + plain = 1; + + for (int row = 0; row < this->height; row++) + { + if (plain) + { + int value; + for (int col = 0; col < line_count; col++) + { + if (!iPNMReadNextInteger(handle, &value)) + return IM_ERR_ACCESS; + + if (this->image_type == '1' && value < 2) + value = 1 - value; + + if (this->file_data_type == IM_USHORT) + ((imushort*)this->line_buffer)[col] = (imushort)value; + else + ((imbyte*)this->line_buffer)[col] = (unsigned char)value; + } + } + else + { + imBinFileRead(handle, this->line_buffer, line_raw_size, 1); + + if (imBinFileError(handle)) + return IM_ERR_ACCESS; + + if (this->image_type == '4') + FixBinary(); + } + + imFileLineBufferRead(this, data, row, 0); + + if (!imCounterInc(this->counter)) + return IM_ERR_COUNTER; + } + + // try to find another image, ignore errors from here + + /* reads the PNM format identifier */ + unsigned char sig[2]; + imBinFileRead(handle, sig, 2, 1); + if (imBinFileError(handle)) + return IM_ERR_NONE; + + if (sig[0] != 'P' || (sig[1] != '1' && sig[1] != '2' && + sig[1] != '3' && sig[1] != '4' && + sig[1] != '5' && sig[1] != '6')) + return IM_ERR_NONE; + + this->image_type = sig[1]; + this->image_count++; + + if (this->image_type == '1' || this->image_type == '2' || this->image_type == '3') + strcpy(this->compression, "ASCII"); + else + strcpy(this->compression, "NONE"); + + return IM_ERR_NONE; +} + +int imFormatPNM::WriteImageData(void* data) +{ + imCounterTotal(this->counter, this->height, "Writing PNM..."); + + int line_count = imImageLineCount(this->width, this->file_color_mode); + + int line_raw_size; + if (this->image_type == '4') + line_raw_size = imFileLineSizeAligned(this->width, 1, 1); + else + line_raw_size = imImageLineSize(this->width, this->file_color_mode, this->file_data_type); + + int plain = 0; + if (this->image_type == '1' || this->image_type == '2' || this->image_type == '3') + plain = 1; + + for (int row = 0; row < this->height; row++) + { + imFileLineBufferWrite(this, data, row, 0); + + if (plain) + { + int line_size = 0; + for (int col = 0; col < line_count; col++) + { + int value; + if (this->file_data_type == IM_USHORT) + value = ((imushort*)this->line_buffer)[col]; + else + value = ((imbyte*)this->line_buffer)[col]; + + if (this->image_type == '1' && value < 2) + value = 1 - value; + + int write_size = imBinFilePrintf(handle, "%d ", value); + if (!write_size) + return IM_ERR_ACCESS; + + line_size += write_size; + + // No line should be longer than 70 characters. + if (line_size > 60 || col == line_count-1) + { + line_size = 0; + imBinFileWrite(handle, (void*)"\n", 1, 1); + } + } + } + else + { + if (this->image_type == '4') + FixBinary(); + + imBinFileWrite(handle, this->line_buffer, line_raw_size, 1); + } + + if (imBinFileError(handle)) + return IM_ERR_ACCESS; + + if (!imCounterInc(this->counter)) + return IM_ERR_COUNTER; + } + + return IM_ERR_NONE; +} + +int imFormatPNM::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, "ASCII")) + return IM_ERR_COMPRESS; + + return IM_ERR_NONE; +} |