This commit is contained in:
Sven Neumann 2006-10-06 06:42:09 +00:00
parent 5b021fc255
commit f1a49507d1
9 changed files with 963 additions and 901 deletions

View File

@ -1,3 +1,12 @@
2006-10-06 Sven Neumann <sven@gimp.org>
* plug-ins/winicon/icodialog.[ch]
* plug-ins/winicon/icoload.[ch]
* plug-ins/winicon/icosave.[ch]
* plug-ins/winicon/main.[ch]: applied patch from Aurimas Juška
with code cleanup and fixes for bug #346016 and other issues
(see bug #352899).
2006-10-05 Sven Neumann <sven@gimp.org>
* plug-ins/script-fu/scripts/*.scm: applied patch from Saul Goode

View File

@ -19,22 +19,23 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <config.h>
#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>
#define ICO_DBG
/* #define ICO_DBG */
#include "icodialog.h"
#include "main.h"
#include "icodialog.h"
#include "icosave.h"
#include "libgimp/stdplugins-intl.h"
static void ico_bpp_changed (GtkWidget *combo,
GObject *hbox);
static GtkWidget * ico_preview_new (gint32 layer);
static void combo_bpp_changed (GtkWidget *combo,
GObject *hbox);
static GtkWidget *
@ -55,12 +56,69 @@ ico_preview_new (gint32 layer)
}
GtkWidget *
ico_dialog_new (IcoSaveInfo *info)
{
GtkWidget *dialog;
GtkWidget *vbox;
GtkWidget *frame;
GtkWidget *scrolledwindow;
dialog = gimp_dialog_new (_("Save as Windows Icon"), "winicon",
NULL, 0,
gimp_standard_help_func, "plug-in-winicon",
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_SAVE, GTK_RESPONSE_OK,
NULL);
gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
GTK_RESPONSE_OK,
GTK_RESPONSE_CANCEL,
-1);
gimp_window_set_transient (GTK_WINDOW (dialog));
/* We store an array that holds each icon's requested bit depth
with the dialog. It's queried when the dialog is closed so the
save routine knows what colormaps etc to generate in the saved
file. We store twice the number necessary because in the second
set, the color depths that are automatically suggested are stored
for later comparison.
*/
g_object_set_data (G_OBJECT (dialog), "save_info", info);
frame = gimp_frame_new (_("Icon Details"));
gtk_container_set_border_width (GTK_CONTAINER (frame), 12);
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), frame,
TRUE, TRUE, 0);
gtk_widget_show (frame);
scrolledwindow = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow),
GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
gtk_container_add (GTK_CONTAINER (frame), scrolledwindow);
gtk_widget_show (scrolledwindow);
vbox = gtk_vbox_new (FALSE, 12);
gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
g_object_set_data (G_OBJECT (dialog), "icons_vbox", vbox);
gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolledwindow),
vbox);
gtk_widget_show (vbox);
return dialog;
}
/* This function creates and returns an hbox for an icon,
which then gets added to the dialog's main vbox. */
static GtkWidget *
ico_create_icon_hbox (GtkWidget *icon_preview,
gint32 layer,
gint layer_num)
ico_create_icon_hbox (GtkWidget *icon_preview,
gint32 layer,
gint layer_num,
IcoSaveInfo *info)
{
static GtkSizeGroup *size = NULL;
@ -99,12 +157,13 @@ ico_create_icon_hbox (GtkWidget *icon_preview,
combo = gimp_int_combo_box_new (_("1 bpp, 1-bit alpha, 2-slot palette"), 1,
_("4 bpp, 1-bit alpha, 16-slot palette"), 4,
_("8 bpp, 1-bit alpha, 256-slot palette"), 8,
_("32 bpp, 8-bit alpha, no palette"), 32,
_("32 bpp, 8-bit alpha, no palette"), 32,
NULL);
gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), 32);
gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo),
info->depths[layer_num]);
g_signal_connect (combo, "changed",
G_CALLBACK (combo_bpp_changed),
G_CALLBACK (ico_bpp_changed),
hbox);
g_object_set_data (G_OBJECT (hbox), "icon_menu", combo);
@ -115,75 +174,13 @@ ico_create_icon_hbox (GtkWidget *icon_preview,
return hbox;
}
GtkWidget *
ico_specs_dialog_new (gint num_layers)
{
GtkWidget *dialog;
GtkWidget *vbox;
GtkWidget *frame;
GtkWidget *scrolledwindow;
gint *icon_depths, i;
dialog = gimp_dialog_new (_("Save as Windows Icon"), "winicon",
NULL, 0,
gimp_standard_help_func, "plug-in-winicon",
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_SAVE, GTK_RESPONSE_OK,
NULL);
gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
GTK_RESPONSE_OK,
GTK_RESPONSE_CANCEL,
-1);
gimp_window_set_transient (GTK_WINDOW (dialog));
/* We store an array that holds each icon's requested bit depth
with the dialog. It's queried when the dialog is closed so the
save routine knows what colormaps etc to generate in the saved
file. We store twice the number necessary because in the second
set, the color depths that are automatically suggested are stored
for later comparison.
*/
icon_depths = g_new (gint, 2 * num_layers);
for (i = 0; i < 2 * num_layers; i++)
icon_depths[i] = 32;
g_object_set_data (G_OBJECT (dialog), "icon_depths", icon_depths);
frame = gimp_frame_new (_("Icon Details"));
gtk_container_set_border_width (GTK_CONTAINER (frame), 12);
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), frame,
TRUE, TRUE, 0);
gtk_widget_show (frame);
scrolledwindow = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow),
GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
gtk_container_add (GTK_CONTAINER (frame), scrolledwindow);
gtk_widget_show (scrolledwindow);
vbox = gtk_vbox_new (FALSE, 12);
gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
g_object_set_data (G_OBJECT (dialog), "icons_vbox", vbox);
gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolledwindow),
vbox);
gtk_widget_show (vbox);
return dialog;
}
static GtkWidget *
ico_specs_dialog_get_layer_preview (GtkWidget *dialog,
gint32 layer)
ico_dialog_get_layer_preview (GtkWidget *dialog,
gint32 layer)
{
GtkWidget *preview;
GtkWidget *icon_hbox;
gchar key[MAXLEN];
gchar key[ICO_MAXBUF];
g_snprintf (key, sizeof (key), "layer_%i_hbox", layer);
icon_hbox = g_object_get_data (G_OBJECT (dialog), key);
@ -205,34 +202,12 @@ ico_specs_dialog_get_layer_preview (GtkWidget *dialog,
return preview;
}
void
ico_specs_dialog_add_icon (GtkWidget *dialog,
gint32 layer,
gint layer_num)
static void
ico_dialog_update_icon_preview (GtkWidget *dialog,
gint32 layer,
gint bpp)
{
GtkWidget *vbox;
GtkWidget *hbox;
GtkWidget *preview;
gchar key[MAXLEN];
vbox = g_object_get_data (G_OBJECT (dialog), "icons_vbox");
preview = ico_preview_new (layer);
hbox = ico_create_icon_hbox (preview, layer, layer_num);
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
gtk_widget_show (hbox);
/* Let's make the hbox accessible through the layer ID */
g_snprintf (key, sizeof (key), "layer_%i_hbox", layer);
g_object_set_data (G_OBJECT (dialog), key, hbox);
}
void
ico_specs_dialog_update_icon_preview (GtkWidget *dialog,
gint32 layer,
gint bpp)
{
GtkWidget *preview = ico_specs_dialog_get_layer_preview (dialog, layer);
GtkWidget *preview = ico_dialog_get_layer_preview (dialog, layer);
GdkPixbuf *pixbuf;
gint w = gimp_drawable_width (layer);
gint h = gimp_drawable_height (layer);
@ -249,6 +224,8 @@ ico_specs_dialog_update_icon_preview (GtkWidget *dialog,
gint32 tmp_image;
gint32 tmp_layer;
guchar *buffer;
guchar *cmap;
gint num_colors;
image = gimp_drawable_get_image (layer);
@ -257,9 +234,6 @@ ico_specs_dialog_update_icon_preview (GtkWidget *dialog,
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);
@ -279,7 +253,6 @@ ico_specs_dialog_update_icon_preview (GtkWidget *dialog,
buffer = g_malloc (w * h * 4);
gimp_pixel_rgn_get_rect (&src_pixel_rgn, buffer, 0, 0, w, h);
gimp_pixel_rgn_set_rect (&dst_pixel_rgn, buffer, 0, 0, w, h);
g_free (buffer);
gimp_drawable_detach (tmp);
gimp_drawable_detach (drawable);
@ -289,7 +262,45 @@ ico_specs_dialog_update_icon_preview (GtkWidget *dialog,
gimp_image_convert_indexed (tmp_image,
GIMP_FS_DITHER, GIMP_MAKE_PALETTE,
1 << bpp, TRUE, FALSE, "dummy");
1 <<bpp, TRUE, FALSE, "dummy");
cmap = gimp_image_get_colormap (tmp_image, &num_colors);
if ( num_colors == (1 << bpp) &&
!ico_cmap_contains_black (cmap, num_colors))
{
/* Windows icons with color maps need the color black.
* We need to eliminate one more color to make room for black.
*/
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);
}
tmp = gimp_drawable_get (tmp_layer);
gimp_pixel_rgn_init (&dst_pixel_rgn,
tmp, 0, 0, w, h, TRUE, FALSE);
gimp_pixel_rgn_set_rect (&dst_pixel_rgn, buffer, 0, 0, w, h);
gimp_drawable_detach (tmp);
if (!gimp_drawable_is_rgb (layer))
gimp_image_convert_rgb (tmp_image);
gimp_image_convert_indexed (tmp_image,
GIMP_FS_DITHER, GIMP_MAKE_PALETTE,
(1<<bpp) - 1, TRUE, FALSE, "dummy");
}
g_free (cmap);
g_free (buffer);
pixbuf = gimp_drawable_get_thumbnail (tmp_layer,
MIN (w, 128), MIN (h, 128),
@ -308,30 +319,57 @@ ico_specs_dialog_update_icon_preview (GtkWidget *dialog,
g_object_unref (pixbuf);
}
static void
combo_bpp_changed (GtkWidget *combo,
GObject *hbox)
void
ico_dialog_add_icon (GtkWidget *dialog,
gint32 layer,
gint layer_num)
{
GtkWidget *dialog = gtk_widget_get_toplevel (combo);
gint32 layer;
gint layer_num;
gint bpp;
gint *icon_depths;
GtkWidget *vbox;
GtkWidget *hbox;
GtkWidget *preview;
gchar key[ICO_MAXBUF];
IcoSaveInfo *info;
vbox = g_object_get_data (G_OBJECT (dialog), "icons_vbox");
info = g_object_get_data (G_OBJECT (dialog), "save_info");
preview = ico_preview_new (layer);
hbox = ico_create_icon_hbox (preview, layer, layer_num, info);
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
gtk_widget_show (hbox);
/* Let's make the hbox accessible through the layer ID */
g_snprintf (key, sizeof (key), "layer_%i_hbox", layer);
g_object_set_data (G_OBJECT (dialog), key, hbox);
ico_dialog_update_icon_preview (dialog, layer, info->depths[layer_num]);
}
static void
ico_bpp_changed (GtkWidget *combo,
GObject *hbox)
{
GtkWidget *dialog;
gint32 layer;
gint layer_num;
gint bpp;
IcoSaveInfo *info;
dialog = gtk_widget_get_toplevel (combo);
gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (combo), &bpp);
icon_depths = g_object_get_data (G_OBJECT (dialog), "icon_depths");
if (! icon_depths)
{
D(("Something's wrong -- can't get icon_depths array from dialog\n"));
return;
}
info = g_object_get_data (G_OBJECT (dialog), "save_info");
g_assert (info);
layer = GPOINTER_TO_INT (g_object_get_data (hbox, "icon_layer"));
layer_num = GPOINTER_TO_INT (g_object_get_data (hbox, "icon_layer_num"));
/* Update vector entry for later when we're actually saving,
and update the preview right away ... */
icon_depths[layer_num] = bpp;
ico_specs_dialog_update_icon_preview (dialog, layer, bpp);
info->depths[layer_num] = bpp;
ico_dialog_update_icon_preview (dialog, layer, bpp);
}

View File

@ -23,13 +23,9 @@
#define __ICO_DIALOG_H__
GtkWidget * ico_specs_dialog_new (gint num_layers);
void ico_specs_dialog_add_icon (GtkWidget *dialog,
gint32 layer,
gint layer_num);
void ico_specs_dialog_update_icon_preview (GtkWidget *dialog,
gint32 layer,
gint bpp);
GtkWidget * ico_dialog_new (IcoSaveInfo *info);
void ico_dialog_add_icon (GtkWidget *dialog,
gint32 layer,
gint layer_num);
#endif /* __ICO_DIALOG_H__ */

View File

@ -27,10 +27,12 @@
#include <glib/gstdio.h>
#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>
/* #define ICO_DBG */
#include "main.h"
#include "icoload.h"
#include "libgimp/stdplugins-intl.h"
@ -55,13 +57,6 @@ static gint ico_read_int16 (FILE *fp,
static gint ico_read_int32 (FILE *fp,
guint32 *data,
gint count);
static gboolean ico_init (const gchar *filename,
MsIcon *ico);
static void ico_read_entry (MsIcon *ico,
MsIconEntry *entry);
static void ico_read_data (MsIcon *ico,
gint icon_num);
static gint
ico_read_int32 (FILE *fp,
@ -124,128 +119,89 @@ ico_read_int8 (FILE *fp,
}
static gboolean
ico_init (const gchar *filename,
MsIcon *ico)
static guint32
ico_read_init (FILE *fp)
{
memset (ico, 0, sizeof (MsIcon));
if (! (ico->fp = g_fopen (filename, "rb")))
IcoFileHeader header;
/* read and check file header */
if (!ico_read_int16 (fp, &header.reserved, 1)
|| !ico_read_int16 (fp, &header.resource_type, 1)
|| !ico_read_int16 (fp, &header.icon_count, 1)
|| header.reserved != 0
|| header.resource_type != 1)
{
g_message (_("Could not open '%s' for reading: %s"),
gimp_filename_to_utf8 (filename), g_strerror (errno));
return FALSE;
return 0;
}
ico->filename = filename;
ico->cp += ico_read_int16 (ico->fp, &ico->reserved, 1);
ico->cp += ico_read_int16 (ico->fp, &ico->resource_type, 1);
/* Icon files use 1 as resource type, that's what I wrote this for.
From descriptions on the web it seems as if this loader should
also be able to handle Win 3.11 - Win 95 cursor files (.cur),
which use resource type 2. I haven't tested this though. */
if (ico->reserved != 0 ||
(ico->resource_type != 1 && ico->resource_type != 2))
{
ico_cleanup (ico);
return FALSE;
}
return TRUE;
return header.icon_count;
}
static void
ico_read_entry (MsIcon *ico,
MsIconEntry *entry)
gboolean
ico_read_size (FILE *fp,
IcoLoadInfo *info)
{
g_return_if_fail (ico != NULL);
g_return_if_fail (entry != NULL);
guint32 magic;
ico->cp += ico_read_int8 (ico->fp, &entry->width, 1);
ico->cp += ico_read_int8 (ico->fp, &entry->height, 1);
ico->cp += ico_read_int8 (ico->fp, &entry->num_colors, 1);
ico->cp += ico_read_int8 (ico->fp, &entry->reserved, 1);
if ( fseek (fp, info->offset, SEEK_SET) < 0 )
return FALSE;
ico->cp += ico_read_int16 (ico->fp, &entry->num_planes, 1);
ico->cp += ico_read_int16 (ico->fp, &entry->bpp, 1);
ico->cp += ico_read_int32 (ico->fp, &entry->size, 1);
ico->cp += ico_read_int32 (ico->fp, &entry->offset, 1);
D(("Read entry with w: "
"%i, h: %i, num_colors: %i, bpp: %i, size %i, offset %i\n",
entry->width, entry->height, entry->num_colors, entry->bpp,
entry->size, entry->offset));
ico_read_int32 (fp, &magic, 1);
if (magic == 40)
{
if (ico_read_int32 (fp, &info->width, 1)
&& ico_read_int32 (fp, &info->height, 1))
{
info->height /= 2;
D(("ico_read_size: ICO: %ix%i\n", info->width, info->height));
return TRUE;
}
else
{
info->width = 0;
info->height = 0;
return FALSE;
}
}
return FALSE;
}
static void
ico_read_data (MsIcon *ico,
gint icon_num)
static IcoLoadInfo*
ico_read_info (FILE *fp,
gint icon_count)
{
MsIconData *data;
MsIconEntry *entry;
gint length;
gint i;
IcoFileEntry *entries;
IcoLoadInfo *info;
g_return_if_fail (ico != NULL);
D(("Reading data for icon %i ------------------------------\n", icon_num));
entry = &ico->icon_dir[icon_num];
data = &ico->icon_data[icon_num];
ico->cp = entry->offset;
if (fseek (ico->fp, entry->offset, SEEK_SET) < 0)
return;
D((" starting at offset %i\n", entry->offset));
ico->cp += ico_read_int32 (ico->fp, &data->header_size, 1);
ico->cp += ico_read_int32 (ico->fp, &data->width, 1);
ico->cp += ico_read_int32 (ico->fp, &data->height, 1);
ico->cp += ico_read_int16 (ico->fp, &data->planes, 1);
ico->cp += ico_read_int16 (ico->fp, &data->bpp, 1);
ico->cp += ico_read_int32 (ico->fp, &data->compression, 1);
ico->cp += ico_read_int32 (ico->fp, &data->image_size, 1);
ico->cp += ico_read_int32 (ico->fp, &data->x_res, 1);
ico->cp += ico_read_int32 (ico->fp, &data->y_res, 1);
ico->cp += ico_read_int32 (ico->fp, &data->used_clrs, 1);
ico->cp += ico_read_int32 (ico->fp, &data->important_clrs, 1);
D((" header size %i, "
"w %i, h %i, planes %i, size %i, bpp %i, used %i, imp %i.\n",
data->header_size, data->width, data->height,
data->planes, data->image_size, data->bpp,
data->used_clrs, data->important_clrs));
if (data->bpp <= 8)
/* read icon entries */
entries = g_new (IcoFileEntry, icon_count);
if ( fread (entries, sizeof(IcoFileEntry), icon_count, fp) <= 0 )
{
if (data->used_clrs == 0)
data->used_clrs = (1 << data->bpp);
D((" allocating a %i-slot palette for %i bpp.\n",
data->used_clrs, data->bpp));
data->palette = g_new0 (guint32, data->used_clrs);
ico->cp += ico_read_int8 (ico->fp,
(guint8 *) data->palette, data->used_clrs * 4);
g_free (entries);
return NULL;
}
data->xor_map = ico_alloc_map (entry->width, entry->height,
data->bpp, &length);
ico->cp += ico_read_int8 (ico->fp, data->xor_map, length);
D((" length of xor_map: %i\n", length));
info = g_new (IcoLoadInfo, icon_count);
for (i = 0; i < icon_count; i++)
{
info[i].width = entries[i].width;
info[i].height = entries[i].height;
info[i].bpp = GUINT16_FROM_LE (entries[i].bpp);
info[i].size = GUINT32_FROM_LE (entries[i].size);
info[i].offset = GUINT32_FROM_LE (entries[i].offset);
/* Read in and_map. It's padded out to 32 bits per line: */
data->and_map = ico_alloc_map (entry->width, entry->height, 1, &length);
ico->cp += ico_read_int8 (ico->fp, data->and_map, length);
D((" length of and_map: %i\n", length));
if (info[i].width == 0 || info[i].height == 0)
{
ico_read_size (fp, info+i);
}
D(("ico_read_info: %ix%i (%i bits, size: %i, offset: %i)\n",
info[i].width, info[i].height, info[i].bpp,
info[i].size, info[i].offset));
}
g_free (entries);
return info;
}
gint
@ -294,9 +250,9 @@ ico_get_nibble_from_data (const guint8 *data,
}
gint
ico_get_byte_from_data (guint8 *data,
gint line_width,
gint byte)
ico_get_byte_from_data (const guint8 *data,
gint line_width,
gint byte)
{
gint line;
gint width32;
@ -310,47 +266,88 @@ ico_get_byte_from_data (guint8 *data,
return data[line * width32 * 4 + offset];
}
static gint32
ico_load_layer (gint32 image,
MsIcon *ico,
gint i)
static gboolean
ico_read_icon (FILE *fp,
guint32 header_size,
guchar *buffer,
gint maxsize,
gint *width,
gint *height)
{
GimpDrawable *drawable;
GimpPixelRgn pixel_rgn;
MsIconData *idata;
MsIconEntry *ientry;
guint8 *xor_map;
guint8 *and_map;
guint32 *palette;
guint32 *dest_vec;
gint32 layer;
gchar buf[MAXLEN];
gint x, y, w, h;
IcoFileDataHeader data;
gint length;
gint x, y, w, h;
guchar *xor_map, *and_map;
guint32 *palette;
guint32 *dest_vec;
idata = &ico->icon_data[i];
ientry = &ico->icon_dir[i];
xor_map = ico->icon_data[i].xor_map;
and_map = ico->icon_data[i].and_map;
palette = ico->icon_data[i].palette;
w = ico->icon_dir[i].width;
h = ico->icon_dir[i].height;
palette = NULL;
gimp_progress_update ((gdouble) i / (gdouble) ico->icon_count);
data.header_size = header_size;
ico_read_int32 (fp, &data.width, 1);
ico_read_int32 (fp, &data.height, 1);
ico_read_int16 (fp, &data.planes, 1);
ico_read_int16 (fp, &data.bpp, 1);
ico_read_int32 (fp, &data.compression, 1);
ico_read_int32 (fp, &data.image_size, 1);
ico_read_int32 (fp, &data.x_res, 1);
ico_read_int32 (fp, &data.y_res, 1);
ico_read_int32 (fp, &data.used_clrs, 1);
ico_read_int32 (fp, &data.important_clrs, 1);
if (w <= 0 || h <= 0)
return -1;
D((" header size %i, "
"w %i, h %i, planes %i, size %i, bpp %i, used %i, imp %i.\n",
data.header_size, data.width, data.height,
data.planes, data.image_size, data.bpp,
data.used_clrs, data.important_clrs));
g_snprintf (buf, sizeof (buf), _("Icon #%i"), i + 1);
if (data.planes != 1
|| data.compression != 0)
{
D(("skipping image: invalid header\n"));
return FALSE;
}
layer = gimp_layer_new (image, buf, w, h,
GIMP_RGBA_IMAGE, 100, GIMP_NORMAL_MODE);
if (data.bpp != 1 && data.bpp != 4
&& data.bpp != 8 && data.bpp != 24
&& data.bpp != 32)
{
D(("skipping image: invalid depth: %i\n", data.bpp));
return FALSE;
}
gimp_image_add_layer (image, layer, i);
drawable = gimp_drawable_get (layer);
if (data.width * data.height * 2 > maxsize)
{
D(("skipping image: too large\n"));
return FALSE;
}
dest_vec = g_new (guint32, w * h);
w = data.width;
h = data.height / 2;
switch (idata->bpp)
if (data.bpp <= 8)
{
if (data.used_clrs == 0)
data.used_clrs = (1 << data.bpp);
D((" allocating a %i-slot palette for %i bpp.\n",
data.used_clrs, data.bpp));
palette = g_new0 (guint32, data.used_clrs);
ico_read_int8 (fp, (guint8 *) palette, data.used_clrs * 4);
}
xor_map = ico_alloc_map (w, h, data.bpp, &length);
ico_read_int8 (fp, xor_map, length);
D((" length of xor_map: %i\n", length));
/* Read in and_map. It's padded out to 32 bits per line: */
and_map = ico_alloc_map (w, h, 1, &length);
ico_read_int8 (fp, and_map, length);
D((" length of and_map: %i\n", length));
dest_vec = (guint32 *) buffer;
switch (data.bpp)
{
case 1:
for (y = 0; y < h; y++)
@ -411,7 +408,7 @@ ico_load_layer (gint32 image,
default:
{
gint bytespp = idata->bpp/8;
gint bytespp = data.bpp/8;
for (y = 0; y < h; y++)
for (x = 0; x < w; x++)
@ -422,7 +419,7 @@ ico_load_layer (gint32 image,
G_VAL_GIMP (dest) = xor_map[(y * w + x) * bytespp + 1];
R_VAL_GIMP (dest) = xor_map[(y * w + x) * bytespp + 2];
if (idata->bpp < 32)
if (data.bpp < 32)
{
if (ico_get_bit_from_data (and_map, w, y * w + x))
A_VAL_GIMP (dest) = 0;
@ -436,79 +433,133 @@ ico_load_layer (gint32 image,
}
}
}
if (palette)
g_free (palette);
g_free (xor_map);
g_free (and_map);
*width = w;
*height = h;
return TRUE;
}
gimp_pixel_rgn_init (&pixel_rgn, drawable,
0, 0, drawable->width, drawable->height,
TRUE, FALSE);
gimp_pixel_rgn_set_rect (&pixel_rgn, (const guchar *) dest_vec,
0, 0, drawable->width, drawable->height);
gint32
ico_load_layer (FILE *fp,
gint32 image,
gint32 icon_num,
guchar *buffer,
gint maxsize,
IcoLoadInfo *info)
{
gint width, height;
gint32 layer;
guint32 first_bytes;
GimpDrawable *drawable;
GimpPixelRgn pixel_rgn;
gchar buf [ICO_MAXBUF];
g_free (dest_vec);
if ( fseek (fp, info->offset, SEEK_SET) < 0
|| !ico_read_int32 (fp, &first_bytes, 1) )
return -1;
if (first_bytes == 40)
{
if (!ico_read_icon (fp, first_bytes, buffer, maxsize, &width, &height))
return -1;
}
else
{
return -1;
}
/* read successfully. add to image */
g_snprintf (buf, sizeof (buf), _("Icon #%i"), icon_num+1);
layer = gimp_layer_new (image, buf, width, height,
GIMP_RGBA_IMAGE, 100, GIMP_NORMAL_MODE);
gimp_image_add_layer (image, layer, icon_num);
drawable = gimp_drawable_get (layer);
gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0,
drawable->width, drawable->height, TRUE, FALSE);
gimp_pixel_rgn_set_rect (&pixel_rgn, (const guchar*) buffer,
0, 0, drawable->width,
drawable->height);
gimp_drawable_detach (drawable);
return layer;
}
gint32
ico_load_image (const gchar *filename)
{
MsIcon ico;
gint32 image;
gint32 layer;
gint width = 0;
gint height = 0;
gint i;
FILE *fp;
IcoLoadInfo *info;
gint max_width, max_height;
gint i;
gint32 image;
gint32 layer;
guchar *buffer;
guint icon_count;
gint maxsize;
gimp_progress_init_printf (_("Opening '%s'"),
gimp_filename_to_utf8 (filename));
if (! ico_init (filename, &ico))
return -1;
ico.cp += ico_read_int16 (ico.fp, &ico.icon_count, 1);
ico.icon_dir = g_new0 (MsIconEntry, ico.icon_count);
ico.icon_data = g_new0 (MsIconData, ico.icon_count);
D(("*** %s: Microsoft icon file, containing %i icon(s)\n",
ico.filename, ico.icon_count));
for (i = 0; i < ico.icon_count; i++)
ico_read_entry (&ico, &ico.icon_dir[i]);
/* Do a quick scan of the icons in the file to find the largest icon */
for (i = 0; i < ico.icon_count; i++)
fp = g_fopen (filename, "rb");
if ( !fp )
{
if (ico.icon_dir[i].width > width)
width = ico.icon_dir[i].width;
if (ico.icon_dir[i].height > height)
height = ico.icon_dir[i].height;
g_message (_("Could not open '%s' for reading: %s"),
gimp_filename_to_utf8 (filename), g_strerror (errno));
return -1;
}
if (width < 1 || height < 1)
return -1;
icon_count = ico_read_init (fp);
if (!icon_count)
{
fclose (fp);
return -1;
}
image = gimp_image_new (width, height, GIMP_RGB);
gimp_image_set_filename (image, ico.filename);
info = ico_read_info (fp, icon_count);
if (!info)
{
fclose (fp);
return -1;
}
/* Scan icons again and set up a layer for each icon */
for (i = 0; i < ico.icon_count; i++)
ico_read_data (&ico, i);
/* find width and height of image */
max_width = 0;
max_height = 0;
for (i = 0; i < icon_count; i++)
{
if ( info[i].width > max_width )
max_width = info[i].width;
if ( info[i].height > max_height )
max_height = info[i].height;
}
if ( max_width <= 0 || max_height <= 0 )
{
g_free (info);
fclose (fp);
return -1;
}
D(("image size: %ix%i\n", max_width, max_height));
layer = ico_load_layer (image, &ico, 0);
for (i = 1; i < ico.icon_count; i++)
ico_load_layer (image, &ico, i);
image = gimp_image_new (max_width, max_height, GIMP_RGB);
gimp_image_set_filename (image, filename);
if (layer != -1)
gimp_image_set_active_layer (image, layer);
D(("*** icon successfully loaded.\n\n"));
maxsize = max_width * max_height * 4;
buffer = g_new (guchar, max_width * max_height * 4);
for (i = 0; i < icon_count; i++)
{
layer = ico_load_layer (fp, image, i, buffer, maxsize, info+i);
}
g_free (buffer);
g_free (info);
fclose (fp);
gimp_progress_update (1.0);
ico_cleanup (&ico);
return image;
}
@ -517,60 +568,74 @@ ico_load_thumbnail_image (const gchar *filename,
gint *width,
gint *height)
{
MsIcon ico;
gint32 image;
gint32 layer;
gint w = 0;
gint h = 0;
gint match = 0;
gint i;
FILE *fp;
IcoLoadInfo *info;
gint32 image;
gint32 layer;
gint w = 0;
gint h = 0;
gint bpp = 0;
gint match = 0;
gint i, icon_count;
guchar *buffer;
gimp_progress_init_printf (_("Opening thumbnail for '%s'"),
gimp_filename_to_utf8 (filename));
if (! ico_init (filename, &ico))
return -1;
fp = g_fopen (filename, "rb");
if ( !fp )
{
g_message (_("Could not open '%s' for reading: %s"),
gimp_filename_to_utf8 (filename), g_strerror (errno));
return -1;
}
ico.cp += ico_read_int16 (ico.fp, &ico.icon_count, 1);
ico.icon_dir = g_new0 (MsIconEntry, ico.icon_count);
ico.icon_data = g_new0 (MsIconData, ico.icon_count);
icon_count = ico_read_init (fp);
if ( !icon_count )
{
fclose (fp);
return -1;
}
D(("*** %s: Microsoft icon file, containing %i icon(s)\n",
ico.filename, ico.icon_count));
filename, icon_count));
for (i = 0; i < ico.icon_count; i++)
ico_read_entry (&ico, &ico.icon_dir[i]);
info = ico_read_info (fp, icon_count);
if ( !info )
{
fclose (fp);
return -1;
}
/* Do a quick scan of the icons in the file to find the best match */
for (i = 0; i < ico.icon_count; i++)
for (i = 0; i < icon_count; i++)
{
if ((ico.icon_dir[i].width > w && w < *width) ||
(ico.icon_dir[i].height > h && h < *height))
if ((info[i].width > w && w < *width) ||
(info[i].height > h && h < *height))
{
w = ico.icon_dir[i].width;
h = ico.icon_dir[i].height;
w = info[i].width;
h = info[i].height;
bpp = info[i].bpp;
match = i;
}
else if ( w == info[i].width
&& h == info[i].height
&& info[i].bpp > bpp )
{
/* better quality */
bpp = info[i].bpp;
match = i;
}
}
if (w < 1 || h < 1)
if (w <= 0 || h <= 0)
return -1;
ico_read_data (&ico, match);
image = gimp_image_new (w, h, GIMP_RGB);
layer = ico_load_layer (image, &ico, match);
/* Do a quick scan of the icons in the file to find the largest icon */
for (i = 0, w = 0, h = 0; i < ico.icon_count; i++)
{
if (ico.icon_dir[i].width > w)
w = ico.icon_dir[i].width;
if (ico.icon_dir[i].height > h)
h = ico.icon_dir[i].height;
}
buffer = g_new (guchar, w*h*4);
layer = ico_load_layer (fp, image, match, buffer, w*h*4, info+match);
g_free (buffer);
*width = w;
*height = h;
@ -579,7 +644,8 @@ ico_load_thumbnail_image (const gchar *filename,
gimp_progress_update (1.0);
ico_cleanup (&ico);
g_free (info);
fclose (fp);
return image;
}

View File

@ -22,7 +22,6 @@
#ifndef __ICO_LOAD_H__
#define __ICO_LOAD_H__
gint32 ico_load_image (const gchar *filename);
gint32 ico_load_thumbnail_image (const gchar *filename,
gint *width,
@ -38,5 +37,4 @@ gint ico_get_byte_from_data (const guint8 *data,
gint byte);
#endif /* __ICO_LOAD_H__ */

View File

@ -38,27 +38,37 @@
#include "libgimp/stdplugins-intl.h"
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);
static gint * ico_show_icon_dialog (gint32 image_ID, gint *num_icons);
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);
/* Helpers to set bits in a *cleared* data chunk */
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);
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);
static gint ico_get_layer_num_colors (gint32 layer,
gboolean *uses_alpha_levels);
gboolean *uses_alpha_levels);
static void ico_image_get_reduced_buf (guint32 layer,
gint bpp,
gint *num_colors,
guchar **cmap_out,
guchar **buf_out);
gint bpp,
gint *num_colors,
guchar **cmap_out,
guchar **buf_out);
static gint
@ -143,142 +153,90 @@ ico_write_int8 (FILE *fp,
}
static gint*
ico_show_icon_dialog (gint32 image_ID,
gint *num_icons)
static void
ico_save_init (gint32 image_ID, IcoSaveInfo *info)
{
GtkWidget *dialog, *hbox;
GtkWidget *icon_menu;
gint *layers, *icon_depths = NULL;
gint num_layers, i, num_colors;
gint *layers;
gint i, num_colors;
gboolean uses_alpha_values;
gchar key[MAXLEN];
*num_icons = 0;
gimp_ui_init ("winicon", TRUE);
layers = gimp_image_get_layers (image_ID, &num_layers);
dialog = ico_specs_dialog_new (num_layers);
for (i = 0; i < num_layers; i++)
{
/* if (gimp_layer_get_visible(layers[i])) */
ico_specs_dialog_add_icon (dialog, layers[i], i);
}
/* Scale the thing to approximately fit its content, but not too large ... */
gtk_window_set_default_size (GTK_WINDOW (dialog),
-1,
120 + (num_layers > 4 ? 500 : num_layers * 120));
icon_depths = g_object_get_data (G_OBJECT (dialog), "icon_depths");
layers = gimp_image_get_layers (image_ID, &info->num_icons);
info->layers = layers;
info->depths = g_new (gint, info->num_icons);
info->default_depths = g_new (gint, info->num_icons);
/* 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 (i = 0; i < num_layers; i++)
for (i = 0; i < info->num_icons; i++)
{
num_colors = ico_get_layer_num_colors (layers[i], &uses_alpha_values);
g_snprintf (key, MAXLEN, "layer_%i_hbox", layers[i]);
hbox = g_object_get_data (G_OBJECT (dialog), key);
icon_menu = g_object_get_data (G_OBJECT (hbox), "icon_menu");
if (!uses_alpha_values)
{
if (num_colors <= 2)
{
/* Let's suggest monochrome */
icon_depths[i] = 1;
icon_depths[num_layers + i] = 1;
ico_specs_dialog_update_icon_preview (dialog, layers[i], 2);
gtk_combo_box_set_active (GTK_COMBO_BOX (icon_menu), 0);
info->default_depths [i] = 1;
}
else if (num_colors <= 16)
{
/* Let's suggest 4bpp */
icon_depths[i] = 4;
icon_depths[num_layers + i] = 4;
ico_specs_dialog_update_icon_preview (dialog, layers[i], 4);
gtk_combo_box_set_active (GTK_COMBO_BOX (icon_menu), 1);
info->default_depths [i] = 4;
}
else if (num_colors <= 256)
{
/* Let's suggest 8bpp */
icon_depths[i] = 8;
icon_depths[num_layers + i] = 8;
ico_specs_dialog_update_icon_preview (dialog, layers[i], 8);
gtk_combo_box_set_active (GTK_COMBO_BOX (icon_menu), 2);
info->default_depths [i] = 8;
}
}
/* Otherwise, or if real alpha levels are used, stick with 32bpp */
else
{
/* Otherwise, or if real alpha levels are used, stick with 32bpp */
info->default_depths [i] = 32;
}
}
g_free (layers);
/* set with default values */
memcpy (info->depths, info->default_depths,
sizeof (gint) * info->num_icons);
}
static gboolean
ico_save_dialog (gint32 image_ID,
IcoSaveInfo *info)
{
GtkWidget *dialog;
gint i;
gint response;
gimp_ui_init ("winicon", TRUE);
dialog = ico_dialog_new (info);
for (i = 0; i < info->num_icons; i++)
{
/* if (gimp_layer_get_visible(layers[i])) */
ico_dialog_add_icon (dialog, info->layers[i], i);
}
/* Scale the thing to approximately fit its content, but not too large ... */
gtk_window_set_default_size (GTK_WINDOW (dialog),
-1,
120 + (info->num_icons > 4 ?
500 : info->num_icons * 120));
gtk_widget_show (dialog);
if (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK)
*num_icons = num_layers;
else
icon_depths = NULL;
response = gimp_dialog_run (GIMP_DIALOG (dialog));
gtk_widget_destroy (dialog);
return icon_depths;
if (response == GTK_RESPONSE_OK)
return TRUE;
else
return FALSE;
}
static GimpPDBStatusType
ico_init (const gchar *filename,
MsIcon *ico,
gint num_icons)
{
memset (ico, 0, sizeof (MsIcon));
if (! (ico->fp = g_fopen (filename, "wb")))
{
g_message (_("Could not open '%s' for writing: %s"),
gimp_filename_to_utf8 (filename), g_strerror (errno));
return GIMP_PDB_EXECUTION_ERROR;
}
ico->filename = filename;
ico->reserved = 0;
ico->resource_type = 1;
ico->icon_count = num_icons;
ico->icon_dir = g_new0 (MsIconEntry, num_icons);
ico->icon_data = g_new0 (MsIconData, num_icons);
return GIMP_PDB_SUCCESS;
}
static void
ico_init_direntry (MsIconEntry *entry,
gint32 layer,
gint bpp)
{
/* Was calloc'd, so initialized to 0. */
entry->width = gimp_drawable_width (layer);
entry->height = gimp_drawable_height (layer);
if (bpp < 8)
entry->num_colors = (1 << bpp);
entry->num_planes = 1;
entry->bpp = bpp;
D(("Initialized entry to w %i, h %i, bpp %i\n",
gimp_drawable_width (layer), entry->width, entry->bpp));
/* We'll set size and offset when writing things out */
}
static void
ico_set_bit_in_data (guint8 *data,
gint line_width,
@ -317,7 +275,8 @@ ico_set_nibble_in_data (guint8 *data,
offset = nibble_num % line_width;
nibble_val = nibble_val & 0x0000000F;
data[line * width8 * 4 + offset/2] |= (nibble_val << (4 * (1 - (offset % 2))));
data[line * width8 * 4 + offset/2] |=
(nibble_val << (4 * (1 - (offset % 2))));
}
@ -343,7 +302,6 @@ ico_set_byte_in_data (guint8 *data,
}
/* Create a colormap from the given buffer data */
static guint32 *
ico_create_palette(guchar *cmap,
@ -455,291 +413,6 @@ ico_get_palette_index (GHashTable *hash,
return *slot;
}
static void
ico_init_data (MsIcon *ico,
gint num_icon,
gint32 layer,
gint bpp)
{
MsIconEntry *entry;
MsIconData *data;
gint and_len, xor_len, palette_index, x, y;
gint num_colors = 0, num_colors_used = 0, black_index = 0;
guchar *buffer = NULL, *pixel;
guint32 *buffer32;
guchar *palette;
GHashTable *color_to_slot = NULL;
D(("Creating data structures for icon %i ------------------------\n", num_icon));
/* Shortcuts, for convenience */
entry = &ico->icon_dir[num_icon];
data = &ico->icon_data[num_icon];
/* Entries and data were calloc'd, so initialized to 0. */
data->header_size = 40;
data->width = gimp_drawable_width (layer);
data->height = 2 * gimp_drawable_height (layer);
data->planes = 1;
data->bpp = bpp;
num_colors = (1L << bpp);
D((" header size %i, w %i, h %i, planes %i, bpp %i\n",
data->header_size, data->width, data->height, data->planes, data->bpp));
/* Reduce colors in copy of image */
ico_image_get_reduced_buf (layer, bpp, &num_colors_used, &palette, &buffer);
buffer32 = (guint32 *) buffer;
/* Set up colormap and andmap when necessary: */
if (bpp <= 8)
{
/* Create a colormap */
data->palette = ico_create_palette (palette,
num_colors, num_colors_used,
&black_index);
data->palette_len = num_colors * 4;
color_to_slot = ico_create_color_to_palette_map (data->palette,
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: */
data->and_map = ico_alloc_map (entry->width, entry->height, 1, &and_len);
data->and_len = and_len;
for (y = 0; y < entry->height; y++)
for (x = 0; x < entry->width; x++)
{
pixel = (guint8 *) &buffer32[y * entry->width + x];
ico_set_bit_in_data (data->and_map, entry->width,
(entry->height-y-1) * entry->width + x,
(pixel[3] == 255 ? 0 : 1));
}
data->xor_map = ico_alloc_map(entry->width, entry->height, bpp, &xor_len);
data->xor_len = xor_len;
/* Now fill in the xor map */
switch (bpp)
{
case 1:
for (y = 0; y < entry->height; y++)
for (x = 0; x < entry->width; x++)
{
pixel = (guint8 *) &buffer32[y * entry->width + x];
palette_index = ico_get_palette_index (color_to_slot,
pixel[0], pixel[1], pixel[2]);
if (ico_get_bit_from_data (data->and_map, entry->width,
(entry->height-y-1) * entry->width + x))
{
ico_set_bit_in_data (data->xor_map, entry->width,
(entry->height-y-1) * entry->width + x,
black_index);
}
else
{
ico_set_bit_in_data (data->xor_map, entry->width,
(entry->height-y-1) * entry->width + x,
palette_index);
}
}
break;
case 4:
for (y = 0; y < entry->height; y++)
for (x = 0; x < entry->width; x++)
{
pixel = (guint8 *) &buffer32[y * entry->width + x];
palette_index = ico_get_palette_index(color_to_slot,
pixel[0], pixel[1], pixel[2]);
if (ico_get_bit_from_data (data->and_map, entry->width,
(entry->height-y-1) * entry->width + x))
{
ico_set_nibble_in_data (data->xor_map, entry->width,
(entry->height-y-1) * entry->width + x,
black_index);
}
else
{
ico_set_nibble_in_data (data->xor_map, entry->width,
(entry->height-y-1) * entry->width + x,
palette_index);
}
}
break;
case 8:
for (y = 0; y < entry->height; y++)
for (x = 0; x < entry->width; x++)
{
pixel = (guint8 *) &buffer32[y * entry->width + x];
palette_index = ico_get_palette_index (color_to_slot,
pixel[0],
pixel[1],
pixel[2]);
if (ico_get_bit_from_data (data->and_map, entry->width,
(entry->height-y-1) * entry->width + x))
{
ico_set_byte_in_data (data->xor_map, entry->width,
(entry->height-y-1) * entry->width + x,
black_index);
}
else
{
ico_set_byte_in_data (data->xor_map, entry->width,
(entry->height-y-1) * entry->width + x,
palette_index);
}
}
break;
default:
for (y = 0; y < entry->height; y++)
for (x = 0; x < entry->width; x++)
{
pixel = (guint8 *) &buffer32[y * entry->width + x];
((guint32 *) data->xor_map)[(entry->height-y-1) * entry->width + x] =
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",
data->and_len, data->xor_len));
if (color_to_slot)
{
g_hash_table_foreach (color_to_slot, ico_free_hash_item, NULL);
g_hash_table_destroy (color_to_slot);
}
g_free(palette);
g_free(buffer);
}
static void
ico_setup (MsIcon *ico,
gint32 image,
gint *icon_depths,
gint num_icons)
{
gint *layers;
gint i;
gint offset;
layers = gimp_image_get_layers (image, &num_icons);
/* Set up icon entries */
for (i = 0; i < num_icons; i++)
{
ico_init_direntry (&ico->icon_dir[i], layers[i], icon_depths[i]);
gimp_progress_update ((gdouble) i / (gdouble) num_icons * 0.3);
}
/* Set up data entries (the actual icons), and calculate each one's size */
for (i = 0; i < num_icons; i++)
{
ico_init_data (ico, i, layers[i], icon_depths[i]);
ico->icon_dir[i].size =
ico->icon_data[i].header_size +
ico->icon_data[i].palette_len +
ico->icon_data[i].xor_len +
ico->icon_data[i].and_len;
gimp_progress_update (0.3 + (gdouble) i / (gdouble) num_icons * 0.3);
}
/* Finally, calculate offsets for each icon and store them in each entry */
offset = 3 * sizeof (guint16) + ico->icon_count * sizeof (MsIconEntry);
for (i = 0; i < num_icons; i++)
{
ico->icon_dir[i].offset = offset;
offset += ico->icon_dir[i].size;
gimp_progress_update (0.6 + (gdouble) i / (gdouble) num_icons * 0.3);
}
gimp_progress_update (1.0);
g_free (layers);
}
static GimpPDBStatusType
ico_save (MsIcon *ico)
{
MsIconEntry *entry;
MsIconData *data;
int i;
ico->cp += ico_write_int16 (ico->fp, &ico->reserved, 3);
for (i = 0; i < ico->icon_count; i++)
{
entry = &ico->icon_dir[i];
ico->cp += ico_write_int8 (ico->fp, (guint8 *) entry, 4);
ico->cp += ico_write_int16 (ico->fp, &entry->num_planes, 2);
ico->cp += ico_write_int32 (ico->fp, &entry->size, 2);
}
for (i = 0; i < ico->icon_count; i++)
{
data = &ico->icon_data[i];
ico->cp += ico_write_int32 (ico->fp, (guint32 *) data, 3);
ico->cp += ico_write_int16 (ico->fp, &data->planes, 2);
ico->cp += ico_write_int32 (ico->fp, &data->compression, 6);
if (data->palette)
ico->cp += ico_write_int8 (ico->fp,
(guint8 *) data->palette, data->palette_len);
ico->cp += ico_write_int8 (ico->fp, data->xor_map, data->xor_len);
ico->cp += ico_write_int8 (ico->fp, data->and_map, data->and_len);
}
return GIMP_PDB_SUCCESS;
}
static gboolean
ico_layers_too_big (gint32 image)
{
gint *layers;
gint i, num_layers;
layers = gimp_image_get_layers (image, &num_layers);
for (i = 0; i < num_layers; i++)
{
if ((gimp_drawable_width (layers[i]) > 255) ||
(gimp_drawable_height (layers[i]) > 255))
{
g_free (layers);
return TRUE;
}
}
g_free (layers);
return FALSE;
}
static gint
ico_get_layer_num_colors (gint32 layer,
gboolean *uses_alpha_levels)
@ -832,7 +505,7 @@ ico_get_layer_num_colors (gint32 layer,
return num_colors;
}
static gboolean
gboolean
ico_cmap_contains_black (guchar *cmap,
gint num_colors)
{
@ -924,7 +597,20 @@ ico_image_get_reduced_buf (guint32 layer,
* We need to eliminate one more color to make room for black.
*/
gimp_image_convert_rgb (tmp_image);
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);
}
tmp = gimp_drawable_get (tmp_layer);
gimp_pixel_rgn_init (&dst_pixel_rgn,
@ -932,9 +618,12 @@ ico_image_get_reduced_buf (guint32 layer,
gimp_pixel_rgn_set_rect (&dst_pixel_rgn, buffer, 0, 0, w, h);
gimp_drawable_detach (tmp);
if (!gimp_drawable_is_rgb (layer))
gimp_image_convert_rgb (tmp_image);
gimp_image_convert_indexed (tmp_image,
GIMP_FS_DITHER, GIMP_MAKE_PALETTE,
(1 << bpp) - 1, TRUE, FALSE, "dummy");
(1<<bpp) - 1, TRUE, FALSE, "dummy");
g_free (cmap);
cmap = gimp_image_get_colormap (tmp_image, num_colors);
}
@ -963,48 +652,314 @@ ico_image_get_reduced_buf (guint32 layer,
*buf_out = buffer;
}
GimpPDBStatusType
SaveICO (const gchar *filename,
gint32 image)
static gboolean
ico_write_icon (FILE *fp,
gint32 layer,
gint32 depth)
{
MsIcon ico;
gint *icon_depths = NULL;
gint num_icons;
GimpPDBStatusType exit_state;
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;
guchar *buffer = NULL, *pixel;
guint32 *buffer32;
guchar *palette;
GHashTable *color_to_slot = NULL;
guchar *xor_map, *and_map;
guint32 *palette32 = NULL;
gint palette_len = 0;
D(("Creating data structures for icon %i ------------------------\n",
num_icon));
width = gimp_drawable_width (layer);
height = gimp_drawable_height (layer);
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,
&palette, &buffer);
buffer32 = (guint32 *) buffer;
/* Set up colormap and andmap when necessary: */
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);
and_len = and_len;
for (y = 0; y < height; y++)
for (x = 0; x < width; x++)
{
pixel = (guint8 *) &buffer32[y * width + x];
ico_set_bit_in_data (and_map, width,
(height-y-1) * width + x,
(pixel[3] > ICO_ALPHA_THRESHOLD ? 0 : 1));
}
xor_map = ico_alloc_map (width, height, header.bpp, &xor_len);
xor_len = 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++)
{
pixel = (guint8 *) &buffer32[y * width + x];
palette_index = ico_get_palette_index (color_to_slot, pixel[0],
pixel[1], pixel[2]);
if (ico_get_bit_from_data (and_map, width,
(height-y-1) * width + x))
{
ico_set_bit_in_data (xor_map, width,
(height-y-1) * width + x,
black_index);
}
else
{
ico_set_bit_in_data (xor_map, width,
(height-y-1) * width + x,
palette_index);
}
}
break;
case 4:
for (y = 0; y < height; y++)
for (x = 0; x < width; x++)
{
pixel = (guint8 *) &buffer32[y * width + x];
palette_index = ico_get_palette_index(color_to_slot, pixel[0],
pixel[1], pixel[2]);
if (ico_get_bit_from_data (and_map, width,
(height-y-1) * width + x))
{
ico_set_nibble_in_data (xor_map, width,
(height-y-1) * width + x,
black_index);
}
else
{
ico_set_nibble_in_data (xor_map, width,
(height-y-1) * width + x,
palette_index);
}
}
break;
case 8:
for (y = 0; y < height; y++)
for (x = 0; x < width; x++)
{
pixel = (guint8 *) &buffer32[y * width + x];
palette_index = ico_get_palette_index (color_to_slot,
pixel[0],
pixel[1],
pixel[2]);
if (ico_get_bit_from_data (and_map, width,
(height-y-1) * width + x))
{
ico_set_byte_in_data (xor_map, width,
(height-y-1) * width + x,
black_index);
}
else
{
ico_set_byte_in_data (xor_map, width,
(height-y-1) * width + x,
palette_index);
}
}
break;
default:
for (y = 0; y < height; y++)
for (x = 0; x < width; x++)
{
pixel = (guint8 *) &buffer32[y * width + x];
((guint32 *) xor_map)[(height-y-1) * width + x] =
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)
{
g_hash_table_foreach (color_to_slot, ico_free_hash_item, NULL);
g_hash_table_destroy (color_to_slot);
}
g_free(palette);
g_free(buffer);
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);
ico_write_int8 (fp, xor_map, xor_len);
ico_write_int8 (fp, and_map, and_len);
if (palette32)
g_free (palette32);
g_free (xor_map);
g_free (and_map);
return TRUE;
}
static void
ico_save_info_free (IcoSaveInfo *info)
{
g_free (info->depths);
g_free (info->default_depths);
g_free (info->layers);
memset (info, 0, sizeof (IcoSaveInfo));
}
GimpPDBStatusType
ico_save_image (const gchar *filename,
gint32 image,
gint32 run_mode)
{
FILE *fp;
gint i;
gint width, height;
IcoSaveInfo info;
IcoFileHeader header;
IcoFileEntry *entries;
gboolean saved;
D(("*** Saving Microsoft icon file %s\n", filename));
if (ico_layers_too_big (image))
{
g_message (_("Windows icons cannot be higher or wider than 255 pixels."));
return GIMP_PDB_EXECUTION_ERROR;
}
ico_save_init (image, &info);
/* First, set up the icon specs dialog and show it: */
if ((icon_depths = ico_show_icon_dialog (image, &num_icons)) == NULL)
return GIMP_PDB_CANCEL;
if (run_mode == GIMP_RUN_INTERACTIVE)
{
/* Allow user to override default values */
if ( !ico_save_dialog (image, &info))
return GIMP_PDB_CANCEL;
}
gimp_progress_init_printf (_("Saving '%s'"),
gimp_filename_to_utf8 (filename));
/* Okay, let's actually save the thing with the depths the
user specified. */
if (! (fp = g_fopen (filename, "wb")))
{
g_message (_("Could not open '%s' for writing: %s"),
gimp_filename_to_utf8 (filename), g_strerror (errno));
return GIMP_PDB_EXECUTION_ERROR;
}
if ((exit_state = ico_init (filename, &ico, num_icons) != GIMP_PDB_SUCCESS))
return exit_state;
header.reserved = 0;
header.resource_type = 1;
header.icon_count = info.num_icons;
if ( !ico_write_int16 (fp, &header.reserved, 1)
|| !ico_write_int16 (fp, &header.resource_type, 1)
|| !ico_write_int16 (fp, &header.icon_count, 1) )
{
ico_save_info_free (&info);
fclose (fp);
return GIMP_PDB_EXECUTION_ERROR;
}
D(("icon initialized ...\n"));
entries = g_new0 (IcoFileEntry, info.num_icons);
if (fwrite (entries, sizeof (IcoFileEntry), info.num_icons, fp) <= 0)
{
ico_save_info_free (&info);
fclose (fp);
return GIMP_PDB_EXECUTION_ERROR;
}
ico_setup (&ico, image, icon_depths, num_icons);
for (i = 0; i < info.num_icons; i++)
{
gimp_progress_update ((gdouble)i / (gdouble)info.num_icons);
D(("icon data created ...\n"));
width = gimp_drawable_width (info.layers[i]);
height = gimp_drawable_height (info.layers[i]);
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);
saved = ico_write_icon (fp, info.layers[i], info.depths[i]);
if (!saved)
{
ico_save_info_free (&info);
fclose (fp);
return GIMP_PDB_EXECUTION_ERROR;
}
entries[i].size = ftell (fp) - entries[i].offset;
}
exit_state = ico_save(&ico);
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;
}
D(("*** icon saved, exit status %i.\n\n", exit_state));
gimp_progress_update (1.0);
ico_cleanup (&ico);
g_free (icon_depths);
return exit_state;
ico_save_info_free (&info);
fclose (fp);
return GIMP_PDB_SUCCESS;
}

View File

@ -23,8 +23,12 @@
#define __ICO_SAVE_H__
GimpPDBStatusType SaveICO (const gchar *file_name,
gint32 image_ID);
GimpPDBStatusType ico_save_image (const gchar *file_name,
gint32 image_ID,
gint32 run_mode);
gboolean ico_cmap_contains_black (guchar *cmap,
gint num_colors);
#endif /* __ICO_SAVE_H__ */

View File

@ -259,7 +259,7 @@ run (const gchar *name,
if (status == GIMP_PDB_SUCCESS)
{
status = SaveICO (file_name, image_ID);
status = ico_save_image (file_name, image_ID, run_mode);
}
if (export == GIMP_EXPORT_EXPORT)
@ -275,6 +275,49 @@ run (const gchar *name,
}
gint
ico_rowstride (gint width,
gint bpp)
{
switch (bpp)
{
case 1:
if ((width % 32) == 0)
return width / 8;
else
return 4 * (width/32 + 1);
break;
case 4:
if ((width % 8) == 0)
return width / 2;
else
return 4 * (width/8 + 1);
break;
case 8:
if ((width % 4) == 0)
return width;
else
return 4 * (width/4 + 1);
break;
case 24:
if (((width*3) % 4) == 0)
return width * 3;
else
return 4 * (width*3/4+1);
case 32:
return width * 4;
default:
g_warning ("invalid bitrate: %d\n", bpp);
g_assert_not_reached ();
return width * (bpp/8);
}
}
guint8 *
ico_alloc_map (gint width,
gint height,
@ -284,32 +327,7 @@ ico_alloc_map (gint width,
gint len = 0;
guint8 *map = NULL;
switch (bpp)
{
case 1:
if ((width % 32) == 0)
len = (width * height / 8);
else
len = 4 * ((width/32 + 1) * height);
break;
case 4:
if ((width % 8) == 0)
len = (width * height / 2);
else
len = 4 * ((width/8 + 1) * height);
break;
case 8:
if ((width % 4) == 0)
len = width * height;
else
len = 4 * ((width/4 + 1) * height);
break;
default:
len = width * height * (bpp/8);
}
len = ico_rowstride (width, bpp) * height;
*length = len;
map = g_new0 (guint8, len);
@ -317,28 +335,3 @@ ico_alloc_map (gint width,
return map;
}
void
ico_cleanup (MsIcon *ico)
{
gint i;
if (!ico)
return;
if (ico->fp)
fclose (ico->fp);
if (ico->icon_dir)
g_free (ico->icon_dir);
if (ico->icon_data)
{
for (i = 0; i < ico->icon_count; i++)
{
g_free (ico->icon_data[i].palette);
g_free (ico->icon_data[i].xor_map);
g_free (ico->icon_data[i].and_map);
}
g_free (ico->icon_data);
}
}

View File

@ -33,71 +33,74 @@
#define D(x)
#endif
#define ICO_ALPHA_THRESHOLD 127
#define ICO_MAXBUF 4096
#define MAXLEN 4096
typedef struct _MsIconEntry
typedef struct _IcoFileHeader
{
guint8 width; /* Width of icon in pixels */
guint8 height; /* Height of icon in pixels */
guint8 num_colors; /* Maximum number of colors */
guint8 reserved; /* Not used */
guint16 num_planes; /* Not used */
guint16 bpp;
guint32 size; /* Length of icon bitmap in bytes */
guint32 offset; /* Offset position of icon bitmap in file */
} MsIconEntry;
guint16 reserved;
guint16 resource_type;
guint16 icon_count;
} IcoFileHeader;
typedef struct _MsIconData
typedef struct _IcoFileEntry
{
/* Bitmap header data */
guint32 header_size; /* = 40 Bytes */
guint32 width;
guint32 height;
guint16 planes;
guint8 width; /* Width of icon in pixels */
guint8 height; /* Height of icon in pixels */
guint8 num_colors; /* Number of colors of paletted image */
guint8 reserved; /* Must be 0 */
guint16 planes; /* Must be 1 */
guint16 bpp; /* 1, 4, 8, 24 or 32 bits per pixel */
guint32 size; /* Size of icon (including data header) */
guint32 offset; /* Absolute offset of data in a file */
} IcoFileEntry;
typedef struct _IcoFileDataHeader
{
guint32 header_size; /* 40 bytes */
guint32 width; /* Width of image in pixels */
guint32 height; /* Height of image in pixels */
guint16 planes; /* Must be 1 */
guint16 bpp;
guint32 compression; /* not used for icons */
guint32 image_size; /* size of image */
guint32 compression; /* Not used for icons */
guint32 image_size; /* Size of image (without this header) */
guint32 x_res;
guint32 y_res;
guint32 used_clrs;
guint32 important_clrs;
} IcoFileDataHeader;
guint32 *palette; /* Color palette, only if bpp <= 8. */
guint8 *xor_map; /* Icon bitmap */
guint8 *and_map; /* Display bit mask */
/* Only used when saving: */
gint palette_len;
gint xor_len;
gint and_len;
} MsIconData;
typedef struct _MsIcon
typedef struct _IcoLoadInfo
{
FILE *fp;
guint cp;
const gchar *filename;
guint width;
guint height;
gint bpp;
gint offset;
gint size;
} IcoLoadInfo;
guint16 reserved;
guint16 resource_type;
guint16 icon_count;
MsIconEntry *icon_dir;
MsIconData *icon_data;
} MsIcon;
typedef struct _IcoSaveInfo
{
gint *depths;
gint *default_depths;
gint *layers;
gint num_icons;
} IcoSaveInfo;
/* Miscellaneous helper functions below: */
gint ico_rowstride (gint width,
gint bpp);
/* Allocates a 32-bit padded bitmap for various color depths.
Returns the allocated array directly, and the length of the
array in the len pointer */
guint8 * ico_alloc_map (gint width,
gint height,
gint bpp,
gint *len);
void ico_cleanup (MsIcon *ico);
gint height,
gint bpp,
gint *len);
#endif /* __MAIN_H__ */