summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorge Hazan <george.hazan@gmail.com>2024-11-04 14:48:38 +0300
committerGeorge Hazan <george.hazan@gmail.com>2024-11-04 14:48:38 +0300
commit2829883e8d5116bb7f0983022e08791aa1aa47c3 (patch)
tree6ea046a33aa32d0e921fa839fcdbaa02e1c819af
parent71a1103f8a902a82f2215932ecb3c1a06e74718e (diff)
FreeImage: no need to rebuild each time + TARGA support
-rw-r--r--libs/freeimage/freeimage.vcxproj3
-rw-r--r--libs/freeimage/freeimage.vcxproj.filters3
-rw-r--r--libs/freeimage/src/FreeImage.h3
-rw-r--r--libs/freeimage/src/FreeImage/Plugin.cpp2
-rw-r--r--libs/freeimage/src/FreeImage/PluginTARGA.cpp1595
-rw-r--r--libs/win32/FreeImage.libbin0 -> 126990 bytes
-rw-r--r--libs/win64/FreeImage.libbin0 -> 121284 bytes
-rw-r--r--plugins/AVS/avs.vcxproj5
-rw-r--r--plugins/NewStory/NewStory.vcxproj3
-rw-r--r--plugins/SendScreenshotPlus/SendSS.vcxproj5
-rw-r--r--plugins/SmileyAdd/SmileyAdd.vcxproj5
-rw-r--r--plugins/TabSRMM/tabsrmm.vcxproj5
-rw-r--r--plugins/TipperYM/tipper_ym.vcxproj5
-rw-r--r--plugins/UserInfoEx/UInfoEx.vcxproj5
-rw-r--r--protocols/JabberG/jabber.vcxproj3
-rw-r--r--protocols/SkypeWeb/SkypeWeb.vcxproj5
-rw-r--r--protocols/Steam/Steam.vcxproj5
-rw-r--r--protocols/Telegram/Telegram.vcxproj3
-rw-r--r--protocols/VKontakte/vk.vcxproj3
-rw-r--r--src/mir_app/mir_app.vcxproj3
20 files changed, 1604 insertions, 57 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
new file mode 100644
index 0000000000..aa87110000
--- /dev/null
+++ b/libs/win32/FreeImage.lib
Binary files differ
diff --git a/libs/win64/FreeImage.lib b/libs/win64/FreeImage.lib
new file mode 100644
index 0000000000..674b034549
--- /dev/null
+++ b/libs/win64/FreeImage.lib
Binary files differ
diff --git a/plugins/AVS/avs.vcxproj b/plugins/AVS/avs.vcxproj
index f8b5b6ff5a..86e399c534 100644
--- a/plugins/AVS/avs.vcxproj
+++ b/plugins/AVS/avs.vcxproj
@@ -47,11 +47,6 @@
<ClInclude Include="src\version.h" />
</ItemGroup>
<ItemGroup>
- <ProjectReference Include="..\..\libs\freeimage\freeimage.vcxproj">
- <Project>{5D14CFF3-0D17-4528-99EA-DE9DCA47CC2E}</Project>
- </ProjectReference>
- </ItemGroup>
- <ItemGroup>
<ResourceCompile Include="res\avs.rc" />
<ResourceCompile Include="res\version.rc" />
</ItemGroup>
diff --git a/plugins/NewStory/NewStory.vcxproj b/plugins/NewStory/NewStory.vcxproj
index ea0f9d72ff..902c0bbb97 100644
--- a/plugins/NewStory/NewStory.vcxproj
+++ b/plugins/NewStory/NewStory.vcxproj
@@ -66,9 +66,6 @@
<ResourceCompile Include="res\Version.rc" />
</ItemGroup>
<ItemGroup>
- <ProjectReference Include="..\..\libs\freeimage\freeimage.vcxproj">
- <Project>{5d14cff3-0d17-4528-99ea-de9dca47cc2e}</Project>
- </ProjectReference>
<ProjectReference Include="..\..\libs\litehtml\litehtml.vcxproj">
<Project>{51db004a-e160-47c7-b017-bec90fdc442d}</Project>
</ProjectReference>
diff --git a/plugins/SendScreenshotPlus/SendSS.vcxproj b/plugins/SendScreenshotPlus/SendSS.vcxproj
index 64f828ef5f..4a3f9d8034 100644
--- a/plugins/SendScreenshotPlus/SendSS.vcxproj
+++ b/plugins/SendScreenshotPlus/SendSS.vcxproj
@@ -62,11 +62,6 @@
<ClInclude Include="src\version.h" />
</ItemGroup>
<ItemGroup>
- <ProjectReference Include="..\..\libs\freeimage\freeimage.vcxproj">
- <Project>{5D14CFF3-0D17-4528-99EA-DE9DCA47CC2E}</Project>
- </ProjectReference>
- </ItemGroup>
- <ItemGroup>
<ResourceCompile Include="res\resource.rc" />
<ResourceCompile Include="res\version.rc" />
</ItemGroup>
diff --git a/plugins/SmileyAdd/SmileyAdd.vcxproj b/plugins/SmileyAdd/SmileyAdd.vcxproj
index 3f1ef12004..4c3890b996 100644
--- a/plugins/SmileyAdd/SmileyAdd.vcxproj
+++ b/plugins/SmileyAdd/SmileyAdd.vcxproj
@@ -63,11 +63,6 @@
<ClInclude Include="src\version.h" />
</ItemGroup>
<ItemGroup>
- <ProjectReference Include="..\..\libs\freeimage\freeimage.vcxproj">
- <Project>{5D14CFF3-0D17-4528-99EA-DE9DCA47CC2E}</Project>
- </ProjectReference>
- </ItemGroup>
- <ItemGroup>
<ResourceCompile Include="res\resource.rc" />
<ResourceCompile Include="res\Version.rc" />
</ItemGroup>
diff --git a/plugins/TabSRMM/tabsrmm.vcxproj b/plugins/TabSRMM/tabsrmm.vcxproj
index 0b811c6f42..ef06eff46a 100644
--- a/plugins/TabSRMM/tabsrmm.vcxproj
+++ b/plugins/TabSRMM/tabsrmm.vcxproj
@@ -93,11 +93,6 @@
<ClInclude Include="src\version.h" />
</ItemGroup>
<ItemGroup>
- <ProjectReference Include="..\..\libs\freeimage\freeimage.vcxproj">
- <Project>{5D14CFF3-0D17-4528-99EA-DE9DCA47CC2E}</Project>
- </ProjectReference>
- </ItemGroup>
- <ItemGroup>
<ResourceCompile Include="res\chat.rc" />
<ResourceCompile Include="res\msgwindow.rc" />
<ResourceCompile Include="res\resource.rc" />
diff --git a/plugins/TipperYM/tipper_ym.vcxproj b/plugins/TipperYM/tipper_ym.vcxproj
index 82ba251ebf..d087d66019 100644
--- a/plugins/TipperYM/tipper_ym.vcxproj
+++ b/plugins/TipperYM/tipper_ym.vcxproj
@@ -53,11 +53,6 @@
<ClInclude Include="src\version.h" />
</ItemGroup>
<ItemGroup>
- <ProjectReference Include="..\..\libs\freeimage\freeimage.vcxproj">
- <Project>{5D14CFF3-0D17-4528-99EA-DE9DCA47CC2E}</Project>
- </ProjectReference>
- </ItemGroup>
- <ItemGroup>
<ResourceCompile Include="res\resource.rc" />
<ResourceCompile Include="res\version.rc" />
</ItemGroup>
diff --git a/plugins/UserInfoEx/UInfoEx.vcxproj b/plugins/UserInfoEx/UInfoEx.vcxproj
index 52bd001f81..49b0846b9a 100644
--- a/plugins/UserInfoEx/UInfoEx.vcxproj
+++ b/plugins/UserInfoEx/UInfoEx.vcxproj
@@ -129,11 +129,6 @@
<ClInclude Include="src\version.h" />
</ItemGroup>
<ItemGroup>
- <ProjectReference Include="..\..\libs\freeimage\freeimage.vcxproj">
- <Project>{5D14CFF3-0D17-4528-99EA-DE9DCA47CC2E}</Project>
- </ProjectReference>
- </ItemGroup>
- <ItemGroup>
<ClInclude Include="src\classMAnnivDate.h" />
<ClInclude Include="src\classMTime.h" />
<ClInclude Include="src\ctrl_annivedit.h" />
diff --git a/protocols/JabberG/jabber.vcxproj b/protocols/JabberG/jabber.vcxproj
index 1c6f2f6266..ea5a4e6cd3 100644
--- a/protocols/JabberG/jabber.vcxproj
+++ b/protocols/JabberG/jabber.vcxproj
@@ -115,9 +115,6 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
- <ProjectReference Include="..\..\libs\freeimage\freeimage.vcxproj">
- <Project>{5D14CFF3-0D17-4528-99EA-DE9DCA47CC2E}</Project>
- </ProjectReference>
<ProjectReference Include="..\..\libs\libsignal\libsignal.vcxproj">
<Project>{620e0be7-3763-4f35-9dbd-4770104e269c}</Project>
</ProjectReference>
diff --git a/protocols/SkypeWeb/SkypeWeb.vcxproj b/protocols/SkypeWeb/SkypeWeb.vcxproj
index 0e05b4afec..0ee9f1eac3 100644
--- a/protocols/SkypeWeb/SkypeWeb.vcxproj
+++ b/protocols/SkypeWeb/SkypeWeb.vcxproj
@@ -79,11 +79,6 @@
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
- <ProjectReference Include="..\..\libs\freeimage\freeimage.vcxproj">
- <Project>{5D14CFF3-0D17-4528-99EA-DE9DCA47CC2E}</Project>
- </ProjectReference>
- </ItemGroup>
- <ItemGroup>
<ProjectReference Include="..\..\libs\libjson\libjson.vcxproj">
<Project>{f6a9340e-b8d9-4c75-be30-47dc66d0abc7}</Project>
</ProjectReference>
diff --git a/protocols/Steam/Steam.vcxproj b/protocols/Steam/Steam.vcxproj
index 1389d5d489..0f2fac75bd 100644
--- a/protocols/Steam/Steam.vcxproj
+++ b/protocols/Steam/Steam.vcxproj
@@ -92,11 +92,6 @@
<None Include="res\steam.ico" />
</ItemGroup>
<ItemGroup>
- <ProjectReference Include="..\..\libs\freeimage\freeimage.vcxproj">
- <Project>{5D14CFF3-0D17-4528-99EA-DE9DCA47CC2E}</Project>
- </ProjectReference>
- </ItemGroup>
- <ItemGroup>
<ResourceCompile Include="res\Resource.rc" />
<ResourceCompile Include="res\version.rc" />
</ItemGroup>
diff --git a/protocols/Telegram/Telegram.vcxproj b/protocols/Telegram/Telegram.vcxproj
index 90e1e5e72d..243fd85388 100644
--- a/protocols/Telegram/Telegram.vcxproj
+++ b/protocols/Telegram/Telegram.vcxproj
@@ -58,9 +58,6 @@
<ClInclude Include="src\version.h" />
</ItemGroup>
<ItemGroup>
- <ProjectReference Include="..\..\libs\freeimage\freeimage.vcxproj">
- <Project>{5d14cff3-0d17-4528-99ea-de9dca47cc2e}</Project>
- </ProjectReference>
<ProjectReference Include="..\..\libs\sqlite3\sqlite3.vcxproj">
<Project>{0c02e395-e73f-47e3-8b95-b7924c0c7a6a}</Project>
</ProjectReference>
diff --git a/protocols/VKontakte/vk.vcxproj b/protocols/VKontakte/vk.vcxproj
index e7fb2c7410..d298f64f77 100644
--- a/protocols/VKontakte/vk.vcxproj
+++ b/protocols/VKontakte/vk.vcxproj
@@ -61,9 +61,6 @@
<ProjectReference Include="..\..\libs\libjson\libjson.vcxproj">
<Project>{f6a9340e-b8d9-4c75-be30-47dc66d0abc7}</Project>
</ProjectReference>
- <ProjectReference Include="..\..\libs\freeimage\freeimage.vcxproj">
- <Project>{5D14CFF3-0D17-4528-99EA-DE9DCA47CC2E}</Project>
- </ProjectReference>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="res\resource.rc" />
diff --git a/src/mir_app/mir_app.vcxproj b/src/mir_app/mir_app.vcxproj
index ed4ea3b3e1..50cfd59262 100644
--- a/src/mir_app/mir_app.vcxproj
+++ b/src/mir_app/mir_app.vcxproj
@@ -211,9 +211,6 @@
</Manifest>
</ItemDefinitionGroup>
<ItemGroup>
- <ProjectReference Include="..\..\libs\freeimage\freeimage.vcxproj">
- <Project>{5d14cff3-0d17-4528-99ea-de9dca47cc2e}</Project>
- </ProjectReference>
<ProjectReference Include="..\..\libs\zlib\zlib.vcxproj">
<Project>{e2a369cd-eda3-414f-8ad0-e732cd7ee68c}</Project>
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>