summaryrefslogtreecommitdiff
path: root/plugins/AdvaImg/src/FreeImage/PluginICO.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/AdvaImg/src/FreeImage/PluginICO.cpp')
-rw-r--r--plugins/AdvaImg/src/FreeImage/PluginICO.cpp805
1 files changed, 805 insertions, 0 deletions
diff --git a/plugins/AdvaImg/src/FreeImage/PluginICO.cpp b/plugins/AdvaImg/src/FreeImage/PluginICO.cpp
new file mode 100644
index 0000000000..d0ece3514c
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/PluginICO.cpp
@@ -0,0 +1,805 @@
+// ==========================================================
+// ICO Loader and Writer
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Hervé Drolon (drolon@infonie.fr)
+//
+// 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 "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+// Constants + headers
+// ----------------------------------------------------------
+
+#ifdef _WIN32
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif
+
+// These next two structs represent how the icon information is stored
+// in an ICO file.
+
+typedef struct tagICONHEADER {
+ WORD idReserved; // reserved
+ WORD idType; // resource type (1 for icons)
+ WORD idCount; // how many images?
+} ICONHEADER;
+
+typedef struct tagICONDIRECTORYENTRY {
+ BYTE bWidth; // width of the image
+ BYTE bHeight; // height of the image (times 2)
+ BYTE bColorCount; // number of colors in image (0 if >=8bpp)
+ BYTE bReserved; // reserved
+ WORD wPlanes; // color Planes
+ WORD wBitCount; // bits per pixel
+ DWORD dwBytesInRes; // how many bytes in this resource?
+ DWORD dwImageOffset; // where in the file is this image
+} ICONDIRENTRY;
+
+#ifdef _WIN32
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif
+
+// ==========================================================
+// Static helpers
+// ==========================================================
+
+/** How wide, in bytes, would this many bits be, DWORD aligned ?
+*/
+static int
+WidthBytes(int bits) {
+ return ((((bits) + 31)>>5)<<2);
+}
+
+/** Calculates the size of a single icon image
+@return Returns the size for that image
+*/
+static DWORD
+CalculateImageSize(FIBITMAP* icon_dib) {
+ DWORD dwNumBytes = 0;
+
+ unsigned colors = FreeImage_GetColorsUsed(icon_dib);
+ unsigned width = FreeImage_GetWidth(icon_dib);
+ unsigned height = FreeImage_GetHeight(icon_dib);
+ unsigned pitch = FreeImage_GetPitch(icon_dib);
+
+ dwNumBytes = sizeof( BITMAPINFOHEADER ); // header
+ dwNumBytes += colors * sizeof(RGBQUAD); // palette
+ dwNumBytes += height * pitch; // XOR mask
+ dwNumBytes += height * WidthBytes(width); // AND mask
+
+ return dwNumBytes;
+}
+
+/** Calculates the file offset for an icon image
+@return Returns the file offset for that image
+*/
+static DWORD
+CalculateImageOffset(std::vector<FIBITMAP*>& vPages, int nIndex ) {
+ DWORD dwSize;
+
+ // calculate the ICO header size
+ dwSize = sizeof(ICONHEADER);
+ // add the ICONDIRENTRY's
+ dwSize += (DWORD)( vPages.size() * sizeof(ICONDIRENTRY));
+ // add the sizes of the previous images
+ for(int k = 0; k < nIndex; k++) {
+ FIBITMAP *icon_dib = (FIBITMAP*)vPages[k];
+ dwSize += CalculateImageSize(icon_dib);
+ }
+
+ return dwSize;
+}
+
+#ifdef FREEIMAGE_BIGENDIAN
+static void
+SwapInfoHeader(BITMAPINFOHEADER *header) {
+ SwapLong(&header->biSize);
+ SwapLong((DWORD *)&header->biWidth);
+ SwapLong((DWORD *)&header->biHeight);
+ SwapShort(&header->biPlanes);
+ SwapShort(&header->biBitCount);
+ SwapLong(&header->biCompression);
+ SwapLong(&header->biSizeImage);
+ SwapLong((DWORD *)&header->biXPelsPerMeter);
+ SwapLong((DWORD *)&header->biYPelsPerMeter);
+ SwapLong(&header->biClrUsed);
+ SwapLong(&header->biClrImportant);
+}
+
+static void
+SwapIconHeader(ICONHEADER *header) {
+ SwapShort(&header->idReserved);
+ SwapShort(&header->idType);
+ SwapShort(&header->idCount);
+}
+
+static void
+SwapIconDirEntries(ICONDIRENTRY *ent, int num) {
+ while(num) {
+ SwapShort(&ent->wPlanes);
+ SwapShort(&ent->wBitCount);
+ SwapLong(&ent->dwBytesInRes);
+ SwapLong(&ent->dwImageOffset);
+ num--;
+ ent++;
+ }
+}
+#endif
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+ return "ICO";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+ return "Windows Icon";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+ return "ico";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+ return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+ return "image/vnd.microsoft.icon";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+ ICONHEADER icon_header;
+
+ io->read_proc(&icon_header, sizeof(ICONHEADER), 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapIconHeader(&icon_header);
+#endif
+
+ return ((icon_header.idReserved == 0) && (icon_header.idType == 1) && (icon_header.idCount > 0));
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+ return (
+ (depth == 1) ||
+ (depth == 4) ||
+ (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;
+}
+
+// ----------------------------------------------------------
+
+static void * DLL_CALLCONV
+Open(FreeImageIO *io, fi_handle handle, BOOL read) {
+ // Allocate memory for the header structure
+ ICONHEADER *lpIH = (ICONHEADER*)malloc(sizeof(ICONHEADER));
+ if(lpIH == NULL) {
+ return NULL;
+ }
+
+ if (read) {
+ // Read in the header
+ io->read_proc(lpIH, 1, sizeof(ICONHEADER), handle);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapIconHeader(lpIH);
+#endif
+
+ if (!(lpIH->idReserved == 0) || !(lpIH->idType == 1)) {
+ // Not an ICO file
+ free(lpIH);
+ return NULL;
+ }
+ }
+ else {
+ // Fill the header
+ lpIH->idReserved = 0;
+ lpIH->idType = 1;
+ lpIH->idCount = 0;
+ }
+
+ return lpIH;
+}
+
+static void DLL_CALLCONV
+Close(FreeImageIO *io, fi_handle handle, void *data) {
+ // free the header structure
+ ICONHEADER *lpIH = (ICONHEADER*)data;
+ free(lpIH);
+}
+
+// ----------------------------------------------------------
+
+static int DLL_CALLCONV
+PageCount(FreeImageIO *io, fi_handle handle, void *data) {
+ ICONHEADER *lpIH = (ICONHEADER*)data;
+
+ if(lpIH) {
+ return lpIH->idCount;
+ }
+ return 1;
+}
+
+// ----------------------------------------------------------
+
+static FIBITMAP*
+LoadStandardIcon(FreeImageIO *io, fi_handle handle, int flags, BOOL header_only) {
+ FIBITMAP *dib = NULL;
+
+ // load the BITMAPINFOHEADER
+ BITMAPINFOHEADER bmih;
+ io->read_proc(&bmih, sizeof(BITMAPINFOHEADER), 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapInfoHeader(&bmih);
+#endif
+
+ // allocate the bitmap
+ int width = bmih.biWidth;
+ int height = bmih.biHeight / 2; // height == xor + and mask
+ unsigned bit_count = bmih.biBitCount;
+ unsigned line = CalculateLine(width, bit_count);
+ unsigned pitch = CalculatePitch(line);
+
+ // allocate memory for one icon
+
+ dib = FreeImage_AllocateHeader(header_only, width, height, bit_count);
+
+ if (dib == NULL) {
+ return NULL;
+ }
+
+ if ( bmih.biBitCount <= 8 ) {
+ // read the palette data
+ io->read_proc(FreeImage_GetPalette(dib), CalculateUsedPaletteEntries(bit_count) * sizeof(RGBQUAD), 1, handle);
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
+ RGBQUAD *pal = FreeImage_GetPalette(dib);
+ for(unsigned i = 0; i < CalculateUsedPaletteEntries(bit_count); i++) {
+ INPLACESWAP(pal[i].rgbRed, pal[i].rgbBlue);
+ }
+#endif
+ }
+
+ if(header_only) {
+ // header only mode
+ return dib;
+ }
+
+ // read the icon
+ io->read_proc(FreeImage_GetBits(dib), height * pitch, 1, handle);
+
+#ifdef FREEIMAGE_BIGENDIAN
+ if (bit_count == 16) {
+ for(int y = 0; y < height; y++) {
+ WORD *pixel = (WORD *)FreeImage_GetScanLine(dib, y);
+ for(int x = 0; x < width; x++) {
+ SwapShort(pixel);
+ pixel++;
+ }
+ }
+ }
+#endif
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
+ if (bit_count == 24 || bit_count == 32) {
+ for(int y = 0; y < height; y++) {
+ BYTE *pixel = FreeImage_GetScanLine(dib, y);
+ for(int x = 0; x < width; x++) {
+ INPLACESWAP(pixel[0], pixel[2]);
+ pixel += (bit_count>>3);
+ }
+ }
+ }
+#endif
+ // bitmap has been loaded successfully!
+
+ // convert to 32bpp and generate an alpha channel
+ if ((flags & ICO_MAKEALPHA) == ICO_MAKEALPHA) {
+ FIBITMAP *dib32 = FreeImage_ConvertTo32Bits(dib);
+ FreeImage_Unload(dib);
+
+ if (dib32 == NULL) {
+ return NULL;
+ }
+
+ int width_and = WidthBytes(width);
+ BYTE *line_and = (BYTE *)malloc(width_and);
+
+ if ( line_and == NULL ) {
+ FreeImage_Unload(dib32);
+ return NULL;
+ }
+
+ //loop through each line of the AND-mask generating the alpha channel, invert XOR-mask
+ for(int y = 0; y < height; y++) {
+ RGBQUAD *quad = (RGBQUAD *)FreeImage_GetScanLine(dib32, y);
+ io->read_proc(line_and, width_and, 1, handle);
+ for(int x = 0; x < width; x++) {
+ quad->rgbReserved = (line_and[x>>3] & (0x80 >> (x & 0x07))) != 0 ? 0 : 0xFF;
+ if ( quad->rgbReserved == 0 ) {
+ quad->rgbBlue ^= 0xFF;
+ quad->rgbGreen ^= 0xFF;
+ quad->rgbRed ^= 0xFF;
+ }
+ quad++;
+ }
+ }
+ free(line_and);
+
+ return dib32;
+ }
+
+ return dib;
+}
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+ if (page == -1) {
+ page = 0;
+ }
+
+ BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+ if (handle != NULL) {
+ FIBITMAP *dib = NULL;
+
+ // get the icon header
+ ICONHEADER *icon_header = (ICONHEADER*)data;
+
+ if (icon_header) {
+ // load the icon descriptions
+ ICONDIRENTRY *icon_list = (ICONDIRENTRY*)malloc(icon_header->idCount * sizeof(ICONDIRENTRY));
+ if(icon_list == NULL) {
+ return NULL;
+ }
+ io->seek_proc(handle, sizeof(ICONHEADER), SEEK_SET);
+ io->read_proc(icon_list, icon_header->idCount * sizeof(ICONDIRENTRY), 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapIconDirEntries(icon_list, icon_header->idCount);
+#endif
+
+ // load the requested icon
+ if (page < icon_header->idCount) {
+ // seek to the start of the bitmap data for the icon
+ io->seek_proc(handle, 0, SEEK_SET);
+ io->seek_proc(handle, icon_list[page].dwImageOffset, SEEK_CUR);
+
+ if ((icon_list[page].bWidth == 0) && (icon_list[page].bHeight == 0)) {
+ // Vista icon support
+ dib = FreeImage_LoadFromHandle(FIF_PNG, io, handle, header_only ? FIF_LOAD_NOPIXELS : PNG_DEFAULT);
+ }
+ else {
+ // standard icon support
+ // see http://msdn.microsoft.com/en-us/library/ms997538.aspx
+ dib = LoadStandardIcon(io, handle, flags, header_only);
+ }
+
+ free(icon_list);
+
+ return dib;
+
+ } else {
+ free(icon_list);
+ FreeImage_OutputMessageProc(s_format_id, "Page doesn't exist");
+ }
+ } else {
+ FreeImage_OutputMessageProc(s_format_id, "File is not an ICO file");
+ }
+ }
+
+ return NULL;
+}
+
+// ----------------------------------------------------------
+
+static BOOL
+SaveStandardIcon(FreeImageIO *io, FIBITMAP *dib, fi_handle handle) {
+ BITMAPINFOHEADER *bmih = NULL;
+
+ // write the BITMAPINFOHEADER
+ bmih = FreeImage_GetInfoHeader(dib);
+ bmih->biHeight *= 2; // height == xor + and mask
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapInfoHeader(bmih);
+#endif
+ io->write_proc(bmih, sizeof(BITMAPINFOHEADER), 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapInfoHeader(bmih);
+#endif
+ bmih->biHeight /= 2;
+
+ // write the palette data
+ if (FreeImage_GetPalette(dib) != NULL) {
+ RGBQUAD *pal = FreeImage_GetPalette(dib);
+ FILE_BGRA bgra;
+ for(unsigned i = 0; i < FreeImage_GetColorsUsed(dib); i++) {
+ bgra.b = pal[i].rgbBlue;
+ bgra.g = pal[i].rgbGreen;
+ bgra.r = pal[i].rgbRed;
+ bgra.a = pal[i].rgbReserved;
+ io->write_proc(&bgra, sizeof(FILE_BGRA), 1, handle);
+ }
+ }
+
+ // write the bits
+ int width = bmih->biWidth;
+ int height = bmih->biHeight;
+ unsigned bit_count = bmih->biBitCount;
+ unsigned line = CalculateLine(width, bit_count);
+ unsigned pitch = CalculatePitch(line);
+ int size_xor = height * pitch;
+ int size_and = height * WidthBytes(width);
+
+ // XOR mask
+#ifdef FREEIMAGE_BIGENDIAN
+ if (bit_count == 16) {
+ WORD pixel;
+ for(unsigned y = 0; y < FreeImage_GetHeight(dib); y++) {
+ BYTE *line = FreeImage_GetScanLine(dib, y);
+ for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
+ pixel = ((WORD *)line)[x];
+ SwapShort(&pixel);
+ if (io->write_proc(&pixel, sizeof(WORD), 1, handle) != 1)
+ return FALSE;
+ }
+ }
+ } else
+#endif
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
+ if (bit_count == 24) {
+ FILE_BGR bgr;
+ for(unsigned y = 0; y < FreeImage_GetHeight(dib); y++) {
+ BYTE *line = FreeImage_GetScanLine(dib, y);
+ for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
+ RGBTRIPLE *triple = ((RGBTRIPLE *)line)+x;
+ bgr.b = triple->rgbtBlue;
+ bgr.g = triple->rgbtGreen;
+ bgr.r = triple->rgbtRed;
+ if (io->write_proc(&bgr, sizeof(FILE_BGR), 1, handle) != 1)
+ return FALSE;
+ }
+ }
+ } else if (bit_count == 32) {
+ FILE_BGRA bgra;
+ for(unsigned y = 0; y < FreeImage_GetHeight(dib); y++) {
+ BYTE *line = FreeImage_GetScanLine(dib, y);
+ for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
+ RGBQUAD *quad = ((RGBQUAD *)line)+x;
+ bgra.b = quad->rgbBlue;
+ bgra.g = quad->rgbGreen;
+ bgra.r = quad->rgbRed;
+ bgra.a = quad->rgbReserved;
+ if (io->write_proc(&bgra, sizeof(FILE_BGRA), 1, handle) != 1)
+ return FALSE;
+ }
+ }
+ } else
+#endif
+#if defined(FREEIMAGE_BIGENDIAN) || FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
+ {
+#endif
+ BYTE *xor_mask = FreeImage_GetBits(dib);
+ io->write_proc(xor_mask, size_xor, 1, handle);
+#if defined(FREEIMAGE_BIGENDIAN) || FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
+ }
+#endif
+ // AND mask
+ BYTE *and_mask = (BYTE*)malloc(size_and);
+ if (!and_mask) {
+ return FALSE;
+ }
+
+ if(FreeImage_IsTransparent(dib)) {
+
+ if(bit_count == 32) {
+ // create the AND mask from the alpha channel
+
+ int width_and = WidthBytes(width);
+ BYTE *and_bits = and_mask;
+
+ // clear the mask
+ memset(and_mask, 0, size_and);
+
+ for(int y = 0; y < height; y++) {
+ RGBQUAD *bits = (RGBQUAD*)FreeImage_GetScanLine(dib, y);
+
+ for(int x = 0; x < width; x++) {
+ if(bits[x].rgbReserved != 0xFF) {
+ // set any transparent color to full transparency
+ and_bits[x >> 3] |= (0x80 >> (x & 0x7));
+ }
+ }
+
+ and_bits += width_and;
+ }
+ }
+ else if(bit_count <= 8) {
+ // create the AND mask from the transparency table
+
+ BYTE *trns = FreeImage_GetTransparencyTable(dib);
+
+ int width_and = WidthBytes(width);
+ BYTE *and_bits = and_mask;
+
+ // clear the mask
+ memset(and_mask, 0, size_and);
+
+ switch(FreeImage_GetBPP(dib)) {
+ case 1:
+ {
+ for(int y = 0; y < height; y++) {
+ BYTE *bits = (BYTE*)FreeImage_GetScanLine(dib, y);
+ for(int x = 0; x < width; x++) {
+ // get pixel at (x, y)
+ BYTE index = (bits[x >> 3] & (0x80 >> (x & 0x07))) != 0;
+ if(trns[index] != 0xFF) {
+ // set any transparent color to full transparency
+ and_bits[x >> 3] |= (0x80 >> (x & 0x7));
+ }
+ }
+ and_bits += width_and;
+ }
+ }
+ break;
+
+ case 4:
+ {
+ for(int y = 0; y < height; y++) {
+ BYTE *bits = (BYTE*)FreeImage_GetScanLine(dib, y);
+ for(int x = 0; x < width; x++) {
+ // get pixel at (x, y)
+ BYTE shift = (BYTE)((1 - x % 2) << 2);
+ BYTE index = (bits[x >> 1] & (0x0F << shift)) >> shift;
+ if(trns[index] != 0xFF) {
+ // set any transparent color to full transparency
+ and_bits[x >> 3] |= (0x80 >> (x & 0x7));
+ }
+ }
+ and_bits += width_and;
+ }
+ }
+ break;
+
+ case 8:
+ {
+ for(int y = 0; y < height; y++) {
+ BYTE *bits = (BYTE*)FreeImage_GetScanLine(dib, y);
+ for(int x = 0; x < width; x++) {
+ // get pixel at (x, y)
+ BYTE index = bits[x];
+ if(trns[index] != 0xFF) {
+ // set any transparent color to full transparency
+ and_bits[x >> 3] |= (0x80 >> (x & 0x7));
+ }
+ }
+ and_bits += width_and;
+ }
+ }
+ break;
+
+ }
+ }
+ }
+ else {
+ // empty AND mask
+ memset(and_mask, 0, size_and);
+ }
+
+ io->write_proc(and_mask, size_and, 1, handle);
+ free(and_mask);
+
+ return TRUE;
+}
+
+static BOOL DLL_CALLCONV
+Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
+ ICONHEADER *icon_header = NULL;
+ std::vector<FIBITMAP*> vPages;
+ int k;
+
+ if (!dib || !handle || !data) {
+ return FALSE;
+ }
+
+ // check format limits
+ unsigned w = FreeImage_GetWidth(dib);
+ unsigned h = FreeImage_GetHeight(dib);
+ if ((w < 16) || (w > 256) || (h < 16) || (h > 256) || (w != h)) {
+ FreeImage_OutputMessageProc(s_format_id, "Unsupported icon size: width x height = %d x %d", w, h);
+ return FALSE;
+ }
+
+ if (page == -1) {
+ page = 0;
+ }
+
+ // get the icon header
+ icon_header = (ICONHEADER*)data;
+
+ try {
+ FIBITMAP *icon_dib = NULL;
+
+ // load all icons
+ for(k = 0; k < icon_header->idCount; k++) {
+ icon_dib = Load(io, handle, k, flags, data);
+ if (!icon_dib) {
+ throw FI_MSG_ERROR_DIB_MEMORY;
+ }
+ vPages.push_back(icon_dib);
+ }
+
+ // add the page
+ icon_dib = FreeImage_Clone(dib);
+ vPages.push_back(icon_dib);
+ icon_header->idCount++;
+
+ // write the header
+ io->seek_proc(handle, 0, SEEK_SET);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapIconHeader(icon_header);
+#endif
+ io->write_proc(icon_header, sizeof(ICONHEADER), 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapIconHeader(icon_header);
+#endif
+
+ // write all icons
+ // ...
+
+ // save the icon descriptions
+
+ ICONDIRENTRY *icon_list = (ICONDIRENTRY *)malloc(icon_header->idCount * sizeof(ICONDIRENTRY));
+ if (!icon_list) {
+ throw FI_MSG_ERROR_MEMORY;
+ }
+ memset(icon_list, 0, icon_header->idCount * sizeof(ICONDIRENTRY));
+
+ for(k = 0; k < icon_header->idCount; k++) {
+ icon_dib = (FIBITMAP*)vPages[k];
+
+ // convert internal format to ICONDIRENTRY
+ // take into account Vista icons whose size is 256x256
+ const BITMAPINFOHEADER *bmih = FreeImage_GetInfoHeader(icon_dib);
+ icon_list[k].bWidth = (bmih->biWidth > 255) ? 0 : (BYTE)bmih->biWidth;
+ icon_list[k].bHeight = (bmih->biHeight > 255) ? 0 : (BYTE)bmih->biHeight;
+ icon_list[k].bReserved = 0;
+ icon_list[k].wPlanes = bmih->biPlanes;
+ icon_list[k].wBitCount = bmih->biBitCount;
+ if ( (icon_list[k].wPlanes * icon_list[k].wBitCount) >= 8 ) {
+ icon_list[k].bColorCount = 0;
+ } else {
+ icon_list[k].bColorCount = (BYTE)(1 << (icon_list[k].wPlanes * icon_list[k].wBitCount));
+ }
+ // initial guess (correct only for standard icons)
+ icon_list[k].dwBytesInRes = CalculateImageSize(icon_dib);
+ icon_list[k].dwImageOffset = CalculateImageOffset(vPages, k);
+ }
+
+ // make a room for icon dir entries, until later update
+ const long directory_start = io->tell_proc(handle);
+ io->write_proc(icon_list, sizeof(ICONDIRENTRY) * icon_header->idCount, 1, handle);
+
+ // write the image bits for each image
+
+ DWORD dwImageOffset = (DWORD)io->tell_proc(handle);
+
+ for(k = 0; k < icon_header->idCount; k++) {
+ icon_dib = (FIBITMAP*)vPages[k];
+
+ if ((icon_list[k].bWidth == 0) && (icon_list[k].bHeight == 0)) {
+ // Vista icon support
+ FreeImage_SaveToHandle(FIF_PNG, icon_dib, io, handle, PNG_DEFAULT);
+ }
+ else {
+ // standard icon support
+ // see http://msdn.microsoft.com/en-us/library/ms997538.aspx
+ SaveStandardIcon(io, icon_dib, handle);
+ }
+
+ // update ICONDIRENTRY members
+ DWORD dwBytesInRes = (DWORD)io->tell_proc(handle) - dwImageOffset;
+ icon_list[k].dwImageOffset = dwImageOffset;
+ icon_list[k].dwBytesInRes = dwBytesInRes;
+ dwImageOffset += dwBytesInRes;
+ }
+
+ // update the icon descriptions
+ const long current_pos = io->tell_proc(handle);
+ io->seek_proc(handle, directory_start, SEEK_SET);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapIconDirEntries(icon_list, icon_header->idCount);
+#endif
+ io->write_proc(icon_list, sizeof(ICONDIRENTRY) * icon_header->idCount, 1, handle);
+ io->seek_proc(handle, current_pos, SEEK_SET);
+
+ free(icon_list);
+
+ // free the vector class
+ for(k = 0; k < icon_header->idCount; k++) {
+ icon_dib = (FIBITMAP*)vPages[k];
+ FreeImage_Unload(icon_dib);
+ }
+
+ return TRUE;
+
+ } catch(const char *text) {
+ // free the vector class
+ for(size_t k = 0; k < vPages.size(); k++) {
+ FIBITMAP *icon_dib = (FIBITMAP*)vPages[k];
+ FreeImage_Unload(icon_dib);
+ }
+ FreeImage_OutputMessageProc(s_format_id, text);
+ return FALSE;
+ }
+}
+
+// ==========================================================
+// Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitICO(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 = Open;
+ plugin->close_proc = Close;
+ plugin->pagecount_proc = PageCount;
+ 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;
+}