diff options
author | George Hazan <george.hazan@gmail.com> | 2024-11-04 14:48:38 +0300 |
---|---|---|
committer | George Hazan <george.hazan@gmail.com> | 2024-11-04 14:48:38 +0300 |
commit | 2829883e8d5116bb7f0983022e08791aa1aa47c3 (patch) | |
tree | 6ea046a33aa32d0e921fa839fcdbaa02e1c819af /libs | |
parent | 71a1103f8a902a82f2215932ecb3c1a06e74718e (diff) |
FreeImage: no need to rebuild each time + TARGA support
Diffstat (limited to 'libs')
-rw-r--r-- | libs/freeimage/freeimage.vcxproj | 3 | ||||
-rw-r--r-- | libs/freeimage/freeimage.vcxproj.filters | 3 | ||||
-rw-r--r-- | libs/freeimage/src/FreeImage.h | 3 | ||||
-rw-r--r-- | libs/freeimage/src/FreeImage/Plugin.cpp | 2 | ||||
-rw-r--r-- | libs/freeimage/src/FreeImage/PluginTARGA.cpp | 1595 | ||||
-rw-r--r-- | libs/win32/FreeImage.lib | bin | 0 -> 126990 bytes | |||
-rw-r--r-- | libs/win64/FreeImage.lib | bin | 0 -> 121284 bytes |
7 files changed, 1604 insertions, 2 deletions
diff --git a/libs/freeimage/freeimage.vcxproj b/libs/freeimage/freeimage.vcxproj index d95a55ad2a..98641cce37 100644 --- a/libs/freeimage/freeimage.vcxproj +++ b/libs/freeimage/freeimage.vcxproj @@ -161,6 +161,9 @@ <ClCompile Include="src\FreeImage\PluginPNG.cpp"> <PrecompiledHeaderFile>../stdafx.h</PrecompiledHeaderFile> </ClCompile> + <ClCompile Include="src\FreeImage\PluginTARGA.cpp"> + <PrecompiledHeaderFile>../stdafx.h</PrecompiledHeaderFile> + </ClCompile> <ClCompile Include="src\FreeImage\PluginWebP.cpp"> <PrecompiledHeaderFile>../stdafx.h</PrecompiledHeaderFile> </ClCompile> diff --git a/libs/freeimage/freeimage.vcxproj.filters b/libs/freeimage/freeimage.vcxproj.filters index 62a8c0d1f7..63428a8ef7 100644 --- a/libs/freeimage/freeimage.vcxproj.filters +++ b/libs/freeimage/freeimage.vcxproj.filters @@ -734,6 +734,9 @@ <ClCompile Include="src\LibWebP\src\utils\utils.c"> <Filter>Source Files\LibWebP</Filter> </ClCompile> + <ClCompile Include="src\FreeImage\PluginTARGA.cpp"> + <Filter>Source Files</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="src\CacheFile.h"> diff --git a/libs/freeimage/src/FreeImage.h b/libs/freeimage/src/FreeImage.h index 2e9c658519..2e2008e7c6 100644 --- a/libs/freeimage/src/FreeImage.h +++ b/libs/freeimage/src/FreeImage.h @@ -52,6 +52,7 @@ #define DLL_API __declspec(dllexport) #else #define DLL_API __declspec(dllimport) + #pragma comment(lib, "freeimage.lib") #endif // FREEIMAGE_EXPORTS #else // try the gcc visibility support (see http://gcc.gnu.org/wiki/Visibility) @@ -388,6 +389,7 @@ FI_ENUM(FREE_IMAGE_FORMAT) { FIF_PNG = 3, FIF_GIF = 4, FIF_WEBP = 5, + FIF_TARGA = 6, //FIF_JNG = 3, //FIF_KOALA = 4, //FIF_LBM = 5, @@ -403,7 +405,6 @@ FI_ENUM(FREE_IMAGE_FORMAT) { //FIF_PPM = 14, //FIF_PPMRAW = 15, //FIF_RAS = 16, - //FIF_TARGA = 17, //FIF_TIFF = 18, //FIF_WBMP = 19, //FIF_PSD = 20, diff --git a/libs/freeimage/src/FreeImage/Plugin.cpp b/libs/freeimage/src/FreeImage/Plugin.cpp index ecedf6f927..f5533b1efc 100644 --- a/libs/freeimage/src/FreeImage/Plugin.cpp +++ b/libs/freeimage/src/FreeImage/Plugin.cpp @@ -232,6 +232,7 @@ FreeImage_Initialise(BOOL load_local_plugins_only) { s_plugins->AddNode(InitPNG); s_plugins->AddNode(InitGIF); s_plugins->AddNode(InitWEBP); + s_plugins->AddNode(InitTARGA); //s_plugins->AddNode(InitJNG); //s_plugins->AddNode(InitKOALA); //s_plugins->AddNode(InitIFF); @@ -245,7 +246,6 @@ FreeImage_Initialise(BOOL load_local_plugins_only) { //s_plugins->AddNode(InitPNM, NULL, "PPM", "Portable Pixelmap (ASCII)", "ppm", "^P3"); //s_plugins->AddNode(InitPNM, NULL, "PPMRAW", "Portable Pixelmap (RAW)", "ppm", "^P6"); //s_plugins->AddNode(InitRAS); - //s_plugins->AddNode(InitTARGA); //s_plugins->AddNode(InitTIFF); //s_plugins->AddNode(InitWBMP); //s_plugins->AddNode(InitPSD); diff --git a/libs/freeimage/src/FreeImage/PluginTARGA.cpp b/libs/freeimage/src/FreeImage/PluginTARGA.cpp new file mode 100644 index 0000000000..7f12230fd6 --- /dev/null +++ b/libs/freeimage/src/FreeImage/PluginTARGA.cpp @@ -0,0 +1,1595 @@ +// ========================================================== +// TARGA Loader and Writer +// +// Design and implementation by +// - Floris van den Berg (flvdberg@wxs.nl) +// - Jani Kajala (janik@remedy.fi) +// - Martin Weber (martweb@gmx.net) +// - Machiel ten Brinke (brinkem@uni-one.nl) +// - Peter Lemmens (peter.lemmens@planetinternet.be) +// - Hervé Drolon (drolon@infonie.fr) +// - Mihail Naydenov (mnaydenov@users.sourceforge.net) +// +// This file is part of FreeImage 3 +// +// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY +// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES +// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE +// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED +// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT +// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY +// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL +// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER +// THIS DISCLAIMER. +// +// Use at your own risk! +// ========================================================== + +#include "../stdafx.h" + +// ---------------------------------------------------------- +// Constants + headers +// ---------------------------------------------------------- + +#ifdef _WIN32 +#pragma pack(push, 1) +#else +#pragma pack(1) +#endif + +typedef struct tagTGAHEADER { + BYTE id_length; //! length of the image ID field + BYTE color_map_type; //! whether a color map is included + BYTE image_type; //! compression and color types + + WORD cm_first_entry; //! first entry index (offset into the color map table) + WORD cm_length; //! color map length (number of entries) + BYTE cm_size; //! color map entry size, in bits (number of bits per pixel) + + WORD is_xorigin; //! X-origin of image (absolute coordinate of lower-left corner for displays where origin is at the lower left) + WORD is_yorigin; //! Y-origin of image (as for X-origin) + WORD is_width; //! image width + WORD is_height; //! image height + BYTE is_pixel_depth; //! bits per pixel + BYTE is_image_descriptor; //! image descriptor, bits 3-0 give the alpha channel depth, bits 5-4 give direction +} TGAHEADER; + +typedef struct tagTGAEXTENSIONAREA { + WORD extension_size; // Size in bytes of the extension area, always 495 + char author_name[41]; // Name of the author. If not used, bytes should be set to NULL (\0) or spaces + char author_comments[324]; // A comment, organized as four lines, each consisting of 80 characters plus a NULL + WORD datetime_stamp[6]; // Date and time at which the image was created + char job_name[41]; // Job ID + WORD job_time[3]; // Hours, minutes and seconds spent creating the file (for billing, etc.) + char software_id[41]; // The application that created the file + BYTE software_version[3]; + DWORD key_color; + WORD pixel_aspect_ratio[2]; + WORD gamma_value[2]; + DWORD color_correction_offset; // Number of bytes from the beginning of the file to the color correction table if present + DWORD postage_stamp_offset; // Number of bytes from the beginning of the file to the postage stamp image if present + DWORD scan_line_offset; // Number of bytes from the beginning of the file to the scan lines table if present + BYTE attributes_type; // Specifies the alpha channel +} TGAEXTENSIONAREA; + +typedef struct tagTGAFOOTER { + DWORD extension_offset; // extension area offset : offset in bytes from the beginning of the file + DWORD developer_offset; // developer directory offset : offset in bytes from the beginning of the file + char signature[18]; // signature string : contains "TRUEVISION-XFILE.\0" +} TGAFOOTER; + +#ifdef _WIN32 +#pragma pack(pop) +#else +#pragma pack() +#endif + +static const char *FI_MSG_ERROR_CORRUPTED = "Image data corrupted"; + +// ---------------------------------------------------------- +// Image type +// +#define TGA_NULL 0 // no image data included +#define TGA_CMAP 1 // uncompressed, color-mapped image +#define TGA_RGB 2 // uncompressed, true-color image +#define TGA_MONO 3 // uncompressed, black-and-white image +#define TGA_RLECMAP 9 // run-length encoded, color-mapped image +#define TGA_RLERGB 10 // run-length encoded, true-color image +#define TGA_RLEMONO 11 // run-length encoded, black-and-white image +#define TGA_CMPCMAP 32 // compressed (Huffman/Delta/RLE) color-mapped image (e.g., VDA/D) - Obsolete +#define TGA_CMPCMAP4 33 // compressed (Huffman/Delta/RLE) color-mapped four pass image (e.g., VDA/D) - Obsolete + +// ========================================================== +// Thumbnail functions +// ========================================================== + +class TargaThumbnail +{ +public: + TargaThumbnail() : _w(0), _h(0), _depth(0), _data(NULL) { + } + ~TargaThumbnail() { + if(_data) { + free(_data); + } + } + + BOOL isNull() const { + return (_data == NULL); + } + + BOOL read(FreeImageIO *io, fi_handle handle, size_t size) { + io->read_proc(&_w, 1, 1, handle); + io->read_proc(&_h, 1, 1, handle); + + const size_t sizeofData = size - 2; + _data = (BYTE*)malloc(sizeofData); + if(_data) { + return (io->read_proc(_data, 1, (unsigned)sizeofData, handle) == sizeofData); + } + return FALSE; + } + + void setDepth(BYTE dp) { + _depth = dp; + } + + FIBITMAP* toFIBITMAP(); + +private: + BYTE _w; + BYTE _h; + BYTE _depth; + BYTE* _data; +}; + +#ifdef FREEIMAGE_BIGENDIAN +static void +swapShortPixels(FIBITMAP* dib) { + if(FreeImage_GetImageType(dib) != FIT_BITMAP) { + return; + } + + const unsigned Bpp = FreeImage_GetBPP(dib)/8; + if(Bpp != 2) { + return; + } + + BYTE* bits = FreeImage_GetBits(dib); + const unsigned height = FreeImage_GetHeight(dib); + const unsigned pitch = FreeImage_GetPitch(dib); + + BYTE* line = bits; + for(unsigned y = 0; y < height; y++, line += pitch) { + for(BYTE* pixel = line; pixel < line + pitch ; pixel += Bpp) { + SwapShort((WORD*)pixel); + } + } +} +#endif // FREEIMAGE_BIGENDIAN + +FIBITMAP* TargaThumbnail::toFIBITMAP() { + if(isNull() || _depth == 0) { + return NULL; + } + + const unsigned line_size = _depth * _w / 8; + FIBITMAP* dib = FreeImage_Allocate(_w, _h, _depth); + if(!dib) { + return NULL; + } + + const BYTE* line = _data; + const BYTE height = _h; + for (BYTE h = 0; h < height; ++h, line += line_size) { + BYTE* dst_line = FreeImage_GetScanLine(dib, height - 1 - h); + memcpy(dst_line, line, line_size); + } + +#ifdef FREEIMAGE_BIGENDIAN + swapShortPixels(dib); +#endif + +#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB + SwapRedBlue32(dib); +#endif + + return dib; +} +// ========================================================== +// Internal functions +// ========================================================== + +/** This class is used when loading RLE compressed images, it implements io cache of fixed size. + In general RLE compressed images *should* be compressed line by line with line sizes stored in Scan Line Table section. + In reality, however there are images not obeying the specification, compressing image data continuously across lines, + making it impossible to load the file cached at every line. +*/ +class IOCache +{ +public: + IOCache(FreeImageIO *io, fi_handle handle, size_t size) : + _ptr(NULL), _begin(NULL), _end(NULL), _size(size), _io(io), _handle(handle) { + _begin = (BYTE*)malloc(size); + if (_begin) { + _end = _begin + _size; + _ptr = _end; // will force refill on first access + } + } + + ~IOCache() { + if (_begin != NULL) { + free(_begin); + } + } + + BOOL isNull() { return _begin == NULL;} + + inline + BYTE getByte() { + if (_ptr >= _end) { + // need refill + _ptr = _begin; + _io->read_proc(_ptr, sizeof(BYTE), (unsigned)_size, _handle); //### EOF - no problem? + } + + BYTE result = *_ptr; + + _ptr++; + + return result; + } + + inline + BYTE* getBytes(size_t count /*must be < _size!*/) { + if (_ptr + count >= _end) { + + // need refill + + // 'count' bytes might span two cache bounds, + // SEEK back to add the remains of the current cache again into the new one + + long read = long(_ptr - _begin); + long remaining = long(_size - read); + + _io->seek_proc(_handle, -remaining, SEEK_CUR); + + _ptr = _begin; + _io->read_proc(_ptr, sizeof(BYTE), (unsigned)_size, _handle); //### EOF - no problem? + } + + BYTE *result = _ptr; + + _ptr += count; + + return result; + } + +private: + IOCache& operator=(const IOCache& src); // deleted + IOCache(const IOCache& other); // deleted + +private: + BYTE *_ptr; + BYTE *_begin; + BYTE *_end; + const size_t _size; + const FreeImageIO *_io; + const fi_handle _handle; +}; + +#ifdef FREEIMAGE_BIGENDIAN +static void +SwapHeader(TGAHEADER *header) { + SwapShort(&header->cm_first_entry); + SwapShort(&header->cm_length); + SwapShort(&header->is_xorigin); + SwapShort(&header->is_yorigin); + SwapShort(&header->is_width); + SwapShort(&header->is_height); +} + +static void +SwapExtensionArea(TGAEXTENSIONAREA *ex) { + SwapShort(&ex->extension_size); + SwapShort(&ex->datetime_stamp[0]); + SwapShort(&ex->datetime_stamp[1]); + SwapShort(&ex->datetime_stamp[2]); + SwapShort(&ex->datetime_stamp[3]); + SwapShort(&ex->datetime_stamp[4]); + SwapShort(&ex->datetime_stamp[5]); + SwapShort(&ex->job_time[0]); + SwapShort(&ex->job_time[1]); + SwapShort(&ex->job_time[2]); + SwapLong (&ex->key_color); + SwapShort(&ex->pixel_aspect_ratio[0]); + SwapShort(&ex->pixel_aspect_ratio[1]); + SwapShort(&ex->gamma_value[0]); + SwapShort(&ex->gamma_value[1]); + SwapLong (&ex->color_correction_offset); + SwapLong (&ex->postage_stamp_offset); + SwapLong (&ex->scan_line_offset); +} + +static void +SwapFooter(TGAFOOTER *footer) { + SwapLong(&footer->extension_offset); + SwapLong(&footer->developer_offset); +} + +#endif // FREEIMAGE_BIGENDIAN + +// ========================================================== +// Plugin Interface +// ========================================================== + +static int s_format_id; + +// ========================================================== +// Plugin Implementation +// ========================================================== + +static const char * DLL_CALLCONV +Format() { + return "TARGA"; +} + +static const char * DLL_CALLCONV +Description() { + return "Truevision Targa"; +} + +static const char * DLL_CALLCONV +Extension() { + return "tga,targa"; +} + +static const char * DLL_CALLCONV +RegExpr() { + return NULL; +} + +static const char * DLL_CALLCONV +MimeType() { + return "image/x-tga"; +} + +static BOOL +isTARGA20(FreeImageIO *io, fi_handle handle) { + const unsigned sizeofSig = 18; + BYTE signature[sizeofSig] = { 0 }; + // tga_signature = "TRUEVISION-XFILE." (TGA 2.0 only) + BYTE tga_signature[sizeofSig] = { 84, 82, 85, 69, 86, 73, 83, 73, 79, 78, 45, 88, 70, 73, 76, 69, 46, 0 }; + // get the start offset + const long start_offset = io->tell_proc(handle); + // get the end-of-file + io->seek_proc(handle, 0, SEEK_END); + const long eof = io->tell_proc(handle); + // read the signature + const long start_of_signature = start_offset + eof - sizeofSig; + if (start_of_signature > 0) { + io->seek_proc(handle, start_of_signature, SEEK_SET); + io->read_proc(&signature, 1, sizeofSig, handle); + } + // rewind + io->seek_proc(handle, start_offset, SEEK_SET); + + return (memcmp(tga_signature, signature, sizeofSig) == 0); +} + +static BOOL DLL_CALLCONV +Validate(FreeImageIO *io, fi_handle handle) { + if(isTARGA20(io, handle)) { + return TRUE; + } + + // not a 2.0 image, try testing if it's a valid TGA anyway (not robust) + { + const long start_offset = io->tell_proc(handle); + + // get the header + TGAHEADER header; + if (io->read_proc(&header, sizeof(tagTGAHEADER), 1, handle) < 1) { + return FALSE; + } +#ifdef FREEIMAGE_BIGENDIAN + SwapHeader(&header); +#endif + // rewind + io->seek_proc(handle, start_offset, SEEK_SET); + + // the color map type should be a 0 or a 1... + if(header.color_map_type != 0 && header.color_map_type != 1) { + return FALSE; + } + // if the color map type is 1 then we validate the map entry information... + if(header.color_map_type == 1) { + // it doesn't make any sense if the first entry is larger than the color map table + if(header.cm_first_entry >= header.cm_length) { + return FALSE; + } + // check header.cm_size, don't allow 0 or anything bigger than 32 + if(header.cm_size == 0 || header.cm_size > 32) { + return FALSE; + } + } + // the width/height shouldn't be 0, right ? + if(header.is_width == 0 || header.is_height == 0) { + return FALSE; + } + // let's now verify all the types that are supported by FreeImage (this is our final verification) + switch(header.image_type) { + case TGA_CMAP: + case TGA_RGB: + case TGA_MONO: + case TGA_RLECMAP: + case TGA_RLERGB: + case TGA_RLEMONO: + switch(header.is_pixel_depth) { + case 8 : + case 16: + case 24: + case 32: + return TRUE; + default: + return FALSE; + } + break; + default: + return FALSE; + } + } +} + +static BOOL DLL_CALLCONV +SupportsExportDepth(int depth) { + return ( + (depth == 8) || + (depth == 16) || + (depth == 24) || + (depth == 32) + ); +} + +static BOOL DLL_CALLCONV +SupportsExportType(FREE_IMAGE_TYPE type) { + return (type == FIT_BITMAP) ? TRUE : FALSE; +} + +static BOOL DLL_CALLCONV +SupportsNoPixels() { + return TRUE; +} + +// ---------------------------------------------------------- + +/** +Used for all 32 and 24 bit loading of uncompressed images +*/ +static void +loadTrueColor(FIBITMAP* dib, int width, int height, int file_pixel_size, FreeImageIO* io, fi_handle handle, BOOL as24bit) { + const int pixel_size = as24bit ? 3 : file_pixel_size; + + // input line cache + BYTE* file_line = (BYTE*)malloc( width * file_pixel_size); + + if (!file_line) { + throw FI_MSG_ERROR_MEMORY; + } + + for (int y = 0; y < height; y++) { + BYTE *bits = FreeImage_GetScanLine(dib, y); + io->read_proc(file_line, file_pixel_size, width, handle); + BYTE *bgra = file_line; + + for (int x = 0; x < width; x++) { + + bits[FI_RGBA_BLUE] = bgra[0]; + bits[FI_RGBA_GREEN] = bgra[1]; + bits[FI_RGBA_RED] = bgra[2]; + + if (!as24bit) { + bits[FI_RGBA_ALPHA] = bgra[3]; + } + + bgra += file_pixel_size; + + bits += pixel_size; + } + } + + free(file_line); +} + +/** +For the generic RLE loader we need to abstract away the pixel format. +We use a specific overload based on bits-per-pixel for each type of pixel +*/ + +template <int nBITS> +inline static void +_assignPixel(BYTE* bits, BYTE* val, BOOL as24bit = FALSE) { + // static assert should go here + assert(FALSE); +} + +template <> +inline void +_assignPixel<8>(BYTE* bits, BYTE* val, BOOL as24bit) { + *bits = *val; +} + +template <> +inline void +_assignPixel<16>(BYTE* bits, BYTE* val, BOOL as24bit) { + WORD value(*reinterpret_cast<WORD*>(val)); + +#ifdef FREEIMAGE_BIGENDIAN + SwapShort(&value); +#endif + + if (as24bit) { + bits[FI_RGBA_BLUE] = (BYTE)((((value & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT) * 0xFF) / 0x1F); + bits[FI_RGBA_GREEN] = (BYTE)((((value & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT) * 0xFF) / 0x1F); + bits[FI_RGBA_RED] = (BYTE)((((value & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT) * 0xFF) / 0x1F); + + } else { + *reinterpret_cast<WORD *>(bits) = 0x7FFF & value; + } +} + +template <> +inline void +_assignPixel<24>(BYTE* bits, BYTE* val, BOOL as24bit) { + bits[FI_RGBA_BLUE] = val[0]; + bits[FI_RGBA_GREEN] = val[1]; + bits[FI_RGBA_RED] = val[2]; +} + +template <> +inline void +_assignPixel<32>(BYTE* bits, BYTE* val, BOOL as24bit) { + if (as24bit) { + _assignPixel<24>(bits, val, TRUE); + + } else { +#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR + *(reinterpret_cast<unsigned*>(bits)) = *(reinterpret_cast<unsigned*> (val)); +#else // NOTE This is faster then doing reinterpret_cast to int + INPLACESWAP ! + bits[FI_RGBA_BLUE] = val[0]; + bits[FI_RGBA_GREEN] = val[1]; + bits[FI_RGBA_RED] = val[2]; + bits[FI_RGBA_ALPHA] = val[3]; +#endif + } +} + +/** +Generic RLE loader +*/ +template<int bPP> +static void +loadRLE(FIBITMAP* dib, int width, int height, FreeImageIO* io, fi_handle handle, long eof, BOOL as24bit) { + const int file_pixel_size = bPP/8; + const int pixel_size = as24bit ? 3 : file_pixel_size; + + const BYTE bpp = as24bit ? 24 : bPP; + const int line_size = CalculateLine(width, bpp); + + // Note, many of the params can be computed inside the function. + // However, because this is a template function, it will lead to redundant code duplication. + + BYTE rle; + BYTE *line_bits; + + // this is used to guard against writing beyond the end of the image (on corrupted rle block) + const BYTE* dib_end = FreeImage_GetScanLine(dib, height);//< one-past-end row + + // Compute the rough size of a line... + long pixels_offset = io->tell_proc(handle); + long sz = ((eof - pixels_offset) / height); + + // ...and allocate cache of this size (yields good results) + IOCache cache(io, handle, sz); + if(cache.isNull()) { + FreeImage_Unload(dib); + dib = NULL; + return; + } + + int x = 0, y = 0; + + line_bits = FreeImage_GetScanLine(dib, y); + + while (y < height) { + + rle = cache.getByte(); + + BOOL has_rle = rle & 0x80; + rle &= ~0x80; // remove type-bit + + BYTE packet_count = rle + 1; + + //packet_count might be corrupt, test if we are not about to write beyond the last image bit + + if ((line_bits+x) + packet_count*pixel_size > dib_end) { + FreeImage_OutputMessageProc(s_format_id, FI_MSG_ERROR_CORRUPTED); + // return what is left from the bitmap + return; + } + + if (has_rle) { + + // read a pixel value from file... + BYTE *val = cache.getBytes(file_pixel_size); + + //...and fill packet_count pixels with it + + for (int ix = 0; ix < packet_count; ix++) { + _assignPixel<bPP>((line_bits+x), val, as24bit); + x += pixel_size; + + if (x >= line_size) { + x = 0; + y++; + line_bits = FreeImage_GetScanLine(dib, y); + } + } + + } else { + // no rle commpresion + + // copy packet_count pixels from file to dib + for (int ix = 0; ix < packet_count; ix++) { + BYTE *val = cache.getBytes(file_pixel_size); + _assignPixel<bPP>((line_bits+x), val, as24bit); + x += pixel_size; + + if (x >= line_size) { + x = 0; + y++; + line_bits = FreeImage_GetScanLine(dib, y); + } + } //< packet_count + } //< has_rle + + } //< while height + +} + +// -------------------------------------------------------------------------- + +static FIBITMAP * DLL_CALLCONV +Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { + FIBITMAP *dib = NULL; + + if (!handle) { + return NULL; + } + + try { + + const BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS; + + // remember the start offset + long start_offset = io->tell_proc(handle); + + // remember end-of-file (used for RLE cache) + io->seek_proc(handle, 0, SEEK_END); + long eof = io->tell_proc(handle); + io->seek_proc(handle, start_offset, SEEK_SET); + + // read and process the bitmap's footer + + TargaThumbnail thumbnail; + if(isTARGA20(io, handle)) { + TGAFOOTER footer; + const long footer_offset = start_offset + eof - sizeof(footer); + + io->seek_proc(handle, footer_offset, SEEK_SET); + io->read_proc(&footer, sizeof(tagTGAFOOTER), 1, handle); + +#ifdef FREEIMAGE_BIGENDIAN + SwapFooter(&footer); +#endif + BOOL hasExtensionArea = footer.extension_offset > 0; + if(hasExtensionArea) { + TGAEXTENSIONAREA extensionarea; + io->seek_proc(handle, footer.extension_offset, SEEK_SET); + io->read_proc(&extensionarea, sizeof(extensionarea), 1, handle); + +#ifdef FREEIMAGE_BIGENDIAN + SwapExtensionArea(&extensionarea); +#endif + + DWORD postage_stamp_offset = extensionarea.postage_stamp_offset; + BOOL hasThumbnail = (postage_stamp_offset > 0) && (postage_stamp_offset < (DWORD)footer_offset); + if(hasThumbnail) { + io->seek_proc(handle, postage_stamp_offset, SEEK_SET); + thumbnail.read(io, handle, footer_offset - postage_stamp_offset); + } + } + } + + // read and process the bitmap's header + + TGAHEADER header; + + io->seek_proc(handle, start_offset, SEEK_SET); + io->read_proc(&header, sizeof(tagTGAHEADER), 1, handle); + +#ifdef FREEIMAGE_BIGENDIAN + SwapHeader(&header); +#endif + + thumbnail.setDepth(header.is_pixel_depth); + + const int line = CalculateLine(header.is_width, header.is_pixel_depth); + const int pixel_bits = header.is_pixel_depth; + const int pixel_size = pixel_bits/8; + + int fliphoriz = (header.is_image_descriptor & 0x10) ? 1 : 0; + int flipvert = (header.is_image_descriptor & 0x20) ? 1 : 0; + + // skip comment + io->seek_proc(handle, header.id_length, SEEK_CUR); + + switch (header.is_pixel_depth) { + case 8 : { + dib = FreeImage_AllocateHeader(header_only, header.is_width, header.is_height, 8); + + if (dib == NULL) { + throw FI_MSG_ERROR_DIB_MEMORY; + } + + // read the palette (even if header only) + + RGBQUAD *palette = FreeImage_GetPalette(dib); + + if (header.color_map_type > 0) { + unsigned count, csize; + + // calculate the color map size + csize = header.cm_length * header.cm_size / 8; + + // read the color map + BYTE *cmap = (BYTE*)malloc(csize * sizeof(BYTE)); + if (cmap == NULL) { + throw FI_MSG_ERROR_DIB_MEMORY; + } + io->read_proc(cmap, sizeof(BYTE), csize, handle); + + // build the palette + + switch (header.cm_size) { + case 16: { + WORD *rgb555 = (WORD*)&cmap[0]; + unsigned start = (unsigned)header.cm_first_entry; + unsigned stop = MIN((unsigned)256, (unsigned)header.cm_length); + + for (count = start; count < stop; count++) { + palette[count].rgbRed = (BYTE)((((*rgb555 & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT) * 0xFF) / 0x1F); + palette[count].rgbGreen = (BYTE)((((*rgb555 & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT) * 0xFF) / 0x1F); + palette[count].rgbBlue = (BYTE)((((*rgb555 & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT) * 0xFF) / 0x1F); + rgb555++; + } + } + break; + + case 24: { + FILE_BGR *bgr = (FILE_BGR*)&cmap[0]; + unsigned start = (unsigned)header.cm_first_entry; + unsigned stop = MIN((unsigned)256, (unsigned)header.cm_length); + + for (count = start; count < stop; count++) { + palette[count].rgbBlue = bgr->b; + palette[count].rgbGreen = bgr->g; + palette[count].rgbRed = bgr->r; + bgr++; + } + } + break; + + case 32: { + BYTE trns[256]; + + // clear the transparency table + memset(trns, 0xFF, 256); + + FILE_BGRA *bgra = (FILE_BGRA*)&cmap[0]; + unsigned start = (unsigned)header.cm_first_entry; + unsigned stop = MIN((unsigned)256, (unsigned)header.cm_length); + + for (count = start; count < stop; count++) { + palette[count].rgbBlue = bgra->b; + palette[count].rgbGreen = bgra->g; + palette[count].rgbRed = bgra->r; + // alpha + trns[count] = bgra->a; + bgra++; + } + + // set the tranparency table + FreeImage_SetTransparencyTable(dib, trns, 256); + } + break; + + } // switch(header.cm_size) + + free(cmap); + } + + // handle thumbnail + + FIBITMAP* th = thumbnail.toFIBITMAP(); + if(th) { + RGBQUAD* pal = FreeImage_GetPalette(dib); + RGBQUAD* dst_pal = FreeImage_GetPalette(th); + if(dst_pal && pal) { + for(unsigned i = 0; i < FreeImage_GetColorsUsed(dib); i++) { + dst_pal[i] = pal[i]; + } + } + + FreeImage_SetTransparencyTable(th, FreeImage_GetTransparencyTable(dib), FreeImage_GetTransparencyCount(dib)); + + FreeImage_SetThumbnail(dib, th); + FreeImage_Unload(th); + } + + if(header_only) { + return dib; + } + + // read in the bitmap bits + + switch (header.image_type) { + case TGA_CMAP: + case TGA_MONO: { + BYTE *bits = NULL; + + for (unsigned count = 0; count < header.is_height; count++) { + bits = FreeImage_GetScanLine(dib, count); + io->read_proc(bits, sizeof(BYTE), line, handle); + } + } + break; + + case TGA_RLECMAP: + case TGA_RLEMONO: { //(8 bit) + loadRLE<8>(dib, header.is_width, header.is_height, io, handle, eof, FALSE); + } + break; + + default : + FreeImage_Unload(dib); + return NULL; + } + } + break; // header.is_pixel_depth == 8 + + case 15 : + + case 16 : { + int pixel_bits = 16; + + // allocate the dib + + if (TARGA_LOAD_RGB888 & flags) { + pixel_bits = 24; + dib = FreeImage_AllocateHeader(header_only, header.is_width, header.is_height, pixel_bits, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); + + } else { + dib = FreeImage_AllocateHeader(header_only, header.is_width, header.is_height, pixel_bits, FI16_555_RED_MASK, FI16_555_GREEN_MASK, FI16_555_BLUE_MASK); + } + + if (dib == NULL) { + throw FI_MSG_ERROR_DIB_MEMORY; + } + + // handle thumbnail + + FIBITMAP* th = thumbnail.toFIBITMAP(); + if(th) { + if(TARGA_LOAD_RGB888 & flags) { + FIBITMAP* t = FreeImage_ConvertTo24Bits(th); + FreeImage_Unload(th); + th = t; + } + + FreeImage_SetThumbnail(dib, th); + FreeImage_Unload(th); + } + + if(header_only) { + return dib; + } + + int line = CalculateLine(header.is_width, pixel_bits); + + const unsigned pixel_size = unsigned(pixel_bits) / 8; + const unsigned src_pixel_size = sizeof(WORD); + + // note header.cm_size is a misleading name, it should be seen as header.cm_bits + // ignore current position in file and set filepointer explicitly from the beginning of the file + + int garblen = 0; + + if (header.color_map_type != 0) { + garblen = (int)((header.cm_size + 7) / 8) * header.cm_length; /* should byte align */ + + } else { + garblen = 0; + } + + io->seek_proc(handle, start_offset, SEEK_SET); + io->seek_proc(handle, sizeof(tagTGAHEADER) + header.id_length + garblen, SEEK_SET); + + // read in the bitmap bits + + switch (header.image_type) { + case TGA_RGB: { //(16 bit) + // input line cache + BYTE *in_line = (BYTE*)malloc(header.is_width * sizeof(WORD)); + + if (!in_line) + throw FI_MSG_ERROR_MEMORY; + + const int h = header.is_height; + + for (int y = 0; y < h; y++) { + + BYTE *bits = FreeImage_GetScanLine(dib, y); + io->read_proc(in_line, src_pixel_size, header.is_width, handle); + + BYTE *val = in_line; + for (int x = 0; x < line; x += pixel_size) { + + _assignPixel<16>(bits+x, val, TARGA_LOAD_RGB888 & flags); + + val += src_pixel_size; + } + } + + free(in_line); + } + break; + + case TGA_RLERGB: { //(16 bit) + loadRLE<16>(dib, header.is_width, header.is_height, io, handle, eof, TARGA_LOAD_RGB888 & flags); + } + break; + + default : + FreeImage_Unload(dib); + return NULL; + } + } + break; // header.is_pixel_depth == 15 or 16 + + case 24 : { + + dib = FreeImage_AllocateHeader(header_only, header.is_width, header.is_height, pixel_bits, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); + + if (dib == NULL) { + throw FI_MSG_ERROR_DIB_MEMORY; + } + + FIBITMAP* th = thumbnail.toFIBITMAP(); + if(th) { + FreeImage_SetThumbnail(dib, th); + FreeImage_Unload(th); + } + + if(header_only) { + return dib; + } + + // read in the bitmap bits + + switch (header.image_type) { + case TGA_RGB: { //(24 bit) + //uncompressed + loadTrueColor(dib, header.is_width, header.is_height, pixel_size,io, handle, TRUE); + } + break; + + case TGA_RLERGB: { //(24 bit) + loadRLE<24>(dib, header.is_width, header.is_height, io, handle, eof, TRUE); + } + break; + + default : + FreeImage_Unload(dib); + return NULL; + } + } + break; // header.is_pixel_depth == 24 + + case 32 : { + int pixel_bits = 32; + + if (TARGA_LOAD_RGB888 & flags) { + pixel_bits = 24; + } + + dib = FreeImage_AllocateHeader(header_only, header.is_width, header.is_height, pixel_bits, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); + + if (dib == NULL) { + throw FI_MSG_ERROR_DIB_MEMORY; + } + + // handle thumbnail + + FIBITMAP* th = thumbnail.toFIBITMAP(); + if(th) { + if(TARGA_LOAD_RGB888 & flags) { + FIBITMAP* t = FreeImage_ConvertTo24Bits(th); + FreeImage_Unload(th); + th = t; + } + FreeImage_SetThumbnail(dib, th); + FreeImage_Unload(th); + } + + if(header_only) { + return dib; + } + + // read in the bitmap bits + + switch (header.image_type) { + case TGA_RGB: { //(32 bit) + // uncompressed + loadTrueColor(dib, header.is_width, header.is_height, 4 /*file_pixel_size*/, io, handle, TARGA_LOAD_RGB888 & flags); + } + break; + + case TGA_RLERGB: { //(32 bit) + loadRLE<32>(dib, header.is_width, header.is_height, io, handle, eof, TARGA_LOAD_RGB888 & flags); + } + break; + + default : + FreeImage_Unload(dib); + return NULL; + } + } + break; // header.is_pixel_depth == 32 + + } // switch(header.is_pixel_depth) + + if (flipvert) { + FreeImage_FlipVertical(dib); + } + + if (fliphoriz) { + FreeImage_FlipHorizontal(dib); + } + + return dib; + + } catch (const char *message) { + if (dib) { + FreeImage_Unload(dib); + } + + FreeImage_OutputMessageProc(s_format_id, message); + + return NULL; + } +} + +// -------------------------------------------------------------------------- + +static BOOL +hasValidThumbnail(FIBITMAP* dib) { + FIBITMAP* thumbnail = FreeImage_GetThumbnail(dib); + + return thumbnail + && SupportsExportType(FreeImage_GetImageType(thumbnail)) + && SupportsExportDepth(FreeImage_GetBPP(thumbnail)) + // Requirements according to the specification: + && FreeImage_GetBPP(thumbnail) == FreeImage_GetBPP(dib) + && FreeImage_GetImageType(thumbnail) == FreeImage_GetImageType(dib) + && FreeImage_GetWidth(thumbnail) <= 255 + && FreeImage_GetHeight(thumbnail) <= 255; +} + +/** +Writes the ready RLE packet to buffer +*/ +static inline void +flushPacket(BYTE*& dest, unsigned pixel_size, BYTE* packet_begin, BYTE*& packet, BYTE& packet_count, BOOL& has_rle) { + if (packet_count) { + const BYTE type_bit = has_rle ? 0x80 : 0x0; + const BYTE write_count = has_rle ? 1 : packet_count; + + // build packet header: zero-based count + type bit + assert(packet_count >= 1); + BYTE rle = packet_count - 1; + rle |= type_bit; + + // write packet header + *dest = rle; + ++dest; + + // write packet data + memcpy(dest, packet_begin, write_count * pixel_size); + dest += write_count * pixel_size; + + // reset state + packet_count = 0; + packet = packet_begin; + has_rle = FALSE; + } +} + + +static inline void +writeToPacket(BYTE* packet, BYTE* pixel, unsigned pixel_size) { + // Take care of channel and byte order here, because packet will be flushed straight to the file + switch (pixel_size) { + case 1: + *packet = *pixel; + break; + + case 2: { + WORD val(*(WORD*)pixel); +#ifdef FREEIMAGE_BIGENDIAN + SwapShort(&val); +#endif + *(WORD*)packet = val; + } + break; + + case 3: { + packet[0] = pixel[FI_RGBA_BLUE]; + packet[1] = pixel[FI_RGBA_GREEN]; + packet[2] = pixel[FI_RGBA_RED]; + } + break; + + case 4: { +#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR + *(reinterpret_cast<unsigned*>(packet)) = *(reinterpret_cast<unsigned*> (pixel)); +#else + packet[0] = pixel[FI_RGBA_BLUE]; + packet[1] = pixel[FI_RGBA_GREEN]; + packet[2] = pixel[FI_RGBA_RED]; + packet[3] = pixel[FI_RGBA_ALPHA]; +#endif + } + break; + + default: + assert(FALSE); + } +} + +static inline BOOL +isEqualPixel(BYTE* lhs, BYTE* rhs, unsigned pixel_size) { + switch (pixel_size) { + case 1: + return *lhs == *rhs; + + case 2: + return *(WORD*)lhs == *(WORD*)rhs; + + case 3: + return *(WORD*)lhs == *(WORD*)rhs && lhs[2] == rhs[2]; + + case 4: + return *(unsigned*)lhs == *(unsigned*)rhs; + + default: + assert(FALSE); + return FALSE; + } +} + +static void +saveRLE(FIBITMAP* dib, FreeImageIO* io, fi_handle handle) { + // Image is compressed line by line, packets don't span multiple lines (TGA2.0 recommendation) + + const unsigned width = FreeImage_GetWidth(dib); + const unsigned height = FreeImage_GetHeight(dib); + const unsigned pixel_size = FreeImage_GetBPP(dib)/8; + const unsigned line_size = FreeImage_GetLine(dib); + + const BYTE max_packet_size = 128; + BYTE packet_count = 0; + BOOL has_rle = FALSE; + + // packet (compressed or not) to be written to line + + BYTE* const packet_begin = (BYTE*)malloc(max_packet_size * pixel_size); + BYTE* packet = packet_begin; + + // line to be written to disk + // Note: we need some extra bytes for anti-commpressed lines. The worst case is: + // 8 bit images were every 3th pixel is different. + // Rle packet becomes two pixels, but nothing is compressed: two byte pixels are transformed into byte header and byte pixel value + // After every rle packet there is a non-rle packet of one pixel: an extra byte for the header will be added for it + // In the end we gain no bytes from compression, but also must insert a byte at every 3th pixel + + // add extra space for anti-commpressed lines + size_t extra_space = (size_t)ceil(width / 3.0); + BYTE* const line_begin = (BYTE*)malloc(width * pixel_size + extra_space); + BYTE* line = line_begin; + + BYTE *current = (BYTE*)malloc(pixel_size); + BYTE *next = (BYTE*)malloc(pixel_size); + + for(unsigned y = 0; y < height; y++) { + BYTE *bits = FreeImage_GetScanLine(dib, y); + + // rewind line pointer + line = line_begin; + + for(unsigned x = 0; x < line_size; x += pixel_size) { + + AssignPixel(current, (bits + x), pixel_size); + + // read next pixel from dib + + if( x + 1*pixel_size < line_size) { + AssignPixel(next, (bits + x + 1*pixel_size), pixel_size); + + } else { + // last pixel in line + + // include current pixel and flush + if(!has_rle) { + + writeToPacket(packet, current, pixel_size); + packet += pixel_size; + + } + + assert(packet_count < max_packet_size); + + ++packet_count; + + flushPacket(line, pixel_size, packet_begin, packet, packet_count, has_rle); + + // start anew on next line + break; + } + + if(isEqualPixel(current, next, pixel_size)) { + + // has rle + + if(!has_rle) { + // flush non rle packet + + flushPacket(line, pixel_size, packet_begin, packet, packet_count, has_rle); + + // start a rle packet + + has_rle = TRUE; + + writeToPacket(packet, current, pixel_size); + packet += pixel_size; + } + + // otherwise do nothing. We will just increase the count at the end + + } else { + + // no rle + + if(has_rle) { + // flush rle packet + + // include current pixel first + assert(packet_count < max_packet_size); + ++packet_count; + + flushPacket(line, pixel_size, packet_begin, packet, packet_count, has_rle); + + // start anew on the next pixel + continue; + + } else { + + writeToPacket(packet, current, pixel_size); + packet += pixel_size; + } + + } + + // increase counter on every pixel + + ++packet_count; + + if(packet_count == max_packet_size) { + flushPacket(line, pixel_size, packet_begin, packet, packet_count, has_rle); + } + + }//for width + + // write line to disk + io->write_proc(line_begin, 1, (unsigned)(line - line_begin), handle); + + }//for height + + free(line_begin); + free(packet_begin); + free(current); + free(next); +} + +static BOOL DLL_CALLCONV +Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) { + if ((dib == NULL) || (handle == NULL)) { + return FALSE; + } + + RGBQUAD *palette = FreeImage_GetPalette(dib); + const unsigned bpp = FreeImage_GetBPP(dib); + + // write the file header + + TGAHEADER header; + + header.id_length = 0; + header.cm_first_entry = 0; + header.is_xorigin = 0; + header.is_yorigin = 0; + header.is_width = (WORD)FreeImage_GetWidth(dib); + header.is_height = (WORD)FreeImage_GetHeight(dib); + header.is_pixel_depth = (BYTE)bpp; + header.is_image_descriptor = (bpp == 32 ? 8 : 0); + + if (palette) { + header.color_map_type = 1; + header.image_type = (TARGA_SAVE_RLE & flags) ? TGA_RLECMAP : TGA_CMAP; + header.cm_length = (WORD)(1 << bpp); + + if (FreeImage_IsTransparent(dib)) { + header.cm_size = 32; + } else { + header.cm_size = 24; + } + + } else { + header.color_map_type = 0; + header.image_type = (TARGA_SAVE_RLE & flags) ? TGA_RLERGB : TGA_RGB; + header.cm_length = 0; + header.cm_size = 0; + } + + // write the header + +#ifdef FREEIMAGE_BIGENDIAN + SwapHeader(&header); +#endif + + io->write_proc(&header, sizeof(header), 1, handle); + +#ifdef FREEIMAGE_BIGENDIAN + SwapHeader(&header); +#endif + + // write the palette + + if (palette) { + if (FreeImage_IsTransparent(dib)) { + FILE_BGRA *bgra_pal = (FILE_BGRA*)malloc(header.cm_length * sizeof(FILE_BGRA)); + + // get the transparency table + BYTE *trns = FreeImage_GetTransparencyTable(dib); + + for (unsigned i = 0; i < header.cm_length; i++) { + bgra_pal[i].b = palette[i].rgbBlue; + bgra_pal[i].g = palette[i].rgbGreen; + bgra_pal[i].r = palette[i].rgbRed; + bgra_pal[i].a = trns[i]; + } + + io->write_proc(bgra_pal, sizeof(FILE_BGRA), header.cm_length, handle); + + free(bgra_pal); + + } else { + FILE_BGR *bgr_pal = (FILE_BGR*)malloc(header.cm_length * sizeof(FILE_BGR)); + + for (unsigned i = 0; i < header.cm_length; i++) { + bgr_pal[i].b = palette[i].rgbBlue; + bgr_pal[i].g = palette[i].rgbGreen; + bgr_pal[i].r = palette[i].rgbRed; + } + + io->write_proc(bgr_pal, sizeof(FILE_BGR), header.cm_length, handle); + + free(bgr_pal); + } + } + + // write the data bits + + + if (TARGA_SAVE_RLE & flags) { + + saveRLE(dib, io, handle); + + } else { + + // -- no rle compression -- + + const unsigned width = header.is_width; + const unsigned height = header.is_height; + const unsigned pixel_size = bpp/8; + + BYTE *line, *const line_begin = (BYTE*)malloc(width * pixel_size); + BYTE *line_source = line_begin; + + for (unsigned y = 0; y < height; y++) { + BYTE *scanline = FreeImage_GetScanLine(dib, y); + + // rewind the line pointer + line = line_begin; + + switch (bpp) { + case 8: { + // don't copy line, read straight from dib + line_source = scanline; + } + break; + + case 16: { + for (unsigned x = 0; x < width; x++) { + WORD pixel = *(((WORD *)scanline) + x); + +#ifdef FREEIMAGE_BIGENDIAN + SwapShort(&pixel); +#endif + *(WORD*)line = pixel; + + line += pixel_size; + } + } + break; + + case 24: { + +#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR + line_source = scanline; +#else + for (unsigned x = 0; x < width; ++x) { + RGBTRIPLE* trip = ((RGBTRIPLE *)scanline) + x; + line[0] = trip->rgbtBlue; + line[1] = trip->rgbtGreen; + line[2] = trip->rgbtRed; + + line += pixel_size; + } +#endif + } + break; + + case 32: { + +#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR + line_source = scanline; +#else + for (unsigned x = 0; x < width; ++x) { + RGBQUAD* quad = ((RGBQUAD *)scanline) + x; + line[0] = quad->rgbBlue; + line[1] = quad->rgbGreen; + line[2] = quad->rgbRed; + line[3] = quad->rgbReserved; + + line += pixel_size; + } +#endif + } + break; + + }//switch(bpp) + + // write line to disk + + io->write_proc(line_source, pixel_size, width, handle); + + }//for height + + free(line_begin); + } + + + long extension_offset = 0 ; + if(hasValidThumbnail(dib)) { + // write extension area + + extension_offset = io->tell_proc(handle); + + TGAEXTENSIONAREA ex; + memset(&ex, 0, sizeof(ex)); + + assert(sizeof(ex) == 495); + ex.extension_size = sizeof(ex); + ex.postage_stamp_offset = extension_offset + ex.extension_size + 0 /*< no Scan Line Table*/; + ex.attributes_type = FreeImage_GetBPP(dib) == 32 ? 3 /*< useful Alpha channel data*/ : 0 /*< no Alpha data*/; + +#ifdef FREEIMAGE_BIGENDIAN + SwapExtensionArea(&ex); +#endif + + io->write_proc(&ex, sizeof(ex), 1, handle); + + // (no Scan Line Table) + + // write thumbnail + + io->seek_proc(handle, ex.postage_stamp_offset, SEEK_SET); + + FIBITMAP* thumbnail = FreeImage_GetThumbnail(dib); + BYTE width = (BYTE)FreeImage_GetWidth(thumbnail); + BYTE height = (BYTE)FreeImage_GetHeight(thumbnail); + + io->write_proc(&width, 1, 1, handle); + io->write_proc(&height, 1, 1, handle); + +#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB + SwapRedBlue32(dib); +#endif + +#ifdef FREEIMAGE_BIGENDIAN + swapShortPixels(dib); +#endif + + const unsigned line_size = FreeImage_GetLine(thumbnail); + + for (BYTE h = 0; h < height; ++h) { + BYTE* src_line = FreeImage_GetScanLine(thumbnail, height - 1 - h); + io->write_proc(src_line, 1, line_size, handle); + } + } + + // (no Color Correction Table) + + // write the footer + + TGAFOOTER footer; + footer.extension_offset = extension_offset; + footer.developer_offset = 0; + strcpy(footer.signature, "TRUEVISION-XFILE."); + + +#ifdef FREEIMAGE_BIGENDIAN + SwapFooter(&footer); +#endif + + io->write_proc(&footer, sizeof(footer), 1, handle); + + return TRUE; +} + +// ========================================================== +// Init +// ========================================================== + +void DLL_CALLCONV +InitTARGA(Plugin *plugin, int format_id) { + s_format_id = format_id; + + plugin->format_proc = Format; + plugin->description_proc = Description; + plugin->extension_proc = Extension; + plugin->regexpr_proc = RegExpr; + plugin->open_proc = NULL; + plugin->close_proc = NULL; + plugin->pagecount_proc = NULL; + plugin->pagecapability_proc = NULL; + plugin->load_proc = Load; + plugin->save_proc = Save; + plugin->validate_proc = Validate; + plugin->mime_proc = MimeType; + plugin->supports_export_bpp_proc = SupportsExportDepth; + plugin->supports_export_type_proc = SupportsExportType; + plugin->supports_icc_profiles_proc = NULL; + plugin->supports_no_pixels_proc = SupportsNoPixels; +} diff --git a/libs/win32/FreeImage.lib b/libs/win32/FreeImage.lib Binary files differnew file mode 100644 index 0000000000..aa87110000 --- /dev/null +++ b/libs/win32/FreeImage.lib diff --git a/libs/win64/FreeImage.lib b/libs/win64/FreeImage.lib Binary files differnew file mode 100644 index 0000000000..674b034549 --- /dev/null +++ b/libs/win64/FreeImage.lib |