diff options
author | scuri <scuri> | 2008-10-17 06:10:15 +0000 |
---|---|---|
committer | scuri <scuri> | 2008-10-17 06:10:15 +0000 |
commit | 5a422aba704c375a307a902bafe658342e209906 (patch) | |
tree | 5005011e086bb863d8fb587ad3319bbec59b2447 /src/im_format_tga.cpp |
First commit - moving from LuaForge to SourceForge
Diffstat (limited to 'src/im_format_tga.cpp')
-rw-r--r-- | src/im_format_tga.cpp | 1104 |
1 files changed, 1104 insertions, 0 deletions
diff --git a/src/im_format_tga.cpp b/src/im_format_tga.cpp new file mode 100644 index 0000000..8e8e176 --- /dev/null +++ b/src/im_format_tga.cpp @@ -0,0 +1,1104 @@ +/** \file + * \brief TGA - Truevision Graphics Adapter File + * + * See Copyright Notice in im_lib.h + * $Id: im_format_tga.cpp,v 1.1 2008/10/17 06:10:16 scuri Exp $ + */ + +#include "im_format.h" +#include "im_util.h" +#include "im_format_all.h" +#include "im_counter.h" +#include "im_math.h" + +#include "im_binfile.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <memory.h> +#include <time.h> +#include <math.h> + + +/* +|--------|--------|------------------------------------------------------------| +| 0 | 1 | Number of Characters in Identification Field. | +| | | This field is a one-byte unsigned integer, specifying | +| | | the length of the Image Identification Field. Its range | +| | | is 0 to 255. A value of 0 means that no Image | +| | | Identification Field is included. | +|--------|--------|------------------------------------------------------------| +| 1 | 1 | Color Map Type. | +|--------|--------|------------------------------------------------------------| +| 2 | 1 | Image Type Code. | +|--------|--------|------------------------------------------------------------| +| 3 | 5 | Color Map Specification. | +| 3 | 2 | Color Map Origin. | +| | | Integer ( lo-hi ) index of first color map entry. | +| 5 | 2 | Color Map Length. | +| | | Integer ( lo-hi ) count of color map entries. | +| 7 | 1 | Color Map Entry Size. | +| | | Number of bits in each color map entry. 16 for | +| | | the Targa 16, 24 for the Targa 24, 32 for the Targa 32. | +|--------|--------|------------------------------------------------------------| +| 8 | 10 | Image Specification. | +| 8 | 2 | X Origin of Image. | +| | | Integer ( lo-hi ) X coordinate of the lower left corner | +| | | of the image. | +| 10 | 2 | Y Origin of Image. | +| | | Integer ( lo-hi ) Y coordinate of the lower left corner | +| | | of the image. | +| 12 | 2 | Width of Image. | +| | | Integer ( lo-hi ) width of the image in pixels. | +| 14 | 2 | Height of Image. | +| | | Integer ( lo-hi ) height of the image in pixels. | +| 16 | 1 | Image Pixel Size. | +| | | Number of bits in a stored pixel index. | +| 17 | 1 | Image Descriptor Byte. | +| | | Bits 3-0 - number of attribute bits associated with each | +| | | pixel. | +| | | Bit 4 - reserved. Must be set to 0. | +| | | Bit 5 - screen origin bit. | +| | | 0 = Origin in lower left-hand corner. | +| | | 1 = Origin in upper left-hand corner. | +| | | Must be 0 for Truevision images. | +| | | Bits 7-6 - Data storage interleaving flag. | +| | | 00 = non-interleaved. | +| | | 01 = two-way (even/odd) interleaving. | +| | | 10 = four way interleaving. | +| | | 11 = reserved. | +| | | This entire byte should be set to 0. Don't ask me. | +|--------|--------|------------------------------------------------------------| +| 18 | varies | Image Identification Field. | +| | | Contains a free-form identification field of the length | +| | | specified in byte 1 of the image record. It's usually | +| | | omitted ( length in byte 1 = 0 ), but can be up to 255 | +| | | characters. If more identification information is | +| | | required, it can be stored after the image data. | +|--------|--------|------------------------------------------------------------| + +Extension Area: + +* The inclusion of a scaled-down “postage stamp” copy of the image +* Date and Time of image file creation +* Author Name +* Author Comments +* Job Name +* Job Accumulated Time +* Gamma Value +* Correct Color LUT +* Pixel Aspect Ratio +* Scan Line Offset Table +* Key Color +* Software Package Name and Version Number +* Developer Definable Areas +* Attribute (Alpha) channel Type +* The ability for simple expansion +*/ + +static int iTGADecodeScanLine(imBinFile* handle, imbyte *DecodedBuffer, int width, int pixel_size) +{ + int i=0; + unsigned char runcount; /* repetition count field */ + imbyte pixel_buffer[4]; + + while (i < width) + { + imBinFileRead(handle, &runcount, 1, 1); + + if (runcount & 0x80) + { + imBinFileRead(handle, pixel_buffer, pixel_size, 1); + runcount &= 0x7F; + + if (imBinFileError(handle)) + return IM_ERR_ACCESS; + + runcount++; + while (runcount-- && i < width) + { + memcpy(DecodedBuffer, pixel_buffer, pixel_size); + i++; + DecodedBuffer += pixel_size; + } + } + else + { + runcount++; + while (runcount-- && i < width) + { + imBinFileRead(handle, pixel_buffer, pixel_size, 1); + memcpy(DecodedBuffer, pixel_buffer, pixel_size); + i++; + DecodedBuffer += pixel_size; + } + + if (imBinFileError(handle)) + return IM_ERR_ACCESS; + } + } + + return IM_ERR_NONE; +} + +static inline int iTGAEqualPixel(const imbyte* Buffer1, const imbyte* Buffer2, int pixel_size) +{ + while(pixel_size--) + { + if (*Buffer1++ != *Buffer2++) + return 0; + } + return 1; +} + +static int iTGAEncodeScanLine(imbyte* EncodedBuffer, const imbyte* DecodedBuffer, int width, int pixel_size) +{ + imbyte pixel_buffer[4]; + unsigned char runcount; /* Length of encoded pixel run */ + int x = 0; /* Index into uncompressed data buffer */ + imbyte* StartBuffer = EncodedBuffer; + + while (x < width) + { + runcount = 1; + memcpy(pixel_buffer, &DecodedBuffer[x*pixel_size], pixel_size); + + // count equal pixels + while (x+runcount < width && runcount < 128 && + iTGAEqualPixel(pixel_buffer, &DecodedBuffer[(x+runcount)*pixel_size], pixel_size)) + runcount++; + + if (runcount == 1) + { + // count different pixels + while (x+runcount+1 < width && runcount < 128) + { + memcpy(pixel_buffer, &DecodedBuffer[(x+runcount)*pixel_size], pixel_size); + + if (!iTGAEqualPixel(pixel_buffer, &DecodedBuffer[(x+runcount+1)*pixel_size], pixel_size)) + runcount++; + else + break; + } + + *EncodedBuffer++ = (imbyte)(runcount-1); + + memcpy(EncodedBuffer, &DecodedBuffer[x*pixel_size], runcount*pixel_size); + EncodedBuffer += runcount*pixel_size; + } + else + { + *EncodedBuffer++ = (imbyte)(0x80 | (runcount-1)); + + memcpy(EncodedBuffer, pixel_buffer, pixel_size); + EncodedBuffer += pixel_size; + } + + x += runcount; + } + + return EncodedBuffer-StartBuffer; /* Return the number of unsigned chars written to buffer */ +} + +static const char* iTGACompTable[2] = +{ + "NONE", + "RLE" +}; + +class imFormatTGA: public imFormat +{ + imBinFile* handle; /* the binary file handle */ + unsigned char id_lenght; + unsigned char map_type, image_type, map_bpp, bpp; + + int ReadPalette(); + int WritePalette(); + void FixRGB(); + int LoadExtensionArea(); + int SaveExtensionArea(); + +public: + imFormatTGA() + :imFormat("TGA", + "Truevision Graphics Adapter File", + "*.tga;*.icb;*.vst;*.tpic;", + iTGACompTable, + 2, + 0) + {} + ~imFormatTGA() {} + + 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 imFormatRegisterTGA(void) +{ + imFormatRegister(new imFormatTGA()); +} + +int imFormatTGA::Open(const char* file_name) +{ + /* opens the binary file for reading with intel byte order */ + handle = imBinFileOpen(file_name); + if (!handle) + return IM_ERR_OPEN; + + imBinFileByteOrder(handle, IM_LITTLEENDIAN); + + imBinFileRead(handle, &this->id_lenght, 1, 1); + imBinFileRead(handle, &this->map_type, 1, 1); + imBinFileRead(handle, &this->image_type, 1, 1); + + if (imBinFileError(handle)) + { + imBinFileClose(handle); + return IM_ERR_ACCESS; + } + + if (this->image_type != 1 && this->image_type != 2 && this->image_type != 3 && + this->image_type != 9 && this->image_type != 10 && this->image_type != 11) + { + imBinFileClose(handle); + return IM_ERR_FORMAT; + } + + if (this->map_type != 0 && this->map_type != 1) + { + imBinFileClose(handle); + return IM_ERR_FORMAT; + } + + if (this->map_type == 0 && (this->image_type == 1 || this->image_type == 9)) + { + imBinFileClose(handle); + return IM_ERR_FORMAT; + } + + if (this->image_type == 9 || this->image_type == 10 || this->image_type == 11) + strcpy(this->compression, "RLE"); + else + strcpy(this->compression, "NONE"); + + this->image_count = 1; + + return IM_ERR_NONE; +} + +int imFormatTGA::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 imFormatTGA::Close() +{ + imBinFileClose(handle); +} + +void* imFormatTGA::Handle(int index) +{ + if (index == 0) + return (void*)this->handle; + else + return NULL; +} + +int imFormatTGA::ReadImageInfo(int index) +{ + (void)index; + unsigned char byte_value; + unsigned short word_value; + + this->file_data_type = IM_BYTE; + + if (this->image_type == 1 || this->image_type == 9) + this->file_color_mode = IM_MAP; + else if (this->image_type == 2 || this->image_type == 10) + { + this->file_color_mode = IM_RGB; + this->file_color_mode |= IM_PACKED; + } + else if (this->image_type == 3 || this->image_type == 11) + this->file_color_mode = IM_GRAY; + else + return IM_ERR_DATA; + + if (this->map_type == 0) + imBinFileSeekOffset(handle, 5); // jump color map information + else + { + /* jump 2 bytes (first entry index) */ + imBinFileSeekOffset(handle, 2); + + imBinFileRead(handle, &word_value, 1, 2); + this->palette_count = word_value; + + imBinFileRead(handle, &this->map_bpp, 1, 1); + + if (this->map_bpp == 15) this->map_bpp = 16; + + if (this->map_bpp != 16 && this->map_bpp != 24 && this->map_bpp != 32) + return IM_ERR_DATA; + } + + /* jump 4 bytes (X-Origin, Y-Origin) */ + unsigned short xmin, ymin; + imBinFileRead(handle, &xmin, 1, 2); + imBinFileRead(handle, &ymin, 1, 2); + + if (imBinFileError(handle)) + return IM_ERR_ACCESS; + + imAttribTable* attrib_table = AttribTable(); + + if (xmin && ymin) + { + attrib_table->Set("XScreen", IM_USHORT, 1, &xmin); + attrib_table->Set("YScreen", IM_USHORT, 1, &ymin); + } + + /* reads the image width */ + imBinFileRead(handle, &word_value, 1, 2); + this->width = word_value; + + /* reads the image height */ + imBinFileRead(handle, &word_value, 1, 2); + this->height = word_value; + + if (imBinFileError(handle)) + return IM_ERR_ACCESS; + + imBinFileRead(handle, &this->bpp, 1, 1); + + if (this->bpp > 8 && imColorModeSpace(this->file_color_mode) != IM_RGB) + return IM_ERR_DATA; + + if (this->bpp == 15) this->bpp = 16; + + if (this->bpp != 8 && this->bpp != 16 && + this->bpp != 24 && this->bpp != 32) + return IM_ERR_DATA; + + if (this->bpp == 32) + this->file_color_mode |= IM_ALPHA; + + // image descriptor + imBinFileRead(handle, &byte_value, 1, 1); + + if (byte_value & 0x20) + this->file_color_mode |= IM_TOPDOWN; + + // image ID + if (this->id_lenght) + { + char desc[256]; + imBinFileRead(handle, desc, this->id_lenght, 1); + desc[this->id_lenght] = 0; + attrib_table->Set("Title", IM_BYTE, this->id_lenght+1, desc); + } + + if (imBinFileError(handle)) + return IM_ERR_ACCESS; + + if (this->map_type) + { + if (!ReadPalette()) + return IM_ERR_ACCESS; + } + + long cur_offset = imBinFileTell(handle); + imBinFileSeekFrom(handle, -18); + char ext_sig[18]; + imBinFileRead(handle, ext_sig, 18, 1); + if (ext_sig[17] == 0 && imStrEqual(ext_sig, "TRUEVISION-XFILE.")) + { + if (!LoadExtensionArea()) + return IM_ERR_ACCESS; + } + imBinFileSeekTo(handle, cur_offset); + + return IM_ERR_NONE; +} + +int imFormatTGA::WriteImageInfo() +{ + unsigned char byte_value; + unsigned short word_value; + + this->map_bpp = 0; + this->map_type = 0; + + this->file_color_mode = imColorModeSpace(this->user_color_mode); + switch (this->file_color_mode) + { + case IM_BINARY: + this->convert_bpp = -1; // expand 1 to 255 + case IM_GRAY: + this->bpp = 8; + if (imStrEqual(this->compression, "RLE")) + this->image_type = 11; + else + this->image_type = 3; + break; + case IM_MAP: + this->bpp = 8; + this->map_bpp = 24; + this->map_type = 1; + if (imStrEqual(this->compression, "RLE")) + this->image_type = 9; + else + this->image_type = 1; + break; + case IM_RGB: + this->bpp = 24; + this->file_color_mode |= IM_PACKED; + if (imStrEqual(this->compression, "RLE")) + this->image_type = 10; + else + this->image_type = 2; + break; + } + + if (this->image_type > 3) + { + // allocates more than enough since compression algoritm can be ineficient + this->line_buffer_extra += 2*this->width*imColorModeDepth(this->file_color_mode); + } + + imAttribTable* attrib_table = AttribTable(); + + /* writes the TGA file header */ + + int length = 0; + const char* desc_attrib = (const char*)attrib_table->Get("Title", NULL, &length); + if (desc) + { + if (length > 255) + this->id_lenght = 255; + else + this->id_lenght = (imbyte)length; + } + else + this->id_lenght = 0; + + /* IDLength */ + imBinFileWrite(handle, &this->id_lenght, 1, 1); + + /* Color Map Type */ + imBinFileWrite(handle, &this->map_type, 1, 1); + + /* Image Type */ + imBinFileWrite(handle, &this->image_type, 1, 1); + + /* Color Map Specification - 1st entry index */ + word_value = 0; + imBinFileWrite(handle, &word_value, 1, 2); + + /* Color map length */ + word_value = (unsigned short) this->palette_count; + imBinFileWrite(handle, &word_value, 1, 2); + + /* Color Map Entry size */ + byte_value = this->map_type? this->map_bpp: (imbyte)0; + imBinFileWrite(handle, &byte_value, 1, 1); + + if (imBinFileError(handle)) + return IM_ERR_ACCESS; + + 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; + + /* X-orign of image */ + word_value = xmin; + imBinFileWrite(handle, &word_value, 1, 2); + + /* Y-orign of image */ + word_value = ymin; + imBinFileWrite(handle, &word_value, 1, 2); + + /* Image Width */ + word_value = (imushort)this->width; + imBinFileWrite(handle, &word_value, 1, 2); + + /* Image Height */ + word_value = (imushort)this->height; + imBinFileWrite(handle, &word_value, 1, 2); + + /* Pixel Depth */ + imBinFileWrite(handle, &this->bpp, 1, 1); + + /* Image Descriptor */ + byte_value = 0x00; + imBinFileWrite(handle, &byte_value, 1, 1); + + /* image ID */ + if (this->id_lenght) + { + if (length > 255) + { + imBinFileWrite(handle, (void*)desc_attrib, 254, 1); + byte_value = 0x00; + imBinFileWrite(handle, &byte_value, 1, 1); + } + else + imBinFileWrite(handle, (void*)desc_attrib, this->id_lenght, 1); + } + + /* tests if everything was ok */ + if (imBinFileError(handle)) + return IM_ERR_ACCESS; + + if (this->map_type) + { + if (!WritePalette()) + return IM_ERR_ACCESS; + } + + return IM_ERR_NONE; +} + +static long iTGARGB2Color(int c, unsigned char *colors, int map_bpp) +{ + unsigned char r,g,b; + + if (map_bpp == 16) + { + unsigned short word_value = ((unsigned short*)colors)[c]; + + r = (imbyte)(((word_value & 0x7C00) >> 10)*8); + g = (imbyte)(((word_value & 0x03E0) >> 5)*8); + b = (imbyte)( (word_value & 0x001F) *8); + } + else // 24 or 32 + { + int i = c * (map_bpp / 8); + + r = colors[i+2]; + g = colors[i+1]; + b = colors[i]; + } + + return imColorEncode(r, g, b); +} + +int imFormatTGA::ReadPalette() +{ + int map_size = imFileLineSizeAligned(this->palette_count, this->map_bpp, 1); + unsigned char* tga_colors = (unsigned char*) malloc(map_size); + + /* reads the color palette */ + imBinFileRead(handle, tga_colors, map_size, 1); + if (imBinFileError(handle)) + return 0; + + if (imBinCPUByteOrder() == IM_BIGENDIAN && this->map_bpp == 16) + imBinSwapBytes2(tga_colors, map_size/2); + + /* convert the color map to the IM format */ + for (int c = 0; c < this->palette_count; c++) + this->palette[c] = iTGARGB2Color(c, tga_colors, this->map_bpp); + + free(tga_colors); + + return 1; +} + +int imFormatTGA::WritePalette() +{ + unsigned char tga_color[256*3]; + + /* convert the color map from the IM format */ + for (int c = 0; c < this->palette_count; c++) + { + int i = 3*c; + imColorDecode(&tga_color[i+2], &tga_color[i+1], &tga_color[i], this->palette[c]); + } + + /* writes the color palette */ + imBinFileWrite(handle, tga_color, this->palette_count * 3, 1); + + if (imBinFileError(handle)) + return 0; + + return 1; +} + +int imFormatTGA::LoadExtensionArea() +{ + unsigned int dword_value; + imBinFileSeekFrom(handle, -26); + + // extension offset + imBinFileRead(handle, &dword_value, 1, 4); + if (imBinFileError(handle)) + return 0; + + imBinFileSeekTo(handle, dword_value); + if (imBinFileError(handle)) + return 0; + + unsigned short word_value; + imbyte buffer[512]; + imAttribTable* attrib_table = AttribTable(); + + // extension size + imBinFileSeekOffset(handle, 2); + + // author name + imBinFileRead(handle, buffer, 41, 1); + if (buffer[0] != 0) + attrib_table->Set("Author", IM_BYTE, imStrNLen((char*)buffer, 41)+1, buffer); + + // author comments + imBinFileRead(handle, buffer, 324, 1); + if (buffer[0] != 0) + { + int size1 = imStrNLen((char*)buffer, 81); + for (int i = 1; i < 4; i++) + { + int sizei = imStrNLen((char*)buffer + i*81, 81); + if (sizei) + { + memcpy(buffer + size1, buffer + i*81, sizei); + size1 += sizei; + } + } + buffer[size1] = 0; + + attrib_table->Set("Description", IM_BYTE, size1+1, buffer); + } + + if (imBinFileError(handle)) + return 0; + + { + tm ttm; + ttm.tm_wday = 0; + ttm.tm_yday = 0; + ttm.tm_isdst = -1; + + int valid = 0; + imBinFileRead(handle, &word_value, 1, 2); // moth + ttm.tm_mon = word_value-1; + if (word_value) valid = 1; + imBinFileRead(handle, &word_value, 1, 2); // day + ttm.tm_mday = word_value; + if (word_value) valid = 1; + imBinFileRead(handle, &word_value, 1, 2); // year + ttm.tm_year = word_value-1900; + if (word_value) valid = 1; + imBinFileRead(handle, &word_value, 1, 2); // hour + ttm.tm_hour = word_value; + imBinFileRead(handle, &word_value, 1, 2); // minute + ttm.tm_min = word_value; + imBinFileRead(handle, &word_value, 1, 2); // seconds + ttm.tm_sec = word_value; + + if (imBinFileError(handle)) + return 0; + + if (valid) + { + time_t tt = mktime(&ttm); + char* str = ctime(&tt); + if (str) + { + int size = strlen(str); + str[size-1] = 0; // remove "\n" + attrib_table->Set("DateTimeModified", IM_BYTE, size, str); + } + } + } + + // job name + imBinFileRead(handle, buffer, 41, 1); + if (buffer[0] != 0) + attrib_table->Set("JobName", IM_BYTE, imStrNLen((char*)buffer, 41)+1, buffer); + + // job time + imBinFileSeekOffset(handle, 6); + + // Software + imBinFileRead(handle, buffer, 41, 1); + if (buffer[0] != 0) + attrib_table->Set("Software", IM_BYTE, imStrNLen((char*)buffer, 41)+1, buffer); + + if (imBinFileError(handle)) + return 0; + + // Software Version + imBinFileRead(handle, &word_value, 1, 2); + if (word_value) + { + int size = sprintf((char*)buffer, "%f", (double)word_value / 100.0); + imBinFileRead(handle, &buffer[size], 1, 1); + buffer[size+1] = 0; + attrib_table->Set("SoftwareVersion", IM_BYTE, size+1, buffer); + } + + // key color, aspect ratio + imBinFileSeekOffset(handle, 8); + + // gamma + imBinFileRead(handle, &word_value, 1, 2); // num + if (word_value) + { + float gamma = (float)word_value; + imBinFileRead(handle, &word_value, 1, 2); // den + if (word_value) + { + gamma /= (float)word_value; + attrib_table->Set("Gamma", IM_FLOAT, 1, &gamma); + } + } + + if (imBinFileError(handle)) + return 0; + + return 1; +} + +static void iGetRational(float fvalue, int *num, int *den) +{ + if (floorf(fvalue) == fvalue) + { + *num = (int)floorf(fvalue); + *den = 1; + return; + } + + float ivalue = 1.0f/fvalue; + if (floorf(ivalue) == ivalue) + { + *den = (int)floorf(ivalue); + *num = 1; + return; + } + + *den = 1; + if (fvalue > 0) + { + while (fvalue < 1L<<(31-3) && *den < 1L<<(31-3)) + { + fvalue *= 1<<3; + *den *= 1<<3; + } + } + + *num = imRound(fvalue); +} + +int imFormatTGA::SaveExtensionArea() +{ + unsigned int dword_value; + unsigned short word_value; + + // get offset before write + long ext_offset = imBinFileTell(handle); + + imbyte buffer[512]; + memset(buffer, 0, 512); + + imAttribTable* attrib_table = AttribTable(); + + // extension size + word_value = 495; + imBinFileWrite(handle, &word_value, 1, 2); + + // author name + int attrib_size; + const void* attrib_data = attrib_table->Get("Author", NULL, &attrib_size); + if (attrib_data) + { + int size = attrib_size > 41? 40: attrib_size; + imBinFileWrite(handle, (void*)attrib_data, size, 1); + if (size < 41) + imBinFileWrite(handle, buffer, 41-size, 1); + } + else + imBinFileWrite(handle, buffer, 41, 1); + + // author comments + attrib_data = attrib_table->Get("Description", NULL, &attrib_size); + if (attrib_data) + { + int size = 0, size2 = 0, i = 0; + while(attrib_size && i < 4) + { + int line_size; + if (attrib_size > 81) + line_size = 80; + else + line_size = attrib_size; + + memcpy(buffer + size, (imbyte*)attrib_data + size2, line_size); + + attrib_size -= line_size; + size2 += line_size; + size += line_size; + i++; + + int remain = 81-line_size; + if (remain) + { + memset(buffer + size, 0, remain); + size += remain; + } + } + + imBinFileWrite(handle, buffer, 324, 1); + memset(buffer, 0, 512); + } + else + imBinFileWrite(handle, buffer, 324, 1); + + if (imBinFileError(handle)) + return 0; + + attrib_data = attrib_table->Get("DateTimeModified"); + if (attrib_data) + { + time_t cur_time; + time(&cur_time); + tm* ttm = localtime(&cur_time); + + word_value = (imushort)ttm->tm_mon+1; + imBinFileWrite(handle, &word_value, 1, 2); // moth + word_value = (imushort)ttm->tm_mday; + imBinFileWrite(handle, &word_value, 1, 2); // day + word_value = (imushort)ttm->tm_year+1900; + imBinFileWrite(handle, &word_value, 1, 2); // year + word_value = (imushort)ttm->tm_hour; + imBinFileWrite(handle, &word_value, 1, 2); // hour + word_value = (imushort)ttm->tm_min; + imBinFileWrite(handle, &word_value, 1, 2); // minute + word_value = (imushort)ttm->tm_sec; + imBinFileWrite(handle, &word_value, 1, 2); // seconds + + if (imBinFileError(handle)) + return 0; + } + else + imBinFileWrite(handle, buffer, 12, 1); + + // job name + attrib_data = attrib_table->Get("JobName", NULL, &attrib_size); + if (attrib_data) + { + int size = attrib_size > 41? 40: attrib_size; + imBinFileWrite(handle, (void*)attrib_data, size, 1); + if (size < 41) + imBinFileWrite(handle, buffer, 41-size, 1); + } + else + imBinFileWrite(handle, buffer, 41, 1); + + // job time + imBinFileWrite(handle, buffer, 6, 1); + + // Software + attrib_data = attrib_table->Get("Software", NULL, &attrib_size); + if (attrib_data) + { + int size = attrib_size > 41? 40: attrib_size; + imBinFileWrite(handle, (void*)attrib_data, size, 1); + if (size < 41) + imBinFileWrite(handle, buffer, 41-size, 1); + } + else + imBinFileWrite(handle, buffer, 41, 1); + + if (imBinFileError(handle)) + return 0; + + // Software Version, key color, aspect ratio + imBinFileWrite(handle, buffer, 11, 1); + + // gamma + attrib_data = attrib_table->Get("Gamma"); + if (attrib_data) + { + float gamma = *(float*)attrib_data; + + int num, den; + iGetRational(gamma, &num, &den); + + word_value = (imushort)num; + imBinFileWrite(handle, &word_value, 1, 2); // num + word_value = (imushort)den; + imBinFileWrite(handle, &word_value, 1, 2); // den + } + else + imBinFileWrite(handle, buffer, 4, 1); + + // Color Correction, Postage Stamp, Scanline Offset, Attributes Type + imBinFileWrite(handle, buffer, 13, 1); + + // FOOTER + + // extension offset + dword_value = ext_offset; + imBinFileWrite(handle, &dword_value, 1, 4); + + // Developer Directory Offset + imBinFileWrite(handle, buffer, 4, 1); + + // signature, reserved, zero string terminator + imBinFileWrite(handle, (void*)"TRUEVISION-XFILE.\0", 18, 1); + + if (imBinFileError(handle)) + return 0; + + return 1; +} + +void imFormatTGA::FixRGB() +{ + int x; + imbyte* byte_data = (imbyte*)this->line_buffer; + + if (this->bpp == 16) + { + /* inverts the WORD values if not intel */ + if (imBinCPUByteOrder() == IM_BIGENDIAN) + imBinSwapBytes2(this->line_buffer, this->width); + + imushort* word_data = (imushort*)this->line_buffer; + + // from end to start + for (x = this->width-1; x >= 0; x--) + { + imushort word_value = word_data[x]; + int c = x*3; + byte_data[c] = (imbyte)(((word_value & 0x7C00) >> 10)*8); + byte_data[c+1] = (imbyte)(((word_value & 0x03E0) >> 5)*8); + byte_data[c+2] = (imbyte)( (word_value & 0x001F) *8); + } + } + else // 24 and 32 + { + // convert BGR <-> RGB + // convert BGRA <-> RGBA + imbyte* byte_data = (imbyte*)this->line_buffer; + int planes = this->bpp/8; + for (x = 0; x < this->width; x++) + { + int c = x*planes; + imbyte temp = byte_data[c]; // swap R and B + byte_data[c] = byte_data[c+2]; + byte_data[c+2] = temp; + } + } +} + +int imFormatTGA::ReadImageData(void* data) +{ + imCounterTotal(this->counter, this->height, "Reading TGA..."); + + int line_size = this->line_buffer_size; + if (this->bpp == 16) + line_size = this->width*2; + + for (int row = 0; row < this->height; row++) + { + if (this->image_type > 3) + { + if (iTGADecodeScanLine(handle, (imbyte*)this->line_buffer, this->width, this->bpp/8) == IM_ERR_ACCESS) + return IM_ERR_ACCESS; + } + else + { + imBinFileRead(handle, this->line_buffer, line_size, 1); + if (imBinFileError(handle)) + 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 imFormatTGA::WriteImageData(void* data) +{ + imCounterTotal(this->counter, this->height, "Writing TGA..."); + + imbyte* compressed_buffer = NULL; + if (this->image_type > 3) // point to the extra buffer + compressed_buffer = (imbyte*)this->line_buffer + this->line_buffer_size; + + for (int row = 0; row < this->height; row++) + { + imFileLineBufferWrite(this, data, row, 0); + + if (this->bpp > 8) + FixRGB(); + + if (this->image_type > 3) + { + int compressed_size = iTGAEncodeScanLine(compressed_buffer, (imbyte*)this->line_buffer, this->width, this->bpp/8); + imBinFileWrite(handle, compressed_buffer, compressed_size, 1); + } + else + { + imBinFileWrite(handle, this->line_buffer, this->line_buffer_size, 1); + } + + if (imBinFileError(handle)) + return IM_ERR_ACCESS; + + if (!imCounterInc(this->counter)) + return IM_ERR_COUNTER; + } + + if (!SaveExtensionArea()) + return IM_ERR_ACCESS; + + return IM_ERR_NONE; +} + +int imFormatTGA::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; +} |