Coverage Report

Created: 2025-12-14 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeimage-svn/FreeImage/trunk/Source/FreeImage/PluginICO.cpp
Line
Count
Source
1
// ==========================================================
2
// ICO Loader and Writer
3
//
4
// Design and implementation by
5
// - Floris van den Berg (flvdberg@wxs.nl)
6
// - Hervé Drolon (drolon@infonie.fr)
7
//
8
// This file is part of FreeImage 3
9
//
10
// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
11
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
12
// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
13
// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
14
// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
15
// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
16
// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
17
// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
18
// THIS DISCLAIMER.
19
//
20
// Use at your own risk!
21
// ==========================================================
22
23
#include "FreeImage.h"
24
#include "Utilities.h"
25
26
// ----------------------------------------------------------
27
//   Constants + headers
28
// ----------------------------------------------------------
29
30
#ifdef _WIN32
31
#pragma pack(push, 1)
32
#else
33
#pragma pack(1)
34
#endif
35
36
// These next two structs represent how the icon information is stored
37
// in an ICO file.
38
39
typedef struct tagICONHEADER {
40
  WORD      idReserved;   // reserved
41
  WORD      idType;       // resource type (1 for icons)
42
  WORD      idCount;      // how many images?
43
} ICONHEADER;
44
45
typedef struct tagICONDIRECTORYENTRY {
46
  BYTE  bWidth;               // width of the image
47
  BYTE  bHeight;              // height of the image (times 2)
48
  BYTE  bColorCount;          // number of colors in image (0 if >=8bpp)
49
  BYTE  bReserved;            // reserved
50
  WORD  wPlanes;              // color Planes
51
  WORD  wBitCount;            // bits per pixel
52
  DWORD dwBytesInRes;         // how many bytes in this resource?
53
  DWORD dwImageOffset;        // where in the file is this image
54
} ICONDIRENTRY;
55
56
#ifdef _WIN32
57
#pragma pack(pop)
58
#else
59
#pragma pack()
60
#endif
61
62
// ==========================================================
63
// Static helpers
64
// ==========================================================
65
66
/**  How wide, in bytes, would this many bits be, DWORD aligned ?
67
*/
68
static int 
69
0
WidthBytes(int bits) {
70
0
  return ((((bits) + 31)>>5)<<2);
71
0
}
72
73
/** Calculates the size of a single icon image
74
@return Returns the size for that image
75
*/
76
static DWORD 
77
0
CalculateImageSize(FIBITMAP* icon_dib) {
78
0
  DWORD dwNumBytes = 0;
79
80
0
  unsigned colors   = FreeImage_GetColorsUsed(icon_dib);
81
0
  unsigned width    = FreeImage_GetWidth(icon_dib);
82
0
  unsigned height   = FreeImage_GetHeight(icon_dib);
83
0
  unsigned pitch    = FreeImage_GetPitch(icon_dib);
84
85
0
  dwNumBytes = sizeof( BITMAPINFOHEADER );  // header
86
0
  dwNumBytes += colors * sizeof(RGBQUAD);   // palette
87
0
  dwNumBytes += height * pitch;       // XOR mask
88
0
  dwNumBytes += height * WidthBytes(width); // AND mask
89
90
0
  return dwNumBytes;
91
0
}
92
93
/** Calculates the file offset for an icon image
94
@return Returns the file offset for that image
95
*/
96
static DWORD 
97
0
CalculateImageOffset(std::vector<FIBITMAP*>& vPages, int nIndex ) {
98
0
  DWORD dwSize;
99
100
    // calculate the ICO header size
101
0
    dwSize = sizeof(ICONHEADER); 
102
    // add the ICONDIRENTRY's
103
0
    dwSize += (DWORD)( vPages.size() * sizeof(ICONDIRENTRY) );
104
    // add the sizes of the previous images
105
0
    for(int k = 0; k < nIndex; k++) {
106
0
    FIBITMAP *icon_dib = (FIBITMAP*)vPages[k];
107
0
    dwSize += CalculateImageSize(icon_dib);
108
0
  }
109
110
0
    return dwSize;
111
0
}
112
113
/**
114
Vista icon support
115
@return Returns TRUE if the bitmap data is stored in PNG format
116
*/
117
static BOOL
118
0
IsPNG(FreeImageIO *io, fi_handle handle) {
119
0
  BYTE png_signature[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };
120
0
  BYTE signature[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
121
122
0
  long tell = io->tell_proc(handle);
123
0
  io->read_proc(&signature, 1, 8, handle);
124
0
  BOOL bIsPNG = (memcmp(png_signature, signature, 8) == 0);
125
0
  io->seek_proc(handle, tell, SEEK_SET);
126
127
0
  return bIsPNG;
128
0
}
129
130
#ifdef FREEIMAGE_BIGENDIAN
131
static void
132
SwapInfoHeader(BITMAPINFOHEADER *header) {
133
  SwapLong(&header->biSize);
134
  SwapLong((DWORD *)&header->biWidth);
135
  SwapLong((DWORD *)&header->biHeight);
136
  SwapShort(&header->biPlanes);
137
  SwapShort(&header->biBitCount);
138
  SwapLong(&header->biCompression);
139
  SwapLong(&header->biSizeImage);
140
  SwapLong((DWORD *)&header->biXPelsPerMeter);
141
  SwapLong((DWORD *)&header->biYPelsPerMeter);
142
  SwapLong(&header->biClrUsed);
143
  SwapLong(&header->biClrImportant);
144
}
145
146
static void
147
SwapIconHeader(ICONHEADER *header) {
148
  SwapShort(&header->idReserved);
149
  SwapShort(&header->idType);
150
  SwapShort(&header->idCount);
151
}
152
153
static void
154
SwapIconDirEntries(ICONDIRENTRY *ent, int num) {
155
  while(num) {
156
    SwapShort(&ent->wPlanes);
157
    SwapShort(&ent->wBitCount);
158
    SwapLong(&ent->dwBytesInRes);
159
    SwapLong(&ent->dwImageOffset);
160
    num--;
161
    ent++;
162
  }
163
}
164
#endif
165
166
// ==========================================================
167
// Plugin Interface
168
// ==========================================================
169
170
static int s_format_id;
171
172
// ==========================================================
173
// Plugin Implementation
174
// ==========================================================
175
176
static const char * DLL_CALLCONV
177
2
Format() {
178
2
  return "ICO";
179
2
}
180
181
static const char * DLL_CALLCONV
182
0
Description() {
183
0
  return "Windows Icon";
184
0
}
185
186
static const char * DLL_CALLCONV
187
0
Extension() {
188
0
  return "ico";
189
0
}
190
191
static const char * DLL_CALLCONV
192
0
RegExpr() {
193
0
  return NULL;
194
0
}
195
196
static const char * DLL_CALLCONV
197
0
MimeType() {
198
0
  return "image/vnd.microsoft.icon";
199
0
}
200
201
static BOOL DLL_CALLCONV
202
37.1k
Validate(FreeImageIO *io, fi_handle handle) {
203
37.1k
  ICONHEADER icon_header;
204
205
37.1k
  io->read_proc(&icon_header, sizeof(ICONHEADER), 1, handle);
206
#ifdef FREEIMAGE_BIGENDIAN
207
  SwapIconHeader(&icon_header);
208
#endif
209
210
37.1k
  return ((icon_header.idReserved == 0) && (icon_header.idType == 1) && (icon_header.idCount > 0));
211
37.1k
}
212
213
static BOOL DLL_CALLCONV
214
0
SupportsExportDepth(int depth) {
215
0
  return (
216
0
      (depth == 1) ||
217
0
      (depth == 4) ||
218
0
      (depth == 8) ||
219
0
      (depth == 16) ||
220
0
      (depth == 24) ||
221
0
      (depth == 32)
222
0
    );
223
0
}
224
225
static BOOL DLL_CALLCONV 
226
0
SupportsExportType(FREE_IMAGE_TYPE type) {
227
0
  return (type == FIT_BITMAP) ? TRUE : FALSE;
228
0
}
229
230
static BOOL DLL_CALLCONV
231
0
SupportsNoPixels() {
232
0
  return TRUE;
233
0
}
234
235
// ----------------------------------------------------------
236
237
static void * DLL_CALLCONV
238
0
Open(FreeImageIO *io, fi_handle handle, BOOL read) {
239
  // Allocate memory for the header structure
240
0
  ICONHEADER *lpIH = (ICONHEADER*)malloc(sizeof(ICONHEADER));
241
0
  if(lpIH == NULL) {
242
0
    return NULL;
243
0
  }
244
245
0
  if (read) {
246
    // Read in the header
247
0
    io->read_proc(lpIH, 1, sizeof(ICONHEADER), handle);
248
#ifdef FREEIMAGE_BIGENDIAN
249
    SwapIconHeader(lpIH);
250
#endif
251
252
0
    if(!(lpIH->idReserved == 0) || !(lpIH->idType == 1)) {
253
      // Not an ICO file
254
0
      free(lpIH);
255
0
      return NULL;
256
0
    }
257
0
  }
258
0
  else {
259
    // Fill the header
260
0
    lpIH->idReserved = 0;
261
0
    lpIH->idType = 1;
262
0
    lpIH->idCount = 0;
263
0
  }
264
265
0
  return lpIH;
266
0
}
267
268
static void DLL_CALLCONV
269
0
Close(FreeImageIO *io, fi_handle handle, void *data) {
270
  // free the header structure
271
0
  ICONHEADER *lpIH = (ICONHEADER*)data;
272
0
  free(lpIH);
273
0
}
274
275
// ----------------------------------------------------------
276
277
static int DLL_CALLCONV
278
0
PageCount(FreeImageIO *io, fi_handle handle, void *data) {
279
0
  ICONHEADER *lpIH = (ICONHEADER*)data;
280
281
0
  if(lpIH) {
282
0
    return lpIH->idCount;
283
0
  }
284
0
  return 1;
285
0
}
286
287
// ----------------------------------------------------------
288
289
static FIBITMAP*
290
0
LoadStandardIcon(FreeImageIO *io, fi_handle handle, int flags, BOOL header_only) {
291
0
  FIBITMAP *dib = NULL;
292
293
  // load the BITMAPINFOHEADER
294
0
  BITMAPINFOHEADER bmih;
295
0
  io->read_proc(&bmih, sizeof(BITMAPINFOHEADER), 1, handle);
296
#ifdef FREEIMAGE_BIGENDIAN
297
  SwapInfoHeader(&bmih);
298
#endif
299
300
  // allocate the bitmap
301
0
  int width  = bmih.biWidth;
302
0
  int height = bmih.biHeight / 2; // height == xor + and mask
303
0
  unsigned bit_count = bmih.biBitCount;
304
0
  unsigned line   = CalculateLine(width, bit_count);
305
0
  unsigned pitch  = CalculatePitch(line);
306
307
  // allocate memory for one icon
308
309
0
  dib = FreeImage_AllocateHeader(header_only, width, height, bit_count);
310
311
0
  if (dib == NULL) {
312
0
    return NULL;
313
0
  }
314
315
0
  if( bmih.biBitCount <= 8 ) {
316
    // read the palette data
317
0
    io->read_proc(FreeImage_GetPalette(dib), CalculateUsedPaletteEntries(bit_count) * sizeof(RGBQUAD), 1, handle);
318
#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
319
    RGBQUAD *pal = FreeImage_GetPalette(dib);
320
    for(unsigned i = 0; i < CalculateUsedPaletteEntries(bit_count); i++) {
321
      INPLACESWAP(pal[i].rgbRed, pal[i].rgbBlue);
322
    }
323
#endif
324
0
  }
325
  
326
0
  if(header_only) {
327
    // header only mode
328
0
    return dib;
329
0
  }
330
331
  // read the icon
332
0
  io->read_proc(FreeImage_GetBits(dib), height * pitch, 1, handle);
333
334
#ifdef FREEIMAGE_BIGENDIAN
335
  if (bit_count == 16) {
336
    for(int y = 0; y < height; y++) {
337
      WORD *pixel = (WORD *)FreeImage_GetScanLine(dib, y);
338
      for(int x = 0; x < width; x++) {
339
        SwapShort(pixel);
340
        pixel++;
341
      }
342
    }
343
  }
344
#endif
345
#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
346
  if (bit_count == 24 || bit_count == 32) {
347
    for(int y = 0; y < height; y++) {
348
      BYTE *pixel = FreeImage_GetScanLine(dib, y);
349
      for(int x = 0; x < width; x++) {
350
        INPLACESWAP(pixel[0], pixel[2]);
351
        pixel += (bit_count>>3);
352
      }
353
    }
354
  }
355
#endif
356
  // bitmap has been loaded successfully!
357
358
  // convert to 32bpp and generate an alpha channel
359
  // apply the AND mask only if the image is not 32 bpp
360
0
  if(((flags & ICO_MAKEALPHA) == ICO_MAKEALPHA) && (bit_count < 32)) {
361
0
    FIBITMAP *dib32 = FreeImage_ConvertTo32Bits(dib);
362
0
    FreeImage_Unload(dib);
363
364
0
    if (dib32 == NULL) {
365
0
      return NULL;
366
0
    }
367
368
0
    int width_and = WidthBytes(width);
369
0
    BYTE *line_and  = (BYTE *)malloc(width_and);
370
371
0
    if( line_and == NULL ) {
372
0
      FreeImage_Unload(dib32);
373
0
      return NULL;
374
0
    }
375
376
    //loop through each line of the AND-mask generating the alpha channel, invert XOR-mask
377
0
    for(int y = 0; y < height; y++) {
378
0
      RGBQUAD *quad = (RGBQUAD *)FreeImage_GetScanLine(dib32, y);
379
0
      io->read_proc(line_and, width_and, 1, handle);
380
0
      for(int x = 0; x < width; x++) {
381
0
        quad->rgbReserved = (line_and[x>>3] & (0x80 >> (x & 0x07))) != 0 ? 0 : 0xFF;
382
0
        if( quad->rgbReserved == 0 ) {
383
0
          quad->rgbBlue ^= 0xFF;
384
0
          quad->rgbGreen ^= 0xFF;
385
0
          quad->rgbRed ^= 0xFF;
386
0
        }
387
0
        quad++;
388
0
      }
389
0
    }
390
0
    free(line_and);
391
392
0
    return dib32;
393
0
  }
394
395
0
  return dib;
396
0
}
397
398
static FIBITMAP * DLL_CALLCONV
399
0
Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
400
0
  if (page == -1) {
401
0
    page = 0;
402
0
  }
403
404
0
  BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
405
406
0
  if (handle != NULL) {
407
0
    FIBITMAP *dib = NULL;
408
409
    // get the icon header
410
0
    ICONHEADER *icon_header = (ICONHEADER*)data;
411
412
0
    if (icon_header) {
413
      // load the icon descriptions
414
0
      ICONDIRENTRY *icon_list = (ICONDIRENTRY*)malloc(icon_header->idCount * sizeof(ICONDIRENTRY));
415
0
      if(icon_list == NULL) {
416
0
        return NULL;
417
0
      }
418
0
      io->seek_proc(handle, sizeof(ICONHEADER), SEEK_SET);
419
0
      io->read_proc(icon_list, icon_header->idCount * sizeof(ICONDIRENTRY), 1, handle);
420
#ifdef FREEIMAGE_BIGENDIAN
421
      SwapIconDirEntries(icon_list, icon_header->idCount);
422
#endif
423
424
      // load the requested icon
425
0
      if (page < icon_header->idCount) {
426
        // seek to the start of the bitmap data for the icon
427
0
        io->seek_proc(handle, icon_list[page].dwImageOffset, SEEK_SET);
428
429
0
        if( IsPNG(io, handle) ) {
430
          // Vista icon support
431
          // see http://blogs.msdn.com/b/oldnewthing/archive/2010/10/22/10079192.aspx
432
0
          dib = FreeImage_LoadFromHandle(FIF_PNG, io, handle, header_only ? FIF_LOAD_NOPIXELS : PNG_DEFAULT);
433
0
        }
434
0
        else {
435
          // standard icon support
436
          // see http://msdn.microsoft.com/en-us/library/ms997538.aspx
437
          // see http://blogs.msdn.com/b/oldnewthing/archive/2010/10/18/10077133.aspx
438
0
          dib = LoadStandardIcon(io, handle, flags, header_only);
439
0
        }
440
441
0
        free(icon_list);
442
443
0
        return dib;
444
445
0
      } else {
446
0
        free(icon_list);
447
0
        FreeImage_OutputMessageProc(s_format_id, "Page doesn't exist");
448
0
      }
449
0
    } else {
450
0
      FreeImage_OutputMessageProc(s_format_id, "File is not an ICO file");
451
0
    }
452
0
  }
453
454
0
  return NULL;
455
0
}
456
457
// ----------------------------------------------------------
458
459
static BOOL 
460
0
SaveStandardIcon(FreeImageIO *io, FIBITMAP *dib, fi_handle handle) {
461
0
  BITMAPINFOHEADER *bmih = NULL;
462
463
  // write the BITMAPINFOHEADER
464
0
  bmih = FreeImage_GetInfoHeader(dib);
465
0
  bmih->biHeight *= 2;  // height == xor + and mask
466
#ifdef FREEIMAGE_BIGENDIAN
467
  SwapInfoHeader(bmih);
468
#endif
469
0
  io->write_proc(bmih, sizeof(BITMAPINFOHEADER), 1, handle);
470
#ifdef FREEIMAGE_BIGENDIAN
471
  SwapInfoHeader(bmih);
472
#endif
473
0
  bmih->biHeight /= 2;
474
475
  // write the palette data
476
0
  if (FreeImage_GetPalette(dib) != NULL) {
477
0
    RGBQUAD *pal = FreeImage_GetPalette(dib);
478
0
    FILE_BGRA bgra;
479
0
    for(unsigned i = 0; i < FreeImage_GetColorsUsed(dib); i++) {
480
0
      bgra.b = pal[i].rgbBlue;
481
0
      bgra.g = pal[i].rgbGreen;
482
0
      bgra.r = pal[i].rgbRed;
483
0
      bgra.a = pal[i].rgbReserved;
484
0
      io->write_proc(&bgra, sizeof(FILE_BGRA), 1, handle);
485
0
    }
486
0
  }
487
488
  // write the bits
489
0
  int width     = bmih->biWidth;
490
0
  int height      = bmih->biHeight;
491
0
  unsigned bit_count  = bmih->biBitCount;
492
0
  unsigned line   = CalculateLine(width, bit_count);
493
0
  unsigned pitch    = CalculatePitch(line);
494
0
  int size_xor    = height * pitch;
495
0
  int size_and    = height * WidthBytes(width);
496
497
  // XOR mask
498
#ifdef FREEIMAGE_BIGENDIAN
499
  if (bit_count == 16) {
500
    WORD pixel;
501
    for(unsigned y = 0; y < FreeImage_GetHeight(dib); y++) {
502
      BYTE *line = FreeImage_GetScanLine(dib, y);
503
      for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
504
        pixel = ((WORD *)line)[x];
505
        SwapShort(&pixel);
506
        if (io->write_proc(&pixel, sizeof(WORD), 1, handle) != 1)
507
          return FALSE;
508
      }
509
    }
510
  } else
511
#endif
512
#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
513
  if (bit_count == 24) {
514
    FILE_BGR bgr;
515
    for(unsigned y = 0; y < FreeImage_GetHeight(dib); y++) {
516
      BYTE *line = FreeImage_GetScanLine(dib, y);
517
      for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
518
        RGBTRIPLE *triple = ((RGBTRIPLE *)line)+x;
519
        bgr.b = triple->rgbtBlue;
520
        bgr.g = triple->rgbtGreen;
521
        bgr.r = triple->rgbtRed;
522
        if (io->write_proc(&bgr, sizeof(FILE_BGR), 1, handle) != 1)
523
          return FALSE;
524
      }
525
    }
526
  } else if (bit_count == 32) {
527
    FILE_BGRA bgra;
528
    for(unsigned y = 0; y < FreeImage_GetHeight(dib); y++) {
529
      BYTE *line = FreeImage_GetScanLine(dib, y);
530
      for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
531
        RGBQUAD *quad = ((RGBQUAD *)line)+x;
532
        bgra.b = quad->rgbBlue;
533
        bgra.g = quad->rgbGreen;
534
        bgra.r = quad->rgbRed;
535
        bgra.a = quad->rgbReserved;
536
        if (io->write_proc(&bgra, sizeof(FILE_BGRA), 1, handle) != 1)
537
          return FALSE;
538
      }
539
    }
540
  } else
541
#endif
542
#if defined(FREEIMAGE_BIGENDIAN) || FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
543
  {
544
#endif
545
0
    BYTE *xor_mask = FreeImage_GetBits(dib);
546
0
    io->write_proc(xor_mask, size_xor, 1, handle);
547
#if defined(FREEIMAGE_BIGENDIAN) || FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
548
  }
549
#endif
550
  // AND mask
551
0
  BYTE *and_mask = (BYTE*)malloc(size_and);
552
0
  if(!and_mask) {
553
0
    return FALSE;
554
0
  }
555
556
0
  if(FreeImage_IsTransparent(dib)) {
557
558
0
    if(bit_count == 32) {
559
      // create the AND mask from the alpha channel
560
561
0
      int width_and  = WidthBytes(width);
562
0
      BYTE *and_bits = and_mask;
563
564
      // clear the mask
565
0
      memset(and_mask, 0, size_and);
566
567
0
      for(int y = 0; y < height; y++) {
568
0
        RGBQUAD *bits = (RGBQUAD*)FreeImage_GetScanLine(dib, y);
569
570
0
        for(int x = 0; x < width; x++) {
571
0
          if(bits[x].rgbReserved != 0xFF) {
572
            // set any transparent color to full transparency
573
0
            and_bits[x >> 3] |= (0x80 >> (x & 0x7)); 
574
0
          }
575
0
        }
576
577
0
        and_bits += width_and;
578
0
      }
579
0
    } 
580
0
    else if(bit_count <= 8) {
581
      // create the AND mask from the transparency table
582
583
0
      BYTE *trns = FreeImage_GetTransparencyTable(dib);
584
585
0
      int width_and  = WidthBytes(width);
586
0
      BYTE *and_bits = and_mask;
587
588
      // clear the mask
589
0
      memset(and_mask, 0, size_and);
590
591
0
      switch(FreeImage_GetBPP(dib)) {
592
0
        case 1:
593
0
        {
594
0
          for(int y = 0; y < height; y++) {
595
0
            BYTE *bits = (BYTE*)FreeImage_GetScanLine(dib, y);
596
0
            for(int x = 0; x < width; x++) {
597
              // get pixel at (x, y)
598
0
              BYTE index = (bits[x >> 3] & (0x80 >> (x & 0x07))) != 0;
599
0
              if(trns[index] != 0xFF) {
600
                // set any transparent color to full transparency
601
0
                and_bits[x >> 3] |= (0x80 >> (x & 0x7)); 
602
0
              }
603
0
            }
604
0
            and_bits += width_and;
605
0
          }
606
0
        }
607
0
        break;
608
609
0
        case 4:
610
0
        {
611
0
          for(int y = 0; y < height; y++) {
612
0
            BYTE *bits = (BYTE*)FreeImage_GetScanLine(dib, y);
613
0
            for(int x = 0; x < width; x++) {
614
              // get pixel at (x, y)
615
0
              BYTE shift = (BYTE)((1 - x % 2) << 2);
616
0
              BYTE index = (bits[x >> 1] & (0x0F << shift)) >> shift;
617
0
              if(trns[index] != 0xFF) {
618
                // set any transparent color to full transparency
619
0
                and_bits[x >> 3] |= (0x80 >> (x & 0x7)); 
620
0
              }
621
0
            }
622
0
            and_bits += width_and;
623
0
          }
624
0
        }
625
0
        break;
626
627
0
        case 8:
628
0
        {
629
0
          for(int y = 0; y < height; y++) {
630
0
            BYTE *bits = (BYTE*)FreeImage_GetScanLine(dib, y);
631
0
            for(int x = 0; x < width; x++) {
632
              // get pixel at (x, y)
633
0
              BYTE index = bits[x];
634
0
              if(trns[index] != 0xFF) {
635
                // set any transparent color to full transparency
636
0
                and_bits[x >> 3] |= (0x80 >> (x & 0x7)); 
637
0
              }
638
0
            }
639
0
            and_bits += width_and;
640
0
          }
641
0
        }
642
0
        break;
643
644
0
      }
645
0
    }
646
0
  }
647
0
  else {
648
    // empty AND mask
649
0
    memset(and_mask, 0, size_and);
650
0
  }
651
652
0
  io->write_proc(and_mask, size_and, 1, handle);
653
0
  free(and_mask);
654
655
0
  return TRUE;
656
0
}
657
658
static BOOL DLL_CALLCONV
659
0
Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
660
0
  ICONHEADER *icon_header = NULL;
661
0
  std::vector<FIBITMAP*> vPages;
662
0
  int k;
663
664
0
  if(!dib || !handle || !data) {
665
0
    return FALSE;
666
0
  }
667
668
  // check format limits
669
0
  unsigned w = FreeImage_GetWidth(dib);
670
0
  unsigned h = FreeImage_GetHeight(dib);
671
0
  if((w < 16) || (w > 256) || (h < 16) || (h > 256) || (w != h)) {
672
0
    FreeImage_OutputMessageProc(s_format_id, "Unsupported icon size: width x height = %d x %d", w, h);
673
0
    return FALSE;
674
0
  }
675
676
0
  if (page == -1) {
677
0
    page = 0;
678
0
  }
679
  
680
  // get the icon header
681
0
  icon_header = (ICONHEADER*)data;
682
683
0
  try {
684
0
    FIBITMAP *icon_dib = NULL;
685
686
    // load all icons
687
0
    for(k = 0; k < icon_header->idCount; k++) {
688
0
      icon_dib = Load(io, handle, k, flags, data);
689
0
      if(!icon_dib) {
690
0
        throw FI_MSG_ERROR_DIB_MEMORY;
691
0
      }
692
0
      vPages.push_back(icon_dib);
693
0
    }
694
695
    // add the page
696
0
    icon_dib = FreeImage_Clone(dib);
697
0
    vPages.push_back(icon_dib);
698
0
    icon_header->idCount++;
699
700
    // write the header
701
0
    io->seek_proc(handle, 0, SEEK_SET);
702
#ifdef FREEIMAGE_BIGENDIAN
703
    SwapIconHeader(icon_header);
704
#endif
705
0
    io->write_proc(icon_header, sizeof(ICONHEADER), 1, handle);
706
#ifdef FREEIMAGE_BIGENDIAN
707
    SwapIconHeader(icon_header);
708
#endif
709
710
    // write all icons
711
    // ...
712
    
713
    // save the icon descriptions
714
715
0
    ICONDIRENTRY *icon_list = (ICONDIRENTRY *)malloc(icon_header->idCount * sizeof(ICONDIRENTRY));
716
0
    if(!icon_list) {
717
0
      throw FI_MSG_ERROR_MEMORY;
718
0
    }
719
0
    memset(icon_list, 0, icon_header->idCount * sizeof(ICONDIRENTRY));
720
721
0
    for(k = 0; k < icon_header->idCount; k++) {
722
0
      icon_dib = (FIBITMAP*)vPages[k];
723
724
      // convert internal format to ICONDIRENTRY
725
      // take into account Vista icons whose size is 256x256
726
0
      const BITMAPINFOHEADER *bmih = FreeImage_GetInfoHeader(icon_dib);
727
0
      icon_list[k].bWidth     = (bmih->biWidth > 255)  ? 0 : (BYTE)bmih->biWidth;
728
0
      icon_list[k].bHeight    = (bmih->biHeight > 255) ? 0 : (BYTE)bmih->biHeight;
729
0
      icon_list[k].bReserved    = 0;
730
0
      icon_list[k].wPlanes    = bmih->biPlanes;
731
0
      icon_list[k].wBitCount    = bmih->biBitCount;
732
0
      if( (icon_list[k].wPlanes * icon_list[k].wBitCount) >= 8 ) {
733
0
        icon_list[k].bColorCount = 0;
734
0
      } else {
735
0
        icon_list[k].bColorCount = (BYTE)(1 << (icon_list[k].wPlanes * icon_list[k].wBitCount));
736
0
      }
737
      // initial guess (correct only for standard icons)
738
0
      icon_list[k].dwBytesInRes = CalculateImageSize(icon_dib);
739
0
      icon_list[k].dwImageOffset = CalculateImageOffset(vPages, k);
740
0
    }
741
742
    // make a room for icon dir entries, until later update
743
0
    const long directory_start = io->tell_proc(handle);
744
0
    io->write_proc(icon_list, sizeof(ICONDIRENTRY) * icon_header->idCount, 1, handle);
745
746
    // write the image bits for each image
747
    
748
0
    DWORD dwImageOffset = (DWORD)io->tell_proc(handle);
749
750
0
    for(k = 0; k < icon_header->idCount; k++) {
751
0
      icon_dib = (FIBITMAP*)vPages[k];
752
      
753
0
      if((icon_list[k].bWidth == 0) && (icon_list[k].bHeight == 0)) {
754
        // Vista icon support
755
0
        FreeImage_SaveToHandle(FIF_PNG, icon_dib, io, handle, PNG_DEFAULT);
756
0
      }
757
0
      else {
758
        // standard icon support
759
        // see http://msdn.microsoft.com/en-us/library/ms997538.aspx
760
0
        SaveStandardIcon(io, icon_dib, handle);
761
0
      }
762
763
      // update ICONDIRENTRY members      
764
0
      DWORD dwBytesInRes = (DWORD)io->tell_proc(handle) - dwImageOffset;
765
0
      icon_list[k].dwImageOffset = dwImageOffset;
766
0
      icon_list[k].dwBytesInRes  = dwBytesInRes;
767
0
      dwImageOffset += dwBytesInRes;
768
0
    }
769
770
    // update the icon descriptions
771
0
    const long current_pos = io->tell_proc(handle);
772
0
    io->seek_proc(handle, directory_start, SEEK_SET);
773
#ifdef FREEIMAGE_BIGENDIAN
774
    SwapIconDirEntries(icon_list, icon_header->idCount);
775
#endif
776
0
    io->write_proc(icon_list, sizeof(ICONDIRENTRY) * icon_header->idCount, 1, handle);
777
0
    io->seek_proc(handle, current_pos, SEEK_SET);
778
779
0
    free(icon_list);
780
781
    // free the vector class
782
0
    for(k = 0; k < icon_header->idCount; k++) {
783
0
      icon_dib = (FIBITMAP*)vPages[k];
784
0
      FreeImage_Unload(icon_dib);
785
0
    }
786
787
0
    return TRUE;
788
789
0
  } catch(const char *text) {
790
    // free the vector class
791
0
    for(size_t k = 0; k < vPages.size(); k++) {
792
0
      FIBITMAP *icon_dib = (FIBITMAP*)vPages[k];
793
0
      FreeImage_Unload(icon_dib);
794
0
    }
795
0
    FreeImage_OutputMessageProc(s_format_id, text);
796
0
    return FALSE;
797
0
  }
798
0
}
799
800
// ==========================================================
801
//   Init
802
// ==========================================================
803
804
void DLL_CALLCONV
805
2
InitICO(Plugin *plugin, int format_id) {
806
2
  s_format_id = format_id;
807
808
2
  plugin->format_proc = Format;
809
2
  plugin->description_proc = Description;
810
2
  plugin->extension_proc = Extension;
811
2
  plugin->regexpr_proc = RegExpr;
812
2
  plugin->open_proc = Open;
813
2
  plugin->close_proc = Close;
814
2
  plugin->pagecount_proc = PageCount;
815
2
  plugin->pagecapability_proc = NULL;
816
2
  plugin->load_proc = Load;
817
2
  plugin->save_proc = Save;
818
2
  plugin->validate_proc = Validate;
819
2
  plugin->mime_proc = MimeType;
820
2
  plugin->supports_export_bpp_proc = SupportsExportDepth;
821
2
  plugin->supports_export_type_proc = SupportsExportType;
822
  plugin->supports_icc_profiles_proc = NULL;
823
2
  plugin->supports_no_pixels_proc = SupportsNoPixels;
824
2
}