ico-save.c 32 KB
Newer Older
1
/* GIMP - The GNU Image Manipulation Program
2
3
4
5
6
 * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
 *
 * GIMP Plug-in for Windows Icon files.
 * Copyright (C) 2002 Christian Kreibich <christian@whoop.org>.
 *
7
 * This program is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 3 of the License, or
10
11
12
13
14
15
16
17
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
18
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19
20
21
22
23
24
25
 */

#include "config.h"

#include <errno.h>
#include <string.h>

26
27
#include <glib/gstdio.h>

28
29
30
#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>

31
32
#include <png.h>

33
34
/* #define ICO_DBG */

Michael Natterer's avatar
Michael Natterer committed
35
36
37
38
#include "ico.h"
#include "ico-load.h"
#include "ico-save.h"
#include "ico-dialog.h"
39
40
41
42

#include "libgimp/stdplugins-intl.h"


Sven Neumann's avatar
Sven Neumann committed
43
44
45
46
47
48
49
50
51
static gint     ico_write_int8         (FILE   *fp,
                                        guint8 *data,
                                        gint    count);
static gint     ico_write_int16        (FILE *fp,
                                        guint16 *data,
                                        gint count);
static gint     ico_write_int32        (FILE    *fp,
                                        guint32 *data,
                                        gint     count);
52
53

/* Helpers to set bits in a *cleared* data chunk */
Sven Neumann's avatar
Sven Neumann committed
54
55
56
57
58
59
60
61
62
63
64
65
static void     ico_set_bit_in_data    (guint8 *data,
                                        gint    line_width,
                                        gint    bit_num,
                                        gint    bit_val);
static void     ico_set_nibble_in_data (guint8 *data,
                                        gint    line_width,
                                        gint    nibble_num,
                                        gint    nibble_val);
static void     ico_set_byte_in_data   (guint8 *data,
                                        gint    line_width,
                                        gint    byte_num,
                                        gint    byte_val);
66

67
68
69
70
71
72
73
static gint     ico_get_layer_num_colors  (GimpLayer    *layer,
                                           gboolean     *uses_alpha_levels);
static void     ico_image_get_reduced_buf (GimpDrawable *layer,
                                           gint          bpp,
                                           gint         *num_colors,
                                           guchar      **cmap_out,
                                           guchar      **buf_out);
74
75
76
77


static gint
ico_write_int32 (FILE     *fp,
78
79
                 guint32  *data,
                 gint      count)
80
{
81
  gint total;
82
83
84
85

  total = count;
  if (count > 0)
    {
86
87
88
#if (G_BYTE_ORDER == G_BIG_ENDIAN)
      gint i;

89
      for (i = 0; i < count; i++)
90
91
        data[i] = GUINT32_FROM_LE (data[i]);
#endif
92

93
      ico_write_int8 (fp, (guint8 *) data, count * 4);
94
95
96
97
98
99

#if (G_BYTE_ORDER == G_BIG_ENDIAN)
      /* Put it back like we found it */
      for (i = 0; i < count; i++)
        data[i] = GUINT32_FROM_LE (data[i]);
#endif
100
101
102
103
104
105
106
107
    }

  return total * 4;
}


static gint
ico_write_int16 (FILE     *fp,
108
109
                 guint16  *data,
                 gint      count)
110
{
111
  gint total;
112
113
114
115

  total = count;
  if (count > 0)
    {
116
117
118
#if (G_BYTE_ORDER == G_BIG_ENDIAN)
      gint i;

119
      for (i = 0; i < count; i++)
120
121
        data[i] = GUINT16_FROM_LE (data[i]);
#endif
122

123
      ico_write_int8 (fp, (guint8 *) data, count * 2);
124
125
126
127
128
129

#if (G_BYTE_ORDER == G_BIG_ENDIAN)
      /* Put it back like we found it */
      for (i = 0; i < count; i++)
        data[i] = GUINT16_FROM_LE (data[i]);
#endif
130
131
132
133
134
135
136
137
    }

  return total * 2;
}


static gint
ico_write_int8 (FILE     *fp,
138
139
                guint8   *data,
                gint      count)
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
{
  gint total;
  gint bytes;

  total = count;
  while (count > 0)
    {
      bytes = fwrite ((gchar *) data, sizeof (gchar), count, fp);
      if (bytes <= 0) /* something bad happened */
        break;
      count -= bytes;
      data += bytes;
    }

  return total;
}


Sven Neumann's avatar
Sven Neumann committed
158
static void
159
ico_save_init (GimpImage   *image,
160
               IcoSaveInfo *info)
161
{
162
163
164
  GList     *iter;
  gint       num_colors;
  gint       i;
165
  gboolean   uses_alpha_values = FALSE;
166

167
168
169
  info->layers         = gimp_image_list_layers (image);
  info->num_icons      = g_list_length (info->layers);
  info->depths         = g_new (gint, info->num_icons);
Sven Neumann's avatar
Sven Neumann committed
170
  info->default_depths = g_new (gint, info->num_icons);
171
  info->compress       = g_new (gboolean, info->num_icons);
172

173
174
175
176
177
178
179
180
  /* Limit the color depths to values that don't cause any color loss
   * -- the user should pick these anyway, so we can save her some
   * time.  If the user wants to lose some colors, the settings can
   * always be changed in the dialog:
   */
  for (iter = info->layers, i = 0;
       iter;
       iter = g_list_next (iter), i++)
181
    {
182
      num_colors = ico_get_layer_num_colors (iter->data, &uses_alpha_values);
183
184

      if (!uses_alpha_values)
185
186
187
188
        {
          if (num_colors <= 2)
            {
              /* Let's suggest monochrome */
Sven Neumann's avatar
Sven Neumann committed
189
              info->default_depths [i] = 1;
190
191
192
193
            }
          else if (num_colors <= 16)
            {
              /* Let's suggest 4bpp */
Sven Neumann's avatar
Sven Neumann committed
194
              info->default_depths [i] = 4;
195
196
197
198
            }
          else if (num_colors <= 256)
            {
              /* Let's suggest 8bpp */
Sven Neumann's avatar
Sven Neumann committed
199
              info->default_depths [i] = 8;
200
            }
201
202
203
204
205
          else
            {
              /* Let's suggest 24bpp */
              info->default_depths [i] = 24;
            }
206
        }
Sven Neumann's avatar
Sven Neumann committed
207
208
209
210
211
      else
        {
          /* Otherwise, or if real alpha levels are used, stick with 32bpp */
          info->default_depths [i] = 32;
        }
212

213
      /* vista icons */
214
215
      if (gimp_drawable_get_width  (iter->data) > 255 ||
          gimp_drawable_get_height (iter->data) > 255)
216
217
218
219
220
221
222
        {
          info->compress[i] = TRUE;
        }
      else
        {
          info->compress[i] = FALSE;
        }
223
224
    }

Sven Neumann's avatar
Sven Neumann committed
225
226
227
  /* set with default values */
  memcpy (info->depths, info->default_depths,
          sizeof (gint) * info->num_icons);
228
229
230
231
}



Sven Neumann's avatar
Sven Neumann committed
232
static gboolean
233
ico_save_dialog (GimpImage      *image,
Sven Neumann's avatar
Sven Neumann committed
234
                 IcoSaveInfo    *info)
235
{
Sven Neumann's avatar
Sven Neumann committed
236
  GtkWidget *dialog;
237
  GList     *iter;
Sven Neumann's avatar
Sven Neumann committed
238
239
  gint       i;
  gint       response;
240

241
  gimp_ui_init (PLUG_IN_BINARY);
242

Sven Neumann's avatar
Sven Neumann committed
243
  dialog = ico_dialog_new (info);
244
245
246
  for (iter = info->layers, i = 0;
       iter;
       iter = g_list_next (iter), i++)
Sven Neumann's avatar
Sven Neumann committed
247
248
    {
      /* if (gimp_layer_get_visible(layers[i])) */
249
      ico_dialog_add_icon (dialog, iter->data, i);
Sven Neumann's avatar
Sven Neumann committed
250
    }
251

Sven Neumann's avatar
Sven Neumann committed
252
253
254
  /* Scale the thing to approximately fit its content, but not too large ... */
  gtk_window_set_default_size (GTK_WINDOW (dialog),
                               -1,
255
                               200 + (info->num_icons > 4 ?
Sven Neumann's avatar
Sven Neumann committed
256
                                      500 : info->num_icons * 120));
257

Sven Neumann's avatar
Sven Neumann committed
258
  gtk_widget_show (dialog);
259

Sven Neumann's avatar
Sven Neumann committed
260
  response = gimp_dialog_run (GIMP_DIALOG (dialog));
261

Sven Neumann's avatar
Sven Neumann committed
262
  gtk_widget_destroy (dialog);
263
264

  return (response == GTK_RESPONSE_OK);
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
}

static void
ico_set_bit_in_data (guint8 *data,
                     gint    line_width,
                     gint    bit_num,
                     gint    bit_val)
{
  gint line;
  gint width32;
  gint offset;

  /* width per line in multiples of 32 bits */
  width32 = (line_width % 32 == 0 ? line_width/32 : line_width/32 + 1);

  line = bit_num / line_width;
  offset = bit_num % line_width;
  bit_val = bit_val & 0x00000001;

284
  data[line * width32 * 4 + offset/8] |= (bit_val << (7 - (offset % 8)));
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
}


static void
ico_set_nibble_in_data (guint8 *data,
                        gint    line_width,
                        gint    nibble_num,
                        gint    nibble_val)
{
  gint line;
  gint width8;
  gint offset;

  /* width per line in multiples of 32 bits */
  width8 = (line_width % 8 == 0 ? line_width/8 : line_width/8 + 1);

  line = nibble_num / line_width;
  offset = nibble_num % line_width;
  nibble_val = nibble_val & 0x0000000F;

Sven Neumann's avatar
Sven Neumann committed
305
306
  data[line * width8 * 4 + offset/2] |=
    (nibble_val << (4 * (1 - (offset % 2))));
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
}


static void
ico_set_byte_in_data (guint8 *data,
                      gint    line_width,
                      gint    byte_num,
                      gint    byte_val)
{
  gint line;
  gint width4;
  gint offset;
  gint byte;

  /* width per line in multiples of 32 bits */
  width4 = (line_width % 4 == 0 ? line_width/4 : line_width/4 + 1);

  line = byte_num / line_width;
  offset = byte_num % line_width;
  byte = byte_val & 0x000000FF;

  data[line * width4 * 4 + offset] = byte;
}


/* Create a colormap from the given buffer data */
static guint32 *
334
335
336
337
ico_create_palette (const guchar *cmap,
                    gint          num_colors,
                    gint          num_colors_used,
                    gint         *black_slot)
338
339
340
341
{
  guchar *palette;
  gint    i;

342
  g_return_val_if_fail (cmap != NULL || num_colors_used == 0, NULL);
343
344
  g_return_val_if_fail (num_colors_used <= num_colors, NULL);

345
346
347
348
349
350
351
352
353
354
  palette = g_new0 (guchar, num_colors * 4);
  *black_slot = -1;

  for (i = 0; i < num_colors_used; i++)
    {
      palette[i * 4 + 2] = cmap[i * 3];
      palette[i * 4 + 1] = cmap[i * 3 + 1];
      palette[i * 4]     = cmap[i * 3 + 2];

      if ((cmap[i*3]     == 0) &&
355
356
357
358
359
          (cmap[i*3 + 1] == 0) &&
          (cmap[i*3 + 2] == 0))
        {
          *black_slot = i;
        }
360
361
362
363
364
    }

  if (*black_slot == -1)
    {
      if (num_colors_used == num_colors)
365
366
367
368
369
370
371
372
        {
          D(("WARNING -- no room for black, this shouldn't happen.\n"));
          *black_slot = num_colors - 1;

          palette[(num_colors-1) * 4]     = 0;
          palette[(num_colors-1) * 4 + 1] = 0;
          palette[(num_colors-1) * 4 + 2] = 0;
        }
373
      else
374
375
376
        {
          *black_slot = num_colors_used;
        }
377
378
    }

379
  return (guint32 *) palette;
380
381
382
383
}


static GHashTable *
384
385
ico_create_color_to_palette_map (const guint32 *palette,
                                 gint           num_colors)
386
387
{
  GHashTable *hash;
388
  gint        i;
389

390
391
392
  hash = g_hash_table_new_full (g_int_hash, g_int_equal,
                                (GDestroyNotify) g_free,
                                (GDestroyNotify) g_free);
393
394
395

  for (i = 0; i < num_colors; i++)
    {
396
397
398
      const guint8 *pixel = (const guint8 *) &palette[i];
      gint         *color;
      gint         *slot;
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431

      color = g_new (gint, 1);
      slot = g_new (gint, 1);

      *color = (pixel[2] << 16 | pixel[1] << 8 | pixel[0]);
      *slot = i;

      g_hash_table_insert (hash, color, slot);
    }

  return hash;
}

static gint
ico_get_palette_index (GHashTable *hash,
                       gint        red,
                       gint        green,
                       gint        blue)
{
  gint  color = 0;
  gint *slot;

  color = (red << 16 | green << 8 | blue);
  slot = g_hash_table_lookup (hash, &color);

  if (!slot)
    {
      return 0;
    }

  return *slot;
}

432
static gint
433
434
ico_get_layer_num_colors (GimpLayer *layer,
                          gboolean  *uses_alpha_levels)
435
{
436
437
438
439
440
441
442
443
444
  gint        w, h;
  gint        bpp;
  gint        num_colors = 0;
  guint       num_pixels;
  guchar     *buf;
  guchar     *src;
  guint32    *colors;
  guint32    *c;
  GHashTable *hash;
445
  GeglBuffer *buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer));
446
447
448
449
  const Babl *format;

  w = gegl_buffer_get_width  (buffer);
  h = gegl_buffer_get_height (buffer);
450
451
452

  num_pixels = w * h;

453
  switch (gimp_drawable_type (GIMP_DRAWABLE (layer)))
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
    {
    case GIMP_RGB_IMAGE:
      format = babl_format ("R'G'B' u8");
      break;

    case GIMP_RGBA_IMAGE:
      format = babl_format ("R'G'B'A u8");
      break;

    case GIMP_GRAY_IMAGE:
      format = babl_format ("Y' u8");
      break;

    case GIMP_GRAYA_IMAGE:
      format = babl_format ("Y'A u8");
      break;

    case GIMP_INDEXED_IMAGE:
    case GIMP_INDEXEDA_IMAGE:
      format = gegl_buffer_get_format (buffer);
474
475
476
477
      /* It is possible to count the colors of indexed image more easily
       * with gimp_image_get_colormap(), but counting only the colors
       * actually used will allow more efficient bpp if possible. */
      break;
478
479
480
481

    default:
      g_return_val_if_reached (0);
    }
482

483
  bpp = babl_format_get_bytes_per_pixel (format);
484

485
  buf = src = g_new (guchar, num_pixels * bpp);
486

487
488
489
490
491
  gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, w, h), 1.0,
                   format, buf,
                   GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);

  g_object_unref (buffer);
492
493
494
495

  hash = g_hash_table_new (g_int_hash, g_int_equal);
  *uses_alpha_levels = FALSE;

496
497
498
499
500
501
502
503
504
505
506
507
508
  colors = c = g_new (guint32, num_pixels);

  switch (bpp)
    {
    case 1:
      while (num_pixels--)
        {
          *c = *src;
          g_hash_table_insert (hash, c, c);
          src++;
          c++;
        }
      break;
509

510
511
512
513
514
515
516
517
518
519
520
    case 2:
      while (num_pixels--)
        {
          *c = (src[1] << 8) | src[0];
          if (src[1] != 0 && src[1] != 255)
            *uses_alpha_levels = TRUE;
          g_hash_table_insert (hash, c, c);
          src += 2;
          c++;
        }
      break;
521

522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
    case 3:
      while (num_pixels--)
        {
          *c = (src[2] << 16) | (src[1] << 8) | src[0];
          g_hash_table_insert (hash, c, c);
          src += 3;
          c++;
        }
      break;

    case 4:
      while (num_pixels--)
        {
          *c = (src[3] << 24) | (src[2] << 16) | (src[1] << 8) | src[0];
          if (src[3] != 0 && src[3] != 255)
            *uses_alpha_levels = TRUE;
          g_hash_table_insert (hash, c, c);
          src += 4;
          c++;
        }
      break;
    }
544
545
546
547
548

  num_colors = g_hash_table_size (hash);

  g_hash_table_destroy (hash);

549
  g_free (colors);
550
  g_free (buf);
551
552
553
554

  return num_colors;
}

Sven Neumann's avatar
Sven Neumann committed
555
gboolean
556
557
ico_cmap_contains_black (const guchar *cmap,
                         gint          num_colors)
558
559
560
561
562
{
  gint i;

  for (i = 0; i < num_colors; i++)
    {
563
564
565
      if ((cmap[3 * i    ] == 0) &&
          (cmap[3 * i + 1] == 0) &&
          (cmap[3 * i + 2] == 0))
566
567
568
569
570
571
572
573
574
        {
          return TRUE;
        }
    }

  return FALSE;
}

static void
575
576
577
578
579
ico_image_get_reduced_buf (GimpDrawable *layer,
                           gint          bpp,
                           gint         *num_colors,
                           guchar      **cmap_out,
                           guchar      **buf_out)
580
{
581
582
  GimpImage  *tmp_image;
  GimpLayer  *tmp_layer;
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
  gint        w, h;
  guchar     *buf;
  guchar     *cmap   = NULL;
  GeglBuffer *buffer = gimp_drawable_get_buffer (layer);
  const Babl *format;

  w = gegl_buffer_get_width  (buffer);
  h = gegl_buffer_get_height (buffer);

  switch (gimp_drawable_type (layer))
    {
    case GIMP_RGB_IMAGE:
      format = babl_format ("R'G'B' u8");
      break;

    case GIMP_RGBA_IMAGE:
      format = babl_format ("R'G'B'A u8");
      break;

    case GIMP_GRAY_IMAGE:
      format = babl_format ("Y' u8");
      break;

    case GIMP_GRAYA_IMAGE:
      format = babl_format ("Y'A u8");
      break;

    case GIMP_INDEXED_IMAGE:
    case GIMP_INDEXEDA_IMAGE:
      format = gegl_buffer_get_format (buffer);
613
      break;
614

615
616
617
    default:
      g_return_if_reached ();
    }
Sven Neumann's avatar
Sven Neumann committed
618

619
620
  *num_colors = 0;

621
  buf = g_new (guchar, w * h * 4);
622

623
  if (bpp <= 8 || bpp == 24 || babl_format_get_bytes_per_pixel (format) != 4)
624
    {
625
      GimpImage  *image = gimp_item_get_image (GIMP_ITEM (layer));
626
      GeglBuffer *tmp;
627

628
      tmp_image = gimp_image_new (w, h, gimp_image_get_base_type (image));
629
630
      gimp_image_undo_disable (tmp_image);

631
632
633
634
635
636
637
638
639
640
      if (gimp_drawable_is_indexed (layer))
        {
          guchar *cmap;
          gint    num_colors;

          cmap = gimp_image_get_colormap (image, &num_colors);
          gimp_image_set_colormap (tmp_image, cmap, num_colors);
          g_free (cmap);
        }

641
642
      tmp_layer = gimp_layer_new (tmp_image, "tmp", w, h,
                                  gimp_drawable_type (layer),
643
644
                                  100,
                                  gimp_image_get_default_new_layer_mode (tmp_image));
645
      gimp_image_insert_layer (tmp_image, tmp_layer, NULL, 0);
646

647
      tmp = gimp_drawable_get_buffer (GIMP_DRAWABLE (tmp_layer));
648
649
650
651
652

      gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, w, h), 1.0,
                       format, buf,
                       GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);

653
      gegl_buffer_copy (buffer, NULL, GEGL_ABYSS_NONE, tmp, NULL);
654

655
      g_object_unref (tmp);
656

657
      if (! gimp_drawable_is_rgb (GIMP_DRAWABLE (tmp_layer)))
658
659
660
661
662
        gimp_image_convert_rgb (tmp_image);

      if (bpp <= 8)
        {
          gimp_image_convert_indexed (tmp_image,
663
                                      GIMP_CONVERT_DITHER_FS,
664
                                      GIMP_CONVERT_PALETTE_GENERATE,
665
                                      1 << bpp, TRUE, FALSE, "dummy");
666
667
668
669

          cmap = gimp_image_get_colormap (tmp_image, num_colors);

          if (*num_colors == (1 << bpp) &&
670
              ! ico_cmap_contains_black (cmap, *num_colors))
671
672
673
674
675
            {
              /* Windows icons with color maps need the color black.
               * We need to eliminate one more color to make room for black.
               */

Sven Neumann's avatar
Sven Neumann committed
676
677
678
679
680
681
682
683
684
685
686
687
688
689
              if (gimp_drawable_is_indexed (layer))
                {
                  g_free (cmap);
                  cmap = gimp_image_get_colormap (image, num_colors);
                  gimp_image_set_colormap (tmp_image, cmap, *num_colors);
                }
              else if (gimp_drawable_is_gray (layer))
                {
                  gimp_image_convert_grayscale (tmp_image);
                }
              else
                {
                  gimp_image_convert_rgb (tmp_image);
                }
690

691
              tmp = gimp_drawable_get_buffer (GIMP_DRAWABLE (tmp_layer));
692

693
694
695
696
697
698
              gegl_buffer_set (tmp, GEGL_RECTANGLE (0, 0, w, h), 0,
                               format, buf, GEGL_AUTO_ROWSTRIDE);

              g_object_unref (tmp);

              if (! gimp_drawable_is_rgb (layer))
Sven Neumann's avatar
Sven Neumann committed
699
700
                gimp_image_convert_rgb (tmp_image);

701
              gimp_image_convert_indexed (tmp_image,
702
                                          GIMP_CONVERT_DITHER_FS,
703
                                          GIMP_CONVERT_PALETTE_GENERATE,
Sven Neumann's avatar
Sven Neumann committed
704
                                          (1<<bpp) - 1, TRUE, FALSE, "dummy");
Sven Neumann's avatar
Sven Neumann committed
705
              g_free (cmap);
706
707
708
709
710
              cmap = gimp_image_get_colormap (tmp_image, num_colors);
            }

          gimp_image_convert_rgb (tmp_image);
        }
711
712
      else if (bpp == 24)
        {
713
          GimpValueArray *return_vals;
714
715

          return_vals =
716
717
            gimp_pdb_run_procedure (gimp_get_pdb (),
                                    "plug-in-threshold-alpha",
718
719
720
721
                                    GIMP_TYPE_RUN_MODE, GIMP_RUN_NONINTERACTIVE,
                                    GIMP_TYPE_IMAGE,    tmp_image,
                                    GIMP_TYPE_DRAWABLE, tmp_layer,
                                    G_TYPE_INT,         ICO_ALPHA_THRESHOLD,
722
723
724
                                    G_TYPE_NONE);

          gimp_value_array_unref (return_vals);
725
        }
726
727
728

      gimp_layer_add_alpha (tmp_layer);

729
      tmp = gimp_drawable_get_buffer (GIMP_DRAWABLE (tmp_layer));
730
731
732
733
734
735

      gegl_buffer_get (tmp, GEGL_RECTANGLE (0, 0, w, h), 1.0,
                       NULL, buf,
                       GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);

      g_object_unref (tmp);
736
737
738
739
740

      gimp_image_delete (tmp_image);
    }
  else
    {
741
742
743
      gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, w, h), 1.0,
                       format, buf,
                       GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
744
745
    }

746
  g_object_unref (buffer);
747

Sven Neumann's avatar
Sven Neumann committed
748
  *cmap_out = cmap;
749
  *buf_out = buf;
750
751
}

752
static gboolean
753
754
755
ico_write_png (FILE         *fp,
               GimpDrawable *layer,
               gint32        depth)
756
757
758
759
760
761
762
763
{
  png_structp png_ptr;
  png_infop   info_ptr;
  png_byte  **row_pointers;
  gint        i, rowstride;
  gint        width, height;
  gint        num_colors_used;
  guchar     *palette;
764
  guchar     *buf;
765
766
767

  row_pointers = NULL;
  palette = NULL;
768
  buf = NULL;
769

770
771
  width = gimp_drawable_get_width (layer);
  height = gimp_drawable_get_height (layer);
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790

  png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  if ( !png_ptr )
    return FALSE;

  info_ptr = png_create_info_struct (png_ptr);
  if ( !info_ptr )
    {
      png_destroy_write_struct (&png_ptr, NULL);
      return FALSE;
    }

  if (setjmp (png_jmpbuf (png_ptr)))
    {
      png_destroy_write_struct (&png_ptr, &info_ptr);
      if ( row_pointers )
        g_free (row_pointers);
      if (palette)
        g_free (palette);
791
792
      if (buf)
        g_free (buf);
793
794
795
796
      return FALSE;
    }

  ico_image_get_reduced_buf (layer, depth, &num_colors_used,
797
                             &palette, &buf);
798
799
800
801
802
803
804
805
806
807
808
809
810
811

  png_init_io (png_ptr, fp);
  png_set_IHDR (png_ptr, info_ptr, width, height,
                8,
                PNG_COLOR_TYPE_RGBA,
                PNG_INTERLACE_NONE,
                PNG_COMPRESSION_TYPE_DEFAULT,
                PNG_FILTER_TYPE_DEFAULT);
  png_write_info (png_ptr, info_ptr);

  rowstride = ico_rowstride (width, 32);
  row_pointers = g_new (png_byte*, height);
  for (i = 0; i < height; i++)
    {
812
      row_pointers[i] = buf + rowstride * i;
813
814
815
816
817
818
819
820
821
822
    }
  png_write_image (png_ptr, row_pointers);

  row_pointers = NULL;

  png_write_end (png_ptr, info_ptr);
  png_destroy_write_struct (&png_ptr, &info_ptr);

  g_free (row_pointers);
  g_free (palette);
823
  g_free (buf);
824
825
826
  return TRUE;
}

Sven Neumann's avatar
Sven Neumann committed
827
static gboolean
828
829
830
ico_write_icon (FILE         *fp,
                GimpDrawable *layer,
                gint32        depth)
Sven Neumann's avatar
Sven Neumann committed
831
832
833
834
835
{
  IcoFileDataHeader  header;
  gint               and_len, xor_len, palette_index, x, y;
  gint               num_colors = 0, num_colors_used = 0, black_index = 0;
  gint               width, height;
836
837
  guchar            *buf = NULL, *pixel;
  guint32           *buf32;
Sven Neumann's avatar
Sven Neumann committed
838
839
840
841
842
843
844
  guchar            *palette;
  GHashTable        *color_to_slot = NULL;
  guchar            *xor_map, *and_map;

  guint32           *palette32 = NULL;
  gint               palette_len = 0;

845
846
  guint8             alpha_threshold;

Sven Neumann's avatar
Sven Neumann committed
847
848
849
  D(("Creating data structures for icon %i ------------------------\n",
     num_icon));

850
851
  width = gimp_drawable_get_width (layer);
  height = gimp_drawable_get_height (layer);
Sven Neumann's avatar
Sven Neumann committed
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872

  header.header_size     = 40;
  header.width          = width;
  header.height         = 2 * height;
  header.planes         = 1;
  header.bpp            = depth;
  header.compression    = 0;
  header.image_size     = 0;
  header.x_res          = 0;
  header.y_res          = 0;
  header.used_clrs      = 0;
  header.important_clrs = 0;

  num_colors = (1L << header.bpp);

  D(("  header size %i, w %i, h %i, planes %i, bpp %i\n",
     header.header_size, header.width, header.height, header.planes,
     header.bpp));

  /* Reduce colors in copy of image */
  ico_image_get_reduced_buf (layer, header.bpp, &num_colors_used,
873
874
                             &palette, &buf);
  buf32 = (guint32 *) buf;
Sven Neumann's avatar
Sven Neumann committed
875

876
  /* Set up colormap and and_map when necessary: */
Sven Neumann's avatar
Sven Neumann committed
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
  if (header.bpp <= 8)
    {
      /* Create a colormap */
      palette32 = ico_create_palette (palette,
                                      num_colors, num_colors_used,
                                      &black_index);
      palette_len = num_colors * 4;

      color_to_slot = ico_create_color_to_palette_map (palette32,
                                                       num_colors_used);
      D(("  created %i-slot colormap with %i colors, black at slot %i\n",
         num_colors, num_colors_used, black_index));
    }

  /* Create and_map. It's padded out to 32 bits per line: */
  and_map = ico_alloc_map (width, height, 1, &and_len);

894
895
896
897
898
899
900
901
902
903
904
  /* 32-bit bitmaps have an alpha channel as well as a mask. Any partially or
   * fully opaque pixel should have an opaque mask (some ICO code in Windows
   * draws pixels as black if they have a transparent mask but a non-transparent
   * alpha value).
   *
   * For bitmaps without an alpha channel, we use the normal threshold to build
   * the mask, so that the mask is as close as possible to the original alpha
   * channel.
   */
  alpha_threshold = header.bpp < 32 ? ICO_ALPHA_THRESHOLD : 0;

Sven Neumann's avatar
Sven Neumann committed
905
906
907
  for (y = 0; y < height; y++)
    for (x = 0; x < width; x++)
      {
908
        pixel = (guint8 *) &buf32[y * width + x];
Sven Neumann's avatar
Sven Neumann committed
909
910

        ico_set_bit_in_data (and_map, width,
911
                             (height - y -1) * width + x,
912
                             (pixel[3] > alpha_threshold ? 0 : 1));
Sven Neumann's avatar
Sven Neumann committed
913
914
915
916
917
918
919
920
921
922
923
      }

  xor_map = ico_alloc_map (width, height, header.bpp, &xor_len);

  /* Now fill in the xor map */
  switch (header.bpp)
    {
    case 1:
      for (y = 0; y < height; y++)
        for (x = 0; x < width; x++)
          {
924
            pixel = (guint8 *) &buf32[y * width + x];
Sven Neumann's avatar
Sven Neumann committed
925
926
927
928
            palette_index = ico_get_palette_index (color_to_slot, pixel[0],
                                                   pixel[1], pixel[2]);

            if (ico_get_bit_from_data (and_map, width,
929
                                       (height - y - 1) * width + x))
Sven Neumann's avatar
Sven Neumann committed
930
931
              {
                ico_set_bit_in_data (xor_map, width,
932
                                     (height - y -1) * width + x,
Sven Neumann's avatar
Sven Neumann committed
933
934
935
936
937
                                     black_index);
              }
            else
              {
                ico_set_bit_in_data (xor_map, width,
938
                                     (height - y -1) * width + x,
Sven Neumann's avatar
Sven Neumann committed
939
940
941
942
943
944
945
946
947
                                     palette_index);
              }
          }
      break;

    case 4:
      for (y = 0; y < height; y++)
        for (x = 0; x < width; x++)
          {
948
            pixel = (guint8 *) &buf32[y * width + x];
Sven Neumann's avatar
Sven Neumann committed
949
950
951
952
            palette_index = ico_get_palette_index(color_to_slot, pixel[0],
                                                  pixel[1], pixel[2]);

            if (ico_get_bit_from_data (and_map, width,
953
                                       (height - y - 1) * width + x))
Sven Neumann's avatar
Sven Neumann committed
954
955
              {
                ico_set_nibble_in_data (xor_map, width,
956
                                        (height - y -1) * width + x,
Sven Neumann's avatar
Sven Neumann committed
957
958
959
960
961
                                        black_index);
              }
            else
              {
                ico_set_nibble_in_data (xor_map, width,
962
                                        (height - y - 1) * width + x,
Sven Neumann's avatar
Sven Neumann committed
963
964
965
966
967
968
969
970
971
                                        palette_index);
              }
          }
      break;

    case 8:
      for (y = 0; y < height; y++)
        for (x = 0; x < width; x++)
          {
972
            pixel = (guint8 *) &buf32[y * width + x];
Sven Neumann's avatar
Sven Neumann committed
973
974
975
976
977
978
            palette_index = ico_get_palette_index (color_to_slot,
                                                   pixel[0],
                                                   pixel[1],
                                                   pixel[2]);

            if (ico_get_bit_from_data (and_map, width,
979
                                       (height - y - 1) * width + x))
Sven Neumann's avatar
Sven Neumann committed
980
981
              {
                ico_set_byte_in_data (xor_map, width,
982
                                      (height - y - 1) * width + x,
Sven Neumann's avatar
Sven Neumann committed
983
984
985
986
987
                                      black_index);
              }
            else
              {
                ico_set_byte_in_data (xor_map, width,
988
                                      (height - y - 1) * width + x,
Sven Neumann's avatar
Sven Neumann committed
989
990
991
992
993
994
                                      palette_index);
              }

          }
      break;

995
996
997
998
999
1000
1001
    case 24:
      for (y = 0; y < height; y++)
        {
          guchar *row = xor_map + (xor_len * (height - y - 1) / height);

          for (x = 0; x < width; x++)
            {
1002
              pixel = (guint8 *) &buf32[y * width + x];
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012

              row[0] = pixel[2];
              row[1] = pixel[1];
              row[2] = pixel[0];

              row += 3;
            }
        }
      break;

Sven Neumann's avatar
Sven Neumann committed
1013
1014
1015
1016
    default:
      for (y = 0; y < height; y++)
        for (x = 0; x < width; x++)
          {
1017
            pixel = (guint8 *) &buf32[y * width + x];
Sven Neumann's avatar
Sven Neumann committed
1018

1019
            ((guint32 *) xor_map)[(height - y -1) * width + x] =
Sven Neumann's avatar
Sven Neumann committed
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
              GUINT32_TO_LE ((pixel[0] << 16) |
                             (pixel[1] << 8)  |
                             (pixel[2])       |
                             (pixel[3] << 24));
          }
    }

  D(("  filled and_map of length %i, xor_map of length %i\n",
     and_len, xor_len));

  if (color_to_slot)
1031
    g_hash_table_destroy (color_to_slot);
Sven Neumann's avatar
Sven Neumann committed
1032

1033
  g_free (palette);
1034
  g_free (buf);
Sven Neumann's avatar
Sven Neumann committed
1035
1036
1037
1038
1039
1040
1041

  ico_write_int32 (fp, (guint32*) &header, 3);
  ico_write_int16 (fp, &header.planes, 2);
  ico_write_int32 (fp, &header.compression, 6);

  if (palette_len)
    ico_write_int8 (fp, (guint8 *) palette32, palette_len);
1042

Sven Neumann's avatar
Sven Neumann committed
1043
1044
1045
  ico_write_int8 (fp, xor_map, xor_len);
  ico_write_int8 (fp, and_map, and_len);

1046
  g_free (palette32);
Sven Neumann's avatar
Sven Neumann committed
1047
1048
  g_free (xor_map);
  g_free (and_map);
1049

Sven Neumann's avatar
Sven Neumann committed
1050
1051
1052
1053
1054
1055
1056
1057
  return TRUE;
}

static void
ico_save_info_free (IcoSaveInfo  *info)
{
  g_free (info->depths);
  g_free (info->default_depths);
1058
  g_free (info->compress);
1059
  g_list_free (info->layers);
Sven Neumann's avatar
Sven Neumann committed
1060
1061
1062
  memset (info, 0, sizeof (IcoSaveInfo));
}

1063
GimpPDBStatusType
1064
1065
1066
1067
ico_save_image (GFile      *file,
                GimpImage  *image,
                gint32      run_mode,
                GError    **error)
1068
{
1069
  FILE          *fp;
1070
1071
1072
1073
1074
1075
1076
1077
  GList         *iter;
  gint           width;
  gint           height;
  IcoSaveInfo    info;
  IcoFileHeader  header;
  IcoFileEntry  *entries;
  gboolean       saved;
  gint           i;
1078

1079
1080
  D(("*** Exporting Microsoft icon file %s\n",
     gimp_file_get_utf8_name (file)));
1081

Sven Neumann's avatar
Sven Neumann committed
1082
1083
1084
  ico_save_init (image, &info);

  if (run_mode == GIMP_RUN_INTERACTIVE)
1085
    {
Sven Neumann's avatar
Sven Neumann committed
1086
1087
1088
      /* Allow user to override default values */
      if ( !ico_save_dialog (image, &info))
        return GIMP_PDB_CANCEL;
1089
1090
    }

1091
  gimp_progress_init_printf (_("Exporting '%s'"),
1092
                             gimp_file_get_utf8_name (file));
1093

1094
  fp = g_fopen (g_file_peek_path (file), "wb");
1095
1096

  if (! fp)
Sven Neumann's avatar
Sven Neumann committed
1097
    {
1098
1099
      g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
                   _("Could not open '%s' for writing: %s"),
1100
                   gimp_file_get_utf8_name (file), g_strerror (errno));
Sven Neumann's avatar
Sven Neumann committed
1101
1102
      return GIMP_PDB_EXECUTION_ERROR;
    }
1103

Sven Neumann's avatar
Sven Neumann committed
1104
1105
1106
  header.reserved = 0;
  header.resource_type = 1;
  header.icon_count = info.num_icons;
1107
1108
1109
  if (! ico_write_int16 (fp, &header.reserved, 1)      ||
      ! ico_write_int16 (fp, &header.resource_type, 1) ||
      ! ico_write_int16 (fp, &header.icon_count, 1))
Sven Neumann's avatar
Sven Neumann committed
1110
1111
1112
1113
1114
    {
      ico_save_info_free (&info);
      fclose (fp);
      return GIMP_PDB_EXECUTION_ERROR;
    }
1115

Sven Neumann's avatar
Sven Neumann committed
1116
1117
1118
1119
  entries = g_new0 (IcoFileEntry, info.num_icons);
  if (fwrite (entries, sizeof (IcoFileEntry), info.num_icons, fp) <= 0)
    {
      ico_save_info_free (&info);
1120
      g_free (entries);
Sven Neumann's avatar
Sven Neumann committed
1121
1122
1123
      fclose (fp);
      return GIMP_PDB_EXECUTION_ERROR;
    }
1124

1125
1126
1127
  for (iter = info.layers, i = 0;
       iter;
       iter = g_list_next (iter), i++)
Sven Neumann's avatar
Sven Neumann committed
1128
1129
    {
      gimp_progress_update ((gdouble)i / (gdouble)info.num_icons);
1130

1131
1132
      width = gimp_drawable_get_width (iter->data);
      height = gimp_drawable_get_height (iter->data);
Sven Neumann's avatar
Sven Neumann committed
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
      if (width <= 255 && height <= 255)
        {
          entries[i].width = width;
          entries[i].height = height;
        }
      else
        {
          entries[i].width = 0;
          entries[i].height = 0;
        }
      if ( info.depths[i] <= 8 )
        entries[i].num_colors = 1 << info.depths[i];
      else
        entries[i].num_colors = 0;
      entries[i].reserved = 0;
      entries[i].planes = 1;
      entries[i].bpp = info.depths[i];
      entries[i].offset = ftell (fp);
1151
1152

      if (info.compress[i])
1153
        saved = ico_write_png (fp, iter->data, info.depths[i]);
1154
      else
1155
        saved = ico_write_icon (fp, iter->data, info.depths[i]);
1156

Sven Neumann's avatar
Sven Neumann committed
1157
1158
1159
1160
1161
1162
      if (!saved)
        {
          ico_save_info_free (&info);
          fclose (fp);
          return GIMP_PDB_EXECUTION_ERROR;
        }
1163

Sven Neumann's avatar
Sven Neumann committed
1164
1165
      entries[i].size = ftell (fp) - entries[i].offset;
    }
1166

1167
1168
1169
1170
1171
1172
1173
1174
  for (i = 0; i < info.num_icons; i++)
    {
      entries[i].planes = GUINT16_TO_LE (entries[i].planes);
      entries[i].bpp    = GUINT16_TO_LE (entries[i].bpp);
      entries[i].size   = GUINT32_TO_LE (entries[i].size);
      entries[i].offset = GUINT32_TO_LE (entries[i].offset);
    }

Sven Neumann's avatar
Sven Neumann committed
1175
1176
1177
1178
1179
1180
1181
  if (fseek (fp, sizeof(IcoFileHeader), SEEK_SET) < 0
      || fwrite (entries, sizeof (IcoFileEntry), info.num_icons, fp) <= 0)
    {
      ico_save_info_free (&info);
      fclose (fp);
      return GIMP_PDB_EXECUTION_ERROR;
    }
1182

Sven Neumann's avatar
Sven Neumann committed
1183
  gimp_progress_update (1.0);
1184

Sven Neumann's avatar
Sven Neumann committed
1185
1186
  ico_save_info_free (&info);
  fclose (fp);
1187
  g_free (entries);
1188

Sven Neumann's avatar
Sven Neumann committed
1189
  return GIMP_PDB_SUCCESS;
1190
}