1
/* GIMP - The GNU Image Manipulation Program
2
* Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
4
* GIMP Plug-in for Windows Icon files.
5
* 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 2 of the License, or
10
* (at your option) any later version.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27
#include <glib/gstdio.h>
29
#include <libgimp/gimp.h>
30
#include <libgimp/gimpui.h>
39
#include "ico-dialog.h"
41
#include "libgimp/stdplugins-intl.h"
44
static gint ico_write_int8 (FILE *fp,
47
static gint ico_write_int16 (FILE *fp,
50
static gint ico_write_int32 (FILE *fp,
54
/* Helpers to set bits in a *cleared* data chunk */
55
static void ico_set_bit_in_data (guint8 *data,
59
static void ico_set_nibble_in_data (guint8 *data,
63
static void ico_set_byte_in_data (guint8 *data,
68
static gint ico_get_layer_num_colors (gint32 layer,
69
gboolean *uses_alpha_levels);
70
static void ico_image_get_reduced_buf (guint32 layer,
78
ico_write_int32 (FILE *fp,
87
#if (G_BYTE_ORDER == G_BIG_ENDIAN)
90
for (i = 0; i < count; i++)
91
data[i] = GUINT32_FROM_LE (data[i]);
94
ico_write_int8 (fp, (guint8 *) data, count * 4);
96
#if (G_BYTE_ORDER == G_BIG_ENDIAN)
97
/* Put it back like we found it */
98
for (i = 0; i < count; i++)
99
data[i] = GUINT32_FROM_LE (data[i]);
108
ico_write_int16 (FILE *fp,
117
#if (G_BYTE_ORDER == G_BIG_ENDIAN)
120
for (i = 0; i < count; i++)
121
data[i] = GUINT16_FROM_LE (data[i]);
124
ico_write_int8 (fp, (guint8 *) data, count * 2);
126
#if (G_BYTE_ORDER == G_BIG_ENDIAN)
127
/* Put it back like we found it */
128
for (i = 0; i < count; i++)
129
data[i] = GUINT16_FROM_LE (data[i]);
138
ico_write_int8 (FILE *fp,
148
bytes = fwrite ((gchar *) data, sizeof (gchar), count, fp);
149
if (bytes <= 0) /* something bad happened */
160
ico_save_init (gint32 image_ID, IcoSaveInfo *info)
164
gboolean uses_alpha_values;
166
layers = gimp_image_get_layers (image_ID, &info->num_icons);
167
info->layers = layers;
168
info->depths = g_new (gint, info->num_icons);
169
info->default_depths = g_new (gint, info->num_icons);
170
info->compress = g_new (gboolean, info->num_icons);
172
/* Limit the color depths to values that don't cause any color loss --
173
the user should pick these anyway, so we can save her some time.
174
If the user wants to lose some colors, the settings can always be changed
176
for (i = 0; i < info->num_icons; i++)
178
num_colors = ico_get_layer_num_colors (layers[i], &uses_alpha_values);
180
if (!uses_alpha_values)
184
/* Let's suggest monochrome */
185
info->default_depths [i] = 1;
187
else if (num_colors <= 16)
189
/* Let's suggest 4bpp */
190
info->default_depths [i] = 4;
192
else if (num_colors <= 256)
194
/* Let's suggest 8bpp */
195
info->default_depths [i] = 8;
199
/* Let's suggest 24bpp */
200
info->default_depths [i] = 24;
205
/* Otherwise, or if real alpha levels are used, stick with 32bpp */
206
info->default_depths [i] = 32;
210
if (gimp_drawable_width (layers[i]) > 255
211
|| gimp_drawable_height (layers[i]) > 255 )
213
info->compress[i] = TRUE;
217
info->compress[i] = FALSE;
221
/* set with default values */
222
memcpy (info->depths, info->default_depths,
223
sizeof (gint) * info->num_icons);
229
ico_save_dialog (gint32 image_ID,
236
gimp_ui_init (PLUG_IN_BINARY, TRUE);
238
dialog = ico_dialog_new (info);
239
for (i = 0; i < info->num_icons; i++)
241
/* if (gimp_layer_get_visible(layers[i])) */
242
ico_dialog_add_icon (dialog, info->layers[i], i);
245
/* Scale the thing to approximately fit its content, but not too large ... */
246
gtk_window_set_default_size (GTK_WINDOW (dialog),
248
200 + (info->num_icons > 4 ?
249
500 : info->num_icons * 120));
251
gtk_widget_show (dialog);
253
response = gimp_dialog_run (GIMP_DIALOG (dialog));
255
gtk_widget_destroy (dialog);
257
return (response == GTK_RESPONSE_OK);
261
ico_set_bit_in_data (guint8 *data,
270
/* width per line in multiples of 32 bits */
271
width32 = (line_width % 32 == 0 ? line_width/32 : line_width/32 + 1);
273
line = bit_num / line_width;
274
offset = bit_num % line_width;
275
bit_val = bit_val & 0x00000001;
277
data[line * width32 * 4 + offset/8] |= (bit_val << (7 - (offset % 8)));
282
ico_set_nibble_in_data (guint8 *data,
291
/* width per line in multiples of 32 bits */
292
width8 = (line_width % 8 == 0 ? line_width/8 : line_width/8 + 1);
294
line = nibble_num / line_width;
295
offset = nibble_num % line_width;
296
nibble_val = nibble_val & 0x0000000F;
298
data[line * width8 * 4 + offset/2] |=
299
(nibble_val << (4 * (1 - (offset % 2))));
304
ico_set_byte_in_data (guint8 *data,
314
/* width per line in multiples of 32 bits */
315
width4 = (line_width % 4 == 0 ? line_width/4 : line_width/4 + 1);
317
line = byte_num / line_width;
318
offset = byte_num % line_width;
319
byte = byte_val & 0x000000FF;
321
data[line * width4 * 4 + offset] = byte;
325
/* Create a colormap from the given buffer data */
327
ico_create_palette (const guchar *cmap,
329
gint num_colors_used,
335
g_return_val_if_fail (cmap != NULL || num_colors_used == 0, NULL);
336
g_return_val_if_fail (num_colors_used <= num_colors, NULL);
338
palette = g_new0 (guchar, num_colors * 4);
341
for (i = 0; i < num_colors_used; i++)
343
palette[i * 4 + 2] = cmap[i * 3];
344
palette[i * 4 + 1] = cmap[i * 3 + 1];
345
palette[i * 4] = cmap[i * 3 + 2];
347
if ((cmap[i*3] == 0) &&
348
(cmap[i*3 + 1] == 0) &&
349
(cmap[i*3 + 2] == 0))
355
if (*black_slot == -1)
357
if (num_colors_used == num_colors)
359
D(("WARNING -- no room for black, this shouldn't happen.\n"));
360
*black_slot = num_colors - 1;
362
palette[(num_colors-1) * 4] = 0;
363
palette[(num_colors-1) * 4 + 1] = 0;
364
palette[(num_colors-1) * 4 + 2] = 0;
368
*black_slot = num_colors_used;
372
return (guint32 *) palette;
377
ico_create_color_to_palette_map (const guint32 *palette,
383
hash = g_hash_table_new_full (g_int_hash, g_int_equal,
384
(GDestroyNotify) g_free,
385
(GDestroyNotify) g_free);
387
for (i = 0; i < num_colors; i++)
389
const guint8 *pixel = (const guint8 *) &palette[i];
393
color = g_new (gint, 1);
394
slot = g_new (gint, 1);
396
*color = (pixel[2] << 16 | pixel[1] << 8 | pixel[0]);
399
g_hash_table_insert (hash, color, slot);
406
ico_get_palette_index (GHashTable *hash,
414
color = (red << 16 | green << 8 | blue);
415
slot = g_hash_table_lookup (hash, &color);
426
ico_get_layer_num_colors (gint32 layer,
427
gboolean *uses_alpha_levels)
429
GimpPixelRgn pixel_rgn;
439
GimpDrawable *drawable = gimp_drawable_get (layer);
441
w = gimp_drawable_width (layer);
442
h = gimp_drawable_height (layer);
446
bpp = gimp_drawable_bpp (layer);
448
buffer = src = g_new (guchar, num_pixels * bpp);
450
gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0, w, h, FALSE, FALSE);
451
gimp_pixel_rgn_get_rect (&pixel_rgn, buffer, 0, 0, w, h);
453
gimp_drawable_detach (drawable);
455
hash = g_hash_table_new (g_int_hash, g_int_equal);
456
*uses_alpha_levels = FALSE;
458
colors = c = g_new (guint32, num_pixels);
466
g_hash_table_insert (hash, c, c);
475
*c = (src[1] << 8) | src[0];
476
if (src[1] != 0 && src[1] != 255)
477
*uses_alpha_levels = TRUE;
478
g_hash_table_insert (hash, c, c);
487
*c = (src[2] << 16) | (src[1] << 8) | src[0];
488
g_hash_table_insert (hash, c, c);
497
*c = (src[3] << 24) | (src[2] << 16) | (src[1] << 8) | src[0];
498
if (src[3] != 0 && src[3] != 255)
499
*uses_alpha_levels = TRUE;
500
g_hash_table_insert (hash, c, c);
507
num_colors = g_hash_table_size (hash);
509
g_hash_table_destroy (hash);
518
ico_cmap_contains_black (const guchar *cmap,
523
for (i = 0; i < num_colors; i++)
525
if ((cmap[3 * i ] == 0) &&
526
(cmap[3 * i + 1] == 0) &&
527
(cmap[3 * i + 2] == 0))
537
ico_image_get_reduced_buf (guint32 layer,
543
GimpPixelRgn src_pixel_rgn, dst_pixel_rgn;
549
GimpDrawable *drawable = gimp_drawable_get (layer);
551
w = gimp_drawable_width (layer);
552
h = gimp_drawable_height (layer);
556
buffer = g_new (guchar, w * h * 4);
558
if (bpp <= 8 || bpp == 24 || drawable->bpp != 4)
560
gint32 image = gimp_drawable_get_image (layer);
563
tmp_image = gimp_image_new (gimp_drawable_width (layer),
564
gimp_drawable_height (layer),
565
gimp_image_base_type (image));
566
gimp_image_undo_disable (tmp_image);
568
if (gimp_drawable_is_indexed (layer))
573
cmap = gimp_image_get_colormap (image, &num_colors);
574
gimp_image_set_colormap (tmp_image, cmap, num_colors);
578
tmp_layer = gimp_layer_new (tmp_image, "tmp", w, h,
579
gimp_drawable_type (layer),
580
100, GIMP_NORMAL_MODE);
581
gimp_image_add_layer (tmp_image, tmp_layer, 0);
583
tmp = gimp_drawable_get (tmp_layer);
585
gimp_pixel_rgn_init (&src_pixel_rgn, drawable, 0, 0, w, h, FALSE, FALSE);
586
gimp_pixel_rgn_init (&dst_pixel_rgn, tmp, 0, 0, w, h, TRUE, FALSE);
587
gimp_pixel_rgn_get_rect (&src_pixel_rgn, buffer, 0, 0, w, h);
588
gimp_pixel_rgn_set_rect (&dst_pixel_rgn, buffer, 0, 0, w, h);
589
gimp_drawable_detach (tmp);
591
if (! gimp_drawable_is_rgb (tmp_layer))
592
gimp_image_convert_rgb (tmp_image);
596
gimp_image_convert_indexed (tmp_image,
597
GIMP_FS_DITHER, GIMP_MAKE_PALETTE,
598
1 << bpp, TRUE, FALSE, "dummy");
600
cmap = gimp_image_get_colormap (tmp_image, num_colors);
602
if (*num_colors == (1 << bpp) &&
603
!ico_cmap_contains_black (cmap, *num_colors))
605
/* Windows icons with color maps need the color black.
606
* We need to eliminate one more color to make room for black.
609
if (gimp_drawable_is_indexed (layer))
612
cmap = gimp_image_get_colormap (image, num_colors);
613
gimp_image_set_colormap (tmp_image, cmap, *num_colors);
615
else if (gimp_drawable_is_gray (layer))
617
gimp_image_convert_grayscale (tmp_image);
621
gimp_image_convert_rgb (tmp_image);
624
tmp = gimp_drawable_get (tmp_layer);
625
gimp_pixel_rgn_init (&dst_pixel_rgn,
626
tmp, 0, 0, w, h, TRUE, FALSE);
627
gimp_pixel_rgn_set_rect (&dst_pixel_rgn, buffer, 0, 0, w, h);
628
gimp_drawable_detach (tmp);
630
if (!gimp_drawable_is_rgb (layer))
631
gimp_image_convert_rgb (tmp_image);
633
gimp_image_convert_indexed (tmp_image,
634
GIMP_FS_DITHER, GIMP_MAKE_PALETTE,
635
(1<<bpp) - 1, TRUE, FALSE, "dummy");
637
cmap = gimp_image_get_colormap (tmp_image, num_colors);
640
gimp_image_convert_rgb (tmp_image);
644
GimpParam *return_vals;
648
gimp_run_procedure ("plug-in-threshold-alpha", &n_return_vals,
649
GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE,
650
GIMP_PDB_IMAGE, tmp_image,
651
GIMP_PDB_DRAWABLE, tmp_layer,
652
GIMP_PDB_INT32, ICO_ALPHA_THRESHOLD,
654
gimp_destroy_params (return_vals, n_return_vals);
657
gimp_layer_add_alpha (tmp_layer);
659
tmp = gimp_drawable_get (tmp_layer);
660
gimp_pixel_rgn_init (&src_pixel_rgn, tmp, 0, 0, w, h, FALSE, FALSE);
661
gimp_pixel_rgn_get_rect (&src_pixel_rgn, buffer, 0, 0, w, h);
662
gimp_drawable_detach (tmp);
664
gimp_image_delete (tmp_image);
668
gimp_pixel_rgn_init (&src_pixel_rgn, drawable, 0, 0, w, h, FALSE, FALSE);
669
gimp_pixel_rgn_get_rect (&src_pixel_rgn, buffer, 0, 0, w, h);
672
gimp_drawable_detach (drawable);
679
ico_write_png (FILE *fp,
685
png_byte **row_pointers;
688
gint num_colors_used;
696
width = gimp_drawable_width (layer);
697
height = gimp_drawable_height (layer);
699
png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
703
info_ptr = png_create_info_struct (png_ptr);
706
png_destroy_write_struct (&png_ptr, NULL);
710
if (setjmp (png_jmpbuf (png_ptr)))
712
png_destroy_write_struct (&png_ptr, &info_ptr);
714
g_free (row_pointers);
722
ico_image_get_reduced_buf (layer, depth, &num_colors_used,
725
png_init_io (png_ptr, fp);
726
png_set_IHDR (png_ptr, info_ptr, width, height,
730
PNG_COMPRESSION_TYPE_DEFAULT,
731
PNG_FILTER_TYPE_DEFAULT);
732
png_write_info (png_ptr, info_ptr);
734
rowstride = ico_rowstride (width, 32);
735
row_pointers = g_new (png_byte*, height);
736
for (i = 0; i < height; i++)
738
row_pointers[i] = buffer + rowstride * i;
740
png_write_image (png_ptr, row_pointers);
744
png_write_end (png_ptr, info_ptr);
745
png_destroy_write_struct (&png_ptr, &info_ptr);
747
g_free (row_pointers);
754
ico_write_icon (FILE *fp,
758
IcoFileDataHeader header;
759
gint and_len, xor_len, palette_index, x, y;
760
gint num_colors = 0, num_colors_used = 0, black_index = 0;
762
guchar *buffer = NULL, *pixel;
765
GHashTable *color_to_slot = NULL;
766
guchar *xor_map, *and_map;
768
guint32 *palette32 = NULL;
769
gint palette_len = 0;
771
D(("Creating data structures for icon %i ------------------------\n",
774
width = gimp_drawable_width (layer);
775
height = gimp_drawable_height (layer);
777
header.header_size = 40;
778
header.width = width;
779
header.height = 2 * height;
782
header.compression = 0;
783
header.image_size = 0;
786
header.used_clrs = 0;
787
header.important_clrs = 0;
789
num_colors = (1L << header.bpp);
791
D((" header size %i, w %i, h %i, planes %i, bpp %i\n",
792
header.header_size, header.width, header.height, header.planes,
795
/* Reduce colors in copy of image */
796
ico_image_get_reduced_buf (layer, header.bpp, &num_colors_used,
798
buffer32 = (guint32 *) buffer;
800
/* Set up colormap and and_map when necessary: */
803
/* Create a colormap */
804
palette32 = ico_create_palette (palette,
805
num_colors, num_colors_used,
807
palette_len = num_colors * 4;
809
color_to_slot = ico_create_color_to_palette_map (palette32,
811
D((" created %i-slot colormap with %i colors, black at slot %i\n",
812
num_colors, num_colors_used, black_index));
815
/* Create and_map. It's padded out to 32 bits per line: */
816
and_map = ico_alloc_map (width, height, 1, &and_len);
818
for (y = 0; y < height; y++)
819
for (x = 0; x < width; x++)
821
pixel = (guint8 *) &buffer32[y * width + x];
823
ico_set_bit_in_data (and_map, width,
824
(height - y -1) * width + x,
825
(pixel[3] > ICO_ALPHA_THRESHOLD ? 0 : 1));
828
xor_map = ico_alloc_map (width, height, header.bpp, &xor_len);
830
/* Now fill in the xor map */
834
for (y = 0; y < height; y++)
835
for (x = 0; x < width; x++)
837
pixel = (guint8 *) &buffer32[y * width + x];
838
palette_index = ico_get_palette_index (color_to_slot, pixel[0],
841
if (ico_get_bit_from_data (and_map, width,
842
(height - y - 1) * width + x))
844
ico_set_bit_in_data (xor_map, width,
845
(height - y -1) * width + x,
850
ico_set_bit_in_data (xor_map, width,
851
(height - y -1) * width + x,
858
for (y = 0; y < height; y++)
859
for (x = 0; x < width; x++)
861
pixel = (guint8 *) &buffer32[y * width + x];
862
palette_index = ico_get_palette_index(color_to_slot, pixel[0],
865
if (ico_get_bit_from_data (and_map, width,
866
(height - y - 1) * width + x))
868
ico_set_nibble_in_data (xor_map, width,
869
(height - y -1) * width + x,
874
ico_set_nibble_in_data (xor_map, width,
875
(height - y - 1) * width + x,
882
for (y = 0; y < height; y++)
883
for (x = 0; x < width; x++)
885
pixel = (guint8 *) &buffer32[y * width + x];
886
palette_index = ico_get_palette_index (color_to_slot,
891
if (ico_get_bit_from_data (and_map, width,
892
(height - y - 1) * width + x))
894
ico_set_byte_in_data (xor_map, width,
895
(height - y - 1) * width + x,
900
ico_set_byte_in_data (xor_map, width,
901
(height - y - 1) * width + x,
909
for (y = 0; y < height; y++)
911
guchar *row = xor_map + (xor_len * (height - y - 1) / height);
913
for (x = 0; x < width; x++)
915
pixel = (guint8 *) &buffer32[y * width + x];
927
for (y = 0; y < height; y++)
928
for (x = 0; x < width; x++)
930
pixel = (guint8 *) &buffer32[y * width + x];
932
((guint32 *) xor_map)[(height - y -1) * width + x] =
933
GUINT32_TO_LE ((pixel[0] << 16) |
940
D((" filled and_map of length %i, xor_map of length %i\n",
944
g_hash_table_destroy (color_to_slot);
949
ico_write_int32 (fp, (guint32*) &header, 3);
950
ico_write_int16 (fp, &header.planes, 2);
951
ico_write_int32 (fp, &header.compression, 6);
954
ico_write_int8 (fp, (guint8 *) palette32, palette_len);
956
ico_write_int8 (fp, xor_map, xor_len);
957
ico_write_int8 (fp, and_map, and_len);
967
ico_save_info_free (IcoSaveInfo *info)
969
g_free (info->depths);
970
g_free (info->default_depths);
971
g_free (info->compress);
972
g_free (info->layers);
973
memset (info, 0, sizeof (IcoSaveInfo));
977
ico_save_image (const gchar *filename,
987
IcoFileHeader header;
988
IcoFileEntry *entries;
991
D(("*** Saving Microsoft icon file %s\n", filename));
993
ico_save_init (image, &info);
995
if (run_mode == GIMP_RUN_INTERACTIVE)
997
/* Allow user to override default values */
998
if ( !ico_save_dialog (image, &info))
999
return GIMP_PDB_CANCEL;
1002
gimp_progress_init_printf (_("Saving '%s'"),
1003
gimp_filename_to_utf8 (filename));
1005
if (! (fp = g_fopen (filename, "wb")))
1007
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
1008
_("Could not open '%s' for writing: %s"),
1009
gimp_filename_to_utf8 (filename), g_strerror (errno));
1010
return GIMP_PDB_EXECUTION_ERROR;
1013
header.reserved = 0;
1014
header.resource_type = 1;
1015
header.icon_count = info.num_icons;
1016
if ( !ico_write_int16 (fp, &header.reserved, 1)
1017
|| !ico_write_int16 (fp, &header.resource_type, 1)
1018
|| !ico_write_int16 (fp, &header.icon_count, 1) )
1020
ico_save_info_free (&info);
1022
return GIMP_PDB_EXECUTION_ERROR;
1025
entries = g_new0 (IcoFileEntry, info.num_icons);
1026
if (fwrite (entries, sizeof (IcoFileEntry), info.num_icons, fp) <= 0)
1028
ico_save_info_free (&info);
1030
return GIMP_PDB_EXECUTION_ERROR;
1033
for (i = 0; i < info.num_icons; i++)
1035
gimp_progress_update ((gdouble)i / (gdouble)info.num_icons);
1037
width = gimp_drawable_width (info.layers[i]);
1038
height = gimp_drawable_height (info.layers[i]);
1039
if (width <= 255 && height <= 255)
1041
entries[i].width = width;
1042
entries[i].height = height;
1046
entries[i].width = 0;
1047
entries[i].height = 0;
1049
if ( info.depths[i] <= 8 )
1050
entries[i].num_colors = 1 << info.depths[i];
1052
entries[i].num_colors = 0;
1053
entries[i].reserved = 0;
1054
entries[i].planes = 1;
1055
entries[i].bpp = info.depths[i];
1056
entries[i].offset = ftell (fp);
1058
if (info.compress[i])
1059
saved = ico_write_png (fp, info.layers[i], info.depths[i]);
1061
saved = ico_write_icon (fp, info.layers[i], info.depths[i]);
1065
ico_save_info_free (&info);
1067
return GIMP_PDB_EXECUTION_ERROR;
1070
entries[i].size = ftell (fp) - entries[i].offset;
1073
for (i = 0; i < info.num_icons; i++)
1075
entries[i].planes = GUINT16_TO_LE (entries[i].planes);
1076
entries[i].bpp = GUINT16_TO_LE (entries[i].bpp);
1077
entries[i].size = GUINT32_TO_LE (entries[i].size);
1078
entries[i].offset = GUINT32_TO_LE (entries[i].offset);
1081
if (fseek (fp, sizeof(IcoFileHeader), SEEK_SET) < 0
1082
|| fwrite (entries, sizeof (IcoFileEntry), info.num_icons, fp) <= 0)
1084
ico_save_info_free (&info);
1086
return GIMP_PDB_EXECUTION_ERROR;
1089
gimp_progress_update (1.0);
1091
ico_save_info_free (&info);
1094
return GIMP_PDB_SUCCESS;