diff --git a/ChangeLog b/ChangeLog index 0505492f7b..37dcfde62c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2004-04-15 Sven Neumann + + * configure.in + * plug-ins/Makefile.am + * plug-ins/winicon/Makefile.am + * plug-ins/winicon/icodialog.[ch] + * plug-ins/winicon/icoload.[ch] + * plug-ins/winicon/icosave.[ch] + * plug-ins/winicon/main.[ch]: added plug-in to load and save + Windows icon files. Plug-in written by Christian Kreibich, port to + GIMP-2.0 API by Gregor Riepl, massive code cleanup by me. Fixes + bug #139160. + 2004-04-15 Michael Natterer * app/widgets/gimpdnd.c (gimp_dnd_data_source_add) diff --git a/configure.in b/configure.in index bdc7d72053..8d4f52ef4e 100644 --- a/configure.in +++ b/configure.in @@ -1551,6 +1551,7 @@ plug-ins/rcm/Makefile plug-ins/sel2path/Makefile plug-ins/sgi/Makefile plug-ins/twain/Makefile +plug-ins/winicon/Makefile plug-ins/winsnap/Makefile modules/Makefile devel-docs/Makefile diff --git a/plug-ins/Makefile.am b/plug-ins/Makefile.am index e65154c1e3..ae4556c990 100644 --- a/plug-ins/Makefile.am +++ b/plug-ins/Makefile.am @@ -53,6 +53,7 @@ SUBDIRS = \ rcm \ sgi \ sel2path \ + winicon \ $(win32dirs) \ $(xjt) \ common diff --git a/plug-ins/winicon/.cvsignore b/plug-ins/winicon/.cvsignore new file mode 100644 index 0000000000..946b86c7f7 --- /dev/null +++ b/plug-ins/winicon/.cvsignore @@ -0,0 +1,6 @@ +Makefile.in +Makefile +.deps +_libs +.libs +winicon diff --git a/plug-ins/winicon/Makefile.am b/plug-ins/winicon/Makefile.am new file mode 100644 index 0000000000..b254556ea9 --- /dev/null +++ b/plug-ins/winicon/Makefile.am @@ -0,0 +1,44 @@ +## Process this file with automake to produce Makefile.in + +libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la +libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la +libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la +libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la +libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la +libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la + +if OS_WIN32 +mwindows = -mwindows +endif + +AM_LDFLAGS = $(mwindows) + +libexecdir = $(gimpplugindir)/plug-ins + +libexec_PROGRAMS = winicon + +winicon_SOURCES = \ + icodialog.c \ + icodialog.h \ + icoload.c \ + icoload.h \ + icosave.c \ + icosave.h \ + main.c \ + main.h + +INCLUDES = \ + -I$(top_srcdir) \ + $(GTK_CFLAGS) \ + -I$(includedir) + +LDADD = \ + $(libgimpui) \ + $(libgimpwidgets) \ + $(libgimp) \ + $(libgimpcolor) \ + $(libgimpmath) \ + $(libgimpbase) \ + $(GTK_LIBS) \ + $(RT_LIBS) \ + $(INTLLIBS) diff --git a/plug-ins/winicon/icodialog.c b/plug-ins/winicon/icodialog.c new file mode 100644 index 0000000000..45778cc00e --- /dev/null +++ b/plug-ins/winicon/icodialog.c @@ -0,0 +1,461 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * GIMP Plug-in for Windows Icon files. + * Copyright (C) 2002 Christian Kreibich . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (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 + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include + +#include + +#undef GTK_DISABLE_DEPRECATED +#include +#define GTK_DISABLE_DEPRECATED + +#include +#include + +#define ICO_DBG + +#include "icodialog.h" +#include "main.h" + +#include "libgimp/stdplugins-intl.h" + + +static GtkWidget *ico_preview_new (gint32 layer); +static void ico_fill_preview_with_thumb (GtkWidget *widget, + gint32 drawable_ID); +static void on_1bpp_menu_selected (GtkWidget *item, + GtkWidget *icon_hbox); +static void on_4bpp_menu_selected (GtkWidget *item, + GtkWidget *icon_hbox); +static void on_8bpp_menu_selected (GtkWidget *item, + GtkWidget *icon_hbox); +static void on_32bpp_menu_selected (GtkWidget *item, + GtkWidget *icon_hbox); + + +static GtkWidget * +ico_preview_new(gint32 layer) +{ + GtkWidget *icon_preview; + + icon_preview = gtk_preview_new (GTK_PREVIEW_COLOR); + ico_fill_preview_with_thumb (icon_preview, layer); + + return icon_preview; +} + + +static void +ico_fill_preview_with_thumb (GtkWidget *widget, + gint32 drawable_ID) +{ + guchar *drawable_data; + gint bpp; + gint x,y; + gint width; + gint height; + guchar *src; + gdouble r, g, b, a; + gdouble c0, c1; + guchar *p0, *p1; + guchar *even, *odd; + + width = gimp_drawable_width (drawable_ID); + height = gimp_drawable_height (drawable_ID); + bpp = 0; /* Only returned */ + + if (width > 128) + width = 128; + if (height > 128) + height = 128; + + drawable_data = + gimp_drawable_get_thumbnail_data (drawable_ID, &width, &height, &bpp); + + if (width < 1 || height < 1) + return; + + gtk_preview_size (GTK_PREVIEW (widget), width, height); + + even = g_malloc (width * 3); + odd = g_malloc (width * 3); + src = drawable_data; + + for (y = 0; y < height; y++) + { + p0 = even; + p1 = odd; + + for (x = 0; x < width; x++) + { + if (bpp == 4) + { + r = ((gdouble) src[x*4 + 0]) / 255.0; + g = ((gdouble) src[x*4 + 1]) / 255.0; + b = ((gdouble) src[x*4 + 2]) / 255.0; + a = ((gdouble) src[x*4 + 3]) / 255.0; + } + else if (bpp == 3) + { + r = ((gdouble) src[x*3 + 0]) / 255.0; + g = ((gdouble) src[x*3 + 1]) / 255.0; + b = ((gdouble) src[x*3 + 2]) / 255.0; + a = 1.0; + } + else + { + r = ((gdouble) src[x*bpp + 0]) / 255.0; + g = b = r; + if (bpp == 2) + a = ((gdouble) src[x*2 + 1]) / 255.0; + else + a = 1.0; + } + + if ((x / GIMP_CHECK_SIZE_SM) & 1) + { + c0 = GIMP_CHECK_LIGHT; + c1 = GIMP_CHECK_DARK; + } + else + { + c0 = GIMP_CHECK_DARK; + c1 = GIMP_CHECK_LIGHT; + } + + *p0++ = (c0 + (r - c0) * a) * 255.0; + *p0++ = (c0 + (g - c0) * a) * 255.0; + *p0++ = (c0 + (b - c0) * a) * 255.0; + + *p1++ = (c1 + (r - c1) * a) * 255.0; + *p1++ = (c1 + (g - c1) * a) * 255.0; + *p1++ = (c1 + (b - c1) * a) * 255.0; + + } + + if ((y / GIMP_CHECK_SIZE_SM) & 1) + gtk_preview_draw_row (GTK_PREVIEW (widget), odd, 0, y, width); + else + gtk_preview_draw_row (GTK_PREVIEW (widget), even, 0, y, width); + + src += width * bpp; + } + + g_free (even); + g_free (odd); + g_free (drawable_data); +} + + +/* 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) +{ + GtkWidget *hbox; + GtkWidget *optionmenu; + GtkWidget *optionmenu_menu; + GtkWidget *menuitem; + + hbox = gtk_hbox_new (FALSE, 0); + + /* To make life easier for the callbacks, we store the + layer's ID and stacking number with the hbox. */ + + g_object_set_data (G_OBJECT (hbox), + "icon_layer", GINT_TO_POINTER (layer)); + g_object_set_data (G_OBJECT (hbox), + "icon_layer_num", GINT_TO_POINTER (layer_num)); + + g_object_set_data (G_OBJECT (hbox), "icon_preview", icon_preview); + gtk_widget_show (icon_preview); + gtk_box_pack_start (GTK_BOX (hbox), icon_preview, TRUE, TRUE, 0); + + optionmenu = gtk_option_menu_new (); + g_object_set_data (G_OBJECT (hbox), "icon_optionmenu", optionmenu); + gtk_widget_show (optionmenu); + gtk_box_pack_start (GTK_BOX (hbox), optionmenu, FALSE, FALSE, 0); + optionmenu_menu = gtk_menu_new (); + + menuitem = + gtk_menu_item_new_with_label (_("1 bpp, 1-bit alpha, 2-slot palette")); + g_signal_connect (menuitem, "activate", + G_CALLBACK (on_1bpp_menu_selected), + hbox); + gtk_widget_show (menuitem); + gtk_menu_shell_append (GTK_MENU_SHELL (optionmenu_menu), menuitem); + + menuitem = + gtk_menu_item_new_with_label (_("4 bpp, 1-bit alpha, 16-slot palette")); + g_signal_connect (menuitem, "activate", + G_CALLBACK (on_4bpp_menu_selected), + hbox); + gtk_widget_show (menuitem); + gtk_menu_shell_append (GTK_MENU_SHELL (optionmenu_menu), menuitem); + + menuitem = + gtk_menu_item_new_with_label (_("8 bpp, 1-bit alpha, 256-slot palette")); + g_signal_connect (menuitem, "activate", + G_CALLBACK (on_8bpp_menu_selected), + hbox); + gtk_widget_show (menuitem); + gtk_menu_shell_append (GTK_MENU_SHELL (optionmenu_menu), menuitem); + + menuitem = + gtk_menu_item_new_with_label (_("32 bpp, 8-bit alpha, no palette")); + g_signal_connect (menuitem, "activate", + G_CALLBACK (on_32bpp_menu_selected), + hbox); + gtk_widget_show (menuitem); + gtk_menu_shell_append (GTK_MENU_SHELL (optionmenu_menu), menuitem); + + gtk_option_menu_set_menu (GTK_OPTION_MENU (optionmenu), + optionmenu_menu); + gtk_option_menu_set_history (GTK_OPTION_MENU (optionmenu), 3); + + return hbox; +} + + +GtkWidget* +ico_specs_dialog_new (gint num_layers) +{ + GtkWidget *dialog; + GtkWidget *vbox; + GtkWidget *frame; + GtkWidget *scrolledwindow; + GtkWidget *viewport; + gint *icon_depths, i; + + dialog = gimp_dialog_new (_("GIMP Windows Icon Plugin"), "winicon", + NULL, 0, + gimp_standard_help_func, "plug-in-winicon", + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OK, GTK_RESPONSE_OK, + NULL); + + /* 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); + + vbox = GTK_DIALOG (dialog)->vbox; + + frame = gtk_frame_new (_("Icon details")); + gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (frame), 5); + gtk_widget_show (frame); + + scrolledwindow = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_container_set_border_width (GTK_CONTAINER (scrolledwindow), 5); + gtk_container_add (GTK_CONTAINER (frame), scrolledwindow); + gtk_widget_show (scrolledwindow); + + viewport = gtk_viewport_new (NULL, NULL); + gtk_container_add (GTK_CONTAINER (scrolledwindow), viewport); + gtk_widget_show (viewport); + + vbox = gtk_vbox_new (TRUE, 0); + g_object_set_data (G_OBJECT (dialog), "icons_vbox", vbox); + gtk_container_add (GTK_CONTAINER (viewport), vbox); + gtk_widget_show (vbox); + + return dialog; +} + +static GtkWidget * +ico_specs_dialog_get_layer_preview (GtkWidget *dialog, + gint32 layer) +{ + GtkWidget *preview; + GtkWidget *icon_hbox; + gchar key[MAXLEN]; + + g_snprintf (key, MAXLEN, "layer_%i_hbox", layer); + icon_hbox = g_object_get_data (G_OBJECT (dialog), key); + + if (!icon_hbox) + { + D(("Something's wrong -- couldn't look up hbox by layer ID\n")); + return NULL; + } + + preview = g_object_get_data (G_OBJECT (icon_hbox), "icon_preview"); + + if (!icon_hbox) + { + D(("Something's wrong -- couldn't look up preview from hbox\n")); + return NULL; + } + + return preview; +} + + +void +ico_specs_dialog_add_icon (GtkWidget *dialog, + gint32 layer, + gint layer_num) +{ + 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, TRUE, FALSE, 2); + gtk_widget_show (hbox); + + /* Let's make the hbox accessible through the layer ID */ + g_snprintf (key, MAXLEN, "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; + GimpPixelRgn src_pixel_rgn, dst_pixel_rgn; + gint32 tmp_image; + gint32 tmp_layer; + gint w, h; + guchar *buffer; + gboolean result; + + tmp_image = gimp_image_new (gimp_drawable_width (layer), + gimp_drawable_height (layer), + GIMP_RGB); + + w = gimp_drawable_width (layer); + h = gimp_drawable_height (layer); + + tmp_layer = gimp_layer_new (tmp_image, "temporary", w, h, + GIMP_RGBA_IMAGE, 100, GIMP_NORMAL_MODE); + gimp_image_add_layer (tmp_image, tmp_layer, 0); + + gimp_pixel_rgn_init (&src_pixel_rgn, gimp_drawable_get (layer), + 0, 0, w, h, TRUE, FALSE); + gimp_pixel_rgn_init (&dst_pixel_rgn, gimp_drawable_get (tmp_layer), + 0, 0, w, h, TRUE, FALSE); + + 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); + + if (bpp < 32) + { + result = gimp_image_convert_indexed (tmp_image, + GIMP_FS_DITHER, + GIMP_MAKE_PALETTE, + 1 << bpp, + TRUE, + FALSE, + "dummy"); + } + + preview = ico_specs_dialog_get_layer_preview (dialog, layer); + ico_fill_preview_with_thumb (preview, tmp_layer); + gtk_widget_queue_draw (preview); + + gimp_image_delete (tmp_image); + g_free (buffer); +} + + +/* meta-callback for color depth changes in an icon */ +static void +on_bpp_change (GtkWidget *icon_hbox, + gint bpp) +{ + GtkWidget *dialog; + gint32 layer; + gint layer_num; + gint *icon_depths; + + dialog = gtk_widget_get_toplevel (icon_hbox); + + layer = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (icon_hbox), + "icon_layer")); + layer_num = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (icon_hbox), + "icon_layer_num")); + 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; + } + + /* 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); +} + +static void +on_1bpp_menu_selected (GtkWidget *item, + GtkWidget *icon_hbox) +{ + on_bpp_change (icon_hbox, 1); + item = NULL; +} + +static void +on_4bpp_menu_selected (GtkWidget *item, + GtkWidget *icon_hbox) +{ + on_bpp_change (icon_hbox, 4); + item = NULL; +} + +static void +on_8bpp_menu_selected (GtkWidget *item, + GtkWidget *icon_hbox) +{ + on_bpp_change (icon_hbox, 8); + item = NULL; +} + +static void +on_32bpp_menu_selected (GtkWidget *item, + GtkWidget *icon_hbox) +{ + on_bpp_change (icon_hbox, 32); + item = NULL; +} diff --git a/plug-ins/winicon/icodialog.h b/plug-ins/winicon/icodialog.h new file mode 100644 index 0000000000..0c898c2251 --- /dev/null +++ b/plug-ins/winicon/icodialog.h @@ -0,0 +1,35 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * GIMP Plug-in for Windows Icon files. + * Copyright (C) 2002 Christian Kreibich . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (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 + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __ICO_DIALOG_H__ +#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); + + +#endif diff --git a/plug-ins/winicon/icoload.c b/plug-ins/winicon/icoload.c new file mode 100644 index 0000000000..4279b6a83a --- /dev/null +++ b/plug-ins/winicon/icoload.c @@ -0,0 +1,537 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * GIMP Plug-in for Windows Icon files. + * Copyright (C) 2002 Christian Kreibich . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (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 + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include +#include + +#include + +#include + +/* #define ICO_DBG */ + +#include "main.h" + +#include "libgimp/stdplugins-intl.h" + + +#if (G_BYTE_ORDER == G_BIG_ENDIAN) + +#define A_VAL(p) ((guchar *)(p))[0] +#define R_VAL(p) ((guchar *)(p))[1] +#define G_VAL(p) ((guchar *)(p))[2] +#define B_VAL(p) ((guchar *)(p))[3] + +#elif (G_BYTE_ORDER == G_LITTLE_ENDIAN) + +#define A_VAL(p) ((guchar *)(p))[3] +#define R_VAL(p) ((guchar *)(p))[2] +#define G_VAL(p) ((guchar *)(p))[1] +#define B_VAL(p) ((guchar *)(p))[0] + +#endif + + +static gint ico_read_int8 (FILE *fp, guint8 *data, gint count); +static gint ico_read_int16 (FILE *fp, guint16 *data, gint count); +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 void ico_load (MsIcon *ico); +static gint32 ico_to_gimp (MsIcon *ico); + + +static gint +ico_read_int32 (FILE *fp, + guint32 *data, + gint count) +{ + gint i, total; + + total = count; + if (count > 0) + { + ico_read_int8 (fp, (guint8*) data, count * 4); + for (i = 0; i < count; i++) + data[i] = GUINT32_FROM_LE (data[i]); + } + + return total * 4; +} + + +static gint +ico_read_int16 (FILE *fp, + guint16 *data, + gint count) +{ + gint i, total; + + total = count; + if (count > 0) + { + ico_read_int8 (fp, (guint8*) data, count * 2); + for (i = 0; i < count; i++) + data[i] = GUINT16_FROM_LE (data[i]); + } + + return total * 2; +} + + +static gint +ico_read_int8 (FILE *fp, + guint8 *data, + gint count) +{ + gint total; + gint bytes; + + total = count; + while (count > 0) + { + bytes = fread ((gchar *) data, sizeof (gchar), count, fp); + if (bytes <= 0) /* something bad happened */ + break; + count -= bytes; + data += bytes; + } + + return total; +} + + +static gboolean +ico_init (const gchar *filename, + MsIcon *ico) +{ + memset (ico, 0, sizeof (MsIcon)); + + if (! (ico->fp = fopen (filename, "r"))) + { + g_message (_("Could not open '%s' for reading: %s"), + gimp_filename_to_utf8 (filename), g_strerror (errno)); + return FALSE; + } + + 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; +} + + +static void +ico_read_entry (MsIcon *ico, + MsIconEntry *entry) +{ + if (!ico || !entry) + return; + + 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); + + 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)); +} + + +static void +ico_read_data (MsIcon *ico, + gint icon_num) +{ + MsIconData *data; + MsIconEntry *entry; + gint length; + + if (!ico) + return; + + 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) + { + 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_int32 (ico->fp, data->palette, data->used_clrs); + } + + 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)); + + /* 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)); +} + + +static void +ico_load (MsIcon *ico) +{ + gint i; + + if (!ico) + return; + + 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]); + + for (i = 0; i < ico->icon_count; i++) + ico_read_data(ico, i); +} + + +gint +ico_get_bit_from_data (guint8 *data, + gint line_width, + gint bit) +{ + gint line; + gint width32; + gint offset; + gint result; + + /* width per line in multiples of 32 bits */ + width32 = (line_width % 32 == 0 ? line_width/32 : line_width/32 + 1); + line = bit / line_width; + offset = bit % line_width; + + result = (data[line * width32 * 4 + offset/8] & (1 << (7 - (bit % 8)))); + + return (result ? 1 : 0); +} + + +gint +ico_get_nibble_from_data (guint8 *data, + gint line_width, + gint nibble) +{ + gint line; + gint width32; + gint offset; + gint result; + + /* width per line in multiples of 32 bits */ + width32 = (line_width % 8 == 0 ? line_width/8 : line_width/8 + 1); + line = nibble / line_width; + offset = nibble % line_width; + + result = (data[line * width32 * 4 + offset/2] & (0x0F << (4 * (1 - nibble % 2)))); + + if (nibble % 2 == 0) + result = result >> 4; + + return result; +} + + +gint +ico_get_byte_from_data (guint8 *data, + gint line_width, + gint byte) +{ + gint line; + gint width32; + gint offset; + + /* width per line in multiples of 32 bits */ + width32 = (line_width % 4 == 0 ? line_width/4 : line_width/4 + 1); + line = byte / line_width; + offset = byte % line_width; + + return data[line * width32 * 4 + offset]; +} + + +static gint32 +ico_to_gimp (MsIcon *ico) +{ + gint icon_nr, x, y, w, h, max_w, max_h; + guint8 *xor_map; + guint8 *and_map; + guint32 *palette; + MsIconData *idata; + MsIconEntry *ientry; + gint32 image, layer, first_layer = -1; + const char *layer_name = N_("Icon #%i"); + char s[MAXLEN]; + guint32 *dest_vec; + GimpDrawable *drawable; + + /* Do a quick scan of the icons in the file to find the + largest icon contained ... */ + + max_w = 0; max_h = 0; + + for (icon_nr = 0; icon_nr < ico->icon_count; icon_nr++) + { + if (ico->icon_dir[icon_nr].width > max_w) + max_w = ico->icon_dir[icon_nr].width; + if (ico->icon_dir[icon_nr].height > max_h) + max_h = ico->icon_dir[icon_nr].height; + } + + /* Allocate the Gimp image */ + + image = gimp_image_new (max_w, max_h, GIMP_RGB); + gimp_image_set_filename (image, ico->filename); + + /* Scan icons again and set up a layer for each icon */ + + for (icon_nr = 0; icon_nr < ico->icon_count; icon_nr++) + { + GimpPixelRgn pixel_rgn; + + if (interactive_ico) + gimp_progress_update ((gdouble) icon_nr / (gdouble) ico->icon_count); + + w = ico->icon_dir[icon_nr].width; + h = ico->icon_dir[icon_nr].height; + palette = ico->icon_data[icon_nr].palette; + xor_map = ico->icon_data[icon_nr].xor_map; + and_map = ico->icon_data[icon_nr].and_map; + idata = &ico->icon_data[icon_nr]; + ientry = &ico->icon_dir[icon_nr]; + + g_snprintf (s, MAXLEN, gettext (layer_name), icon_nr + 1); + + layer = gimp_layer_new (image, s, w, h, + GIMP_RGBA_IMAGE, 100, GIMP_NORMAL_MODE); + + if (first_layer == -1) + first_layer = layer; + + gimp_image_add_layer (image, layer, icon_nr); + drawable = gimp_drawable_get (layer); + + dest_vec = g_malloc (w * h * 4); + + switch (idata->bpp) + { + case 1: + for (y = 0; y < h; y++) + for (x = 0; x < w; x++) + { + guint32 color = palette[ico_get_bit_from_data (xor_map, + w, y * w + x)]; + guint32 *dest = &(dest_vec[(h-1-y) * w + x]); + + B_VAL (dest) = R_VAL (&color); + G_VAL (dest) = G_VAL (&color); + R_VAL (dest) = B_VAL (&color); + + if (ico_get_bit_from_data (and_map, w, y*w + x)) + A_VAL (dest) = 0; + else + A_VAL (dest) = 255; + } + break; + + case 4: + for (y = 0; y < h; y++) + for (x = 0; x < w; x++) + { + guint32 color = palette[ico_get_nibble_from_data (xor_map, + w, y * w + x)]; + guint32 *dest = &(dest_vec[(h-1-y) * w + x]); + + B_VAL (dest) = R_VAL (&color); + G_VAL (dest) = G_VAL (&color); + R_VAL (dest) = B_VAL (&color); + + if (ico_get_bit_from_data (and_map, w, y*w + x)) + A_VAL (dest) = 0; + else + A_VAL (dest) = 255; + } + break; + + case 8: + for (y = 0; y < h; y++) + for (x = 0; x < w; x++) + { + guint32 color = palette[ico_get_byte_from_data (xor_map, + w, y * w + x)]; + guint32 *dest = &(dest_vec[(h-1-y) * w + x]); + + B_VAL (dest) = R_VAL (&color); + G_VAL (dest) = G_VAL (&color); + R_VAL (dest) = B_VAL (&color); + + if (ico_get_bit_from_data (and_map, w, y*w + x)) + A_VAL (dest) = 0; + else + A_VAL (dest) = 255; + } + break; + + default: + { + int bytespp = idata->bpp/8; + + for (y = 0; y < h; y++) + for (x = 0; x < w; x++) + { + guint32 *dest = &(dest_vec[(h-1-y) * w + x]); + + R_VAL(dest) = xor_map[(y * w + x) * bytespp]; + G_VAL(dest) = xor_map[(y * w + x) * bytespp + 1]; + B_VAL(dest) = xor_map[(y * w + x) * bytespp + 2]; + + if (idata->bpp < 32) + { + if (ico_get_bit_from_data (and_map, w, y*w + x)) + A_VAL (dest) = 0; + else + A_VAL (dest) = 255; + } + else + { + A_VAL (dest) = xor_map[(y * w + x) * bytespp + 3]; + } + } + } + } + + gimp_pixel_rgn_init (&pixel_rgn, drawable, + 0, 0, drawable->width, drawable->height, + TRUE, FALSE); + gimp_pixel_rgn_set_rect (&pixel_rgn, (guchar*) dest_vec, + 0, 0, drawable->width, drawable->height); + + g_free(dest_vec); + gimp_drawable_flush (drawable); + gimp_drawable_detach (drawable); + } + + gimp_image_set_active_layer (image, first_layer); + + if (interactive_ico) + gimp_progress_update (1.0); + + return image; +} + + +gint32 +LoadICO (const gchar *filename) +{ + gint32 image_ID; + MsIcon ico; + + if (interactive_ico) + { + guchar *temp = g_strdup_printf (_("Loading %s:"), + gimp_filename_to_utf8 (filename)); + gimp_progress_init (temp); + g_free (temp); + } + + if (! ico_init (filename, &ico)) + return -1; + + ico_load (&ico); + + if ( (image_ID = ico_to_gimp (&ico)) < 0) + { + ico_cleanup (&ico); + return -1; + } + + ico_cleanup (&ico); + + D(("*** icon successfully loaded.\n\n")); + + return image_ID; +} diff --git a/plug-ins/winicon/icoload.h b/plug-ins/winicon/icoload.h new file mode 100644 index 0000000000..ddff2bfa4c --- /dev/null +++ b/plug-ins/winicon/icoload.h @@ -0,0 +1,33 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * GIMP Plug-in for Windows Icon files. + * Copyright (C) 2002 Christian Kreibich . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (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 + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __ICO_LOAD_H__ +#define __ICO_LOAD_H__ + + +gint ico_get_bit_from_data (guint8 *data, gint line_width, gint bit); +gint ico_get_nibble_from_data (guint8 *data, gint line_width, gint nibble); +gint ico_get_byte_from_data (guint8 *data, gint line_width, gint byte); + +gint32 LoadICO (const gchar *file_name); + + +#endif diff --git a/plug-ins/winicon/icosave.c b/plug-ins/winicon/icosave.c new file mode 100644 index 0000000000..b6fcee99e2 --- /dev/null +++ b/plug-ins/winicon/icosave.c @@ -0,0 +1,798 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * GIMP Plug-in for Windows Icon files. + * Copyright (C) 2002 Christian Kreibich . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (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 + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include +#include + +#include + +#include +#include + +/* #define ICO_DBG */ + +#include "main.h" +#include "icoload.h" +#include "icodialog.h" + +#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); + +/* 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 gint +ico_write_int32 (FILE *fp, + guint32 *data, + gint count) +{ + gint i, total; + + total = count; + if (count > 0) + { + for (i = 0; i < count; i++) + data[i] = GUINT32_FROM_LE (data[i]); + + ico_write_int8 (fp, (guint8*) data, count * 4); + } + + return total * 4; +} + + +static gint +ico_write_int16 (FILE *fp, + guint16 *data, + gint count) +{ + gint i, total; + + total = count; + if (count > 0) + { + for (i = 0; i < count; i++) + data[i] = GUINT16_FROM_LE (data[i]); + + ico_write_int8 (fp, (guint8*) data, count * 2); + } + + return total * 2; +} + + +static gint +ico_write_int8 (FILE *fp, + guint8 *data, + gint count) +{ + 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; +} + + +static gint* +ico_show_icon_dialog (gint32 image_ID, + gint *num_icons) +{ + GtkWidget *dialog, *hbox; + GtkWidget *icon_optionmenu; + gint *layers, *icon_depths = NULL; + gint num_layers, 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), + 350, + (num_layers > 5 ? 500 : num_layers * 100)); + + icon_depths = g_object_get_data (G_OBJECT (dialog), "icon_depths"); + + /* 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++) + { + 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_optionmenu = g_object_get_data (G_OBJECT(hbox), "icon_optionmenu"); + + 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_option_menu_set_history (GTK_OPTION_MENU (icon_optionmenu), 0); + } + 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_option_menu_set_history (GTK_OPTION_MENU (icon_optionmenu), 1); + } + 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_option_menu_set_history (GTK_OPTION_MENU (icon_optionmenu), 2); + } + } + + /* Otherwise, or if real alpha levels are used, stick with 32bpp */ + } + + g_free (layers); + + gtk_widget_show (dialog); + + if (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK) + *num_icons = num_layers; + else + icon_depths = NULL; + + gtk_widget_destroy (dialog); + + return icon_depths; +} + + +static GimpPDBStatusType +ico_init (const gchar *filename, + MsIcon *ico, + gint num_icons) +{ + memset (ico, 0, sizeof(MsIcon)); + + if (! (ico->fp = 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 = GUINT16_FROM_LE (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, + 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; + + data[line * width32 * 4 + offset/8] |= (bit_val << (7 - (bit_num % 8))); +} + + +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; + + data[line * width8 * 4 + offset/2] |= (nibble_val << (4 * (1 - (nibble_num % 2)))); +} + + +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 * +ico_create_palette(guchar *cmap, + gint num_colors, + gint num_colors_used, + gint *black_slot) +{ + guchar *palette; + gint i; + + 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) && + (cmap[i*3 + 1] == 0) && + (cmap[i*3 + 2] == 0)) + { + *black_slot = i; + } + } + + if (*black_slot == -1) + { + if (num_colors_used == num_colors) + { + 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; + } + else + { + *black_slot = num_colors_used; + } + } + + return (guint32*) palette; +} + + +static GHashTable * +ico_create_color_to_palette_map (guint32 *palette, + gint num_colors) +{ + GHashTable *hash; + gint i; + + hash = g_hash_table_new (g_int_hash, g_int_equal); + + for (i = 0; i < num_colors; i++) + { + gint *color, *slot; + guint8 *pixel = (guint8*) &palette[i]; + + 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 void +ico_free_hash_item (gpointer data1, + gpointer data2, + gpointer data3) +{ + g_free (data1); + g_free (data2); + + /* Shut up warnings: */ + data3 = NULL; +} + + +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; +} + +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, buf_bpp, black_index; + 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, &buf_bpp); + 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] = + ((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]); + if (interactive_ico) + 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; + + if (interactive_ico) + 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; + + if (interactive_ico) + gimp_progress_update (0.6 + (gdouble) i / (gdouble) num_icons * 0.3); + } + + if (interactive_ico) + 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_int32 (ico->fp, + data->palette, data->palette_len / 4); + + 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 void +ico_sync_image_to_ico (gint32 image, + gint *icon_depths) +{ + gint *layers; + gint i, num_layers; + + layers = gimp_image_get_layers(image, &num_layers); + + for (i = 0; i < num_layers; i++) + { + if (icon_depths[i] < icon_depths[num_layers + i]) + { + D(("Layer '%s' was reduced to %i bpp -- updating source image.\n", + gimp_layer_get_name (layers[i]), icon_depths[i])); + + ico_image_reduce_layer_bpp (layers[i], icon_depths[i]); + } + } + + g_free(layers); +} + + +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; +} + + +GimpPDBStatusType +SaveICO (const gchar *filename, + gint32 image) +{ + MsIcon ico; + gint *icon_depths = NULL; + gint num_icons; + GimpPDBStatusType exit_state; + + 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; + } + + if (gimp_image_base_type (image) != GIMP_RGB) + { + if (! gimp_image_convert_rgb (image)) + return GIMP_PDB_EXECUTION_ERROR; + } + + /* 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 (interactive_ico) + { + guchar *temp_buf = g_strdup_printf (_("Saving %s:"), + gimp_filename_to_utf8 (filename)); + gimp_progress_init (temp_buf); + g_free (temp_buf); + } + + /* Okay, let's actually save the thing with the depths the + user specified. */ + + if ((exit_state = ico_init (filename, &ico, num_icons) != GIMP_PDB_SUCCESS)) + return exit_state; + + D(("icon initialized ...\n")); + + ico_setup (&ico, image, icon_depths, num_icons); + + D(("icon data created ...\n")); + + if ( (exit_state = ico_save(&ico)) == GIMP_PDB_SUCCESS) + { + ico_sync_image_to_ico (image, icon_depths); + } + + D(("*** icon saved, exit status %i.\n\n", exit_state)); + + ico_cleanup (&ico); + g_free (icon_depths); + + return exit_state; +} diff --git a/plug-ins/winicon/icosave.h b/plug-ins/winicon/icosave.h new file mode 100644 index 0000000000..35e1b52774 --- /dev/null +++ b/plug-ins/winicon/icosave.h @@ -0,0 +1,30 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * GIMP Plug-in for Windows Icon files. + * Copyright (C) 2002 Christian Kreibich . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (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 + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __ICO_SAVE_H__ +#define __ICO_SAVE_H__ + + +GimpPDBStatusType SaveICO (const gchar *file_name, + gint32 image_ID); + + +#endif diff --git a/plug-ins/winicon/main.c b/plug-ins/winicon/main.c new file mode 100644 index 0000000000..05781d84ca --- /dev/null +++ b/plug-ins/winicon/main.c @@ -0,0 +1,526 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * GIMP Plug-in for Windows Icon files. + * Copyright (C) 2002 Christian Kreibich . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (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 + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include + +#include + +#include +#include + +/* #define ICO_DBG */ + +#include "main.h" +#include "icoload.h" +#include "icosave.h" + +#include "libgimp/stdplugins-intl.h" + + +gboolean interactive_ico = FALSE; + + +static void query (void); +static void run (const gchar *name, + gint nparams, + const GimpParam *param, + gint *nreturn_vals, + GimpParam **return_vals); + + +GimpPlugInInfo PLUG_IN_INFO = +{ + NULL, /* init_proc */ + NULL, /* quit_proc */ + query, /* query_proc */ + run, /* run_proc */ +}; + +MAIN () + +static void +query (void) +{ + static GimpParamDef load_args[] = + { + { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" }, + { GIMP_PDB_STRING, "filename", "The name of the file to load" }, + { GIMP_PDB_STRING, "raw_filename", "The name entered" } + }; + static GimpParamDef load_return_vals[] = + { + { GIMP_PDB_IMAGE, "image", "Output image" }, + }; + + static GimpParamDef save_args[] = + { + { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" }, + { GIMP_PDB_IMAGE, "image", "Input image" }, + { GIMP_PDB_DRAWABLE, "drawable", "Drawable to save" }, + { GIMP_PDB_STRING, "filename", "The name of the file to save the image in" }, + { GIMP_PDB_STRING, "raw_filename", "The name entered" }, + }; + + gimp_install_procedure ("file_ico_load", + "Loads files of Windows ICO file format", + "Loads files of Windows ICO file format", + "Christian Kreibich ", + "Christian Kreibich ", + "2002", + N_("/ICO"), + NULL, + GIMP_PLUGIN, + G_N_ELEMENTS (load_args), + G_N_ELEMENTS (load_return_vals), + load_args, load_return_vals); + + gimp_install_procedure ("file_ico_save", + "Saves files in Windows ICO file format", + "Saves files in Windows ICO file format", + "Christian Kreibich ", + "Christian Kreibich ", + "2002", + N_("/ICO"), + "INDEXEDA, GRAYA, RGBA", + GIMP_PLUGIN, + G_N_ELEMENTS (save_args), 0, + save_args, NULL); + + gimp_register_magic_load_handler ("file_ico_load", + "ico,wico", + "", + "0,string,\\000\\001\\000\\000,0,string,\\000\\002\\000\\000"); + gimp_register_save_handler ("file_ico_save", + "ico,wico", + ""); + +} + +static void +run (const gchar *name, + gint nparams, + const GimpParam *param, + gint *nreturn_vals, + GimpParam **return_vals) +{ + static GimpParam values[2]; + gint32 image_ID; + gint32 drawable_ID; + GimpRunMode run_mode; + GimpPDBStatusType status = GIMP_PDB_SUCCESS; + GimpExportReturn export = GIMP_EXPORT_CANCEL; + + INIT_I18N (); + + run_mode = param[0].data.d_int32; + + *nreturn_vals = 1; + *return_vals = values; + values[0].type = GIMP_PDB_STATUS; + values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR; + + if (strcmp (name, "file_ico_load") == 0) + { + switch (run_mode) + { + case GIMP_RUN_INTERACTIVE: + interactive_ico = TRUE; + break; + + case GIMP_RUN_NONINTERACTIVE: + interactive_ico = FALSE; + if (nparams != 3) + status = GIMP_PDB_CALLING_ERROR; + break; + + default: + break; + } + + if (status == GIMP_PDB_SUCCESS) + { + image_ID = LoadICO (param[1].data.d_string); + + if (image_ID != -1) + { + *nreturn_vals = 2; + values[1].type = GIMP_PDB_IMAGE; + values[1].data.d_image = image_ID; + } + else + { + status = GIMP_PDB_EXECUTION_ERROR; + } + } + } + else if (strcmp (name, "file_ico_save") == 0) + { + gchar *file_name; + + image_ID = param[1].data.d_int32; + drawable_ID = param[2].data.d_int32; + file_name = param[3].data.d_string; + + switch (run_mode) + { + case GIMP_RUN_INTERACTIVE: + interactive_ico = TRUE; + break; + + case GIMP_RUN_NONINTERACTIVE: + interactive_ico = FALSE; + /* Make sure all the arguments are there! */ + if (nparams < 5) + status = GIMP_PDB_CALLING_ERROR; + break; + + case GIMP_RUN_WITH_LAST_VALS: + interactive_ico = FALSE; + break; + + default: + break; + } + + if (status == GIMP_PDB_SUCCESS) + { + status = SaveICO (file_name, image_ID); + } + + if (export == GIMP_EXPORT_EXPORT) + gimp_image_delete (image_ID); + } + else + { + status = GIMP_PDB_CALLING_ERROR; + } + + values[0].type = GIMP_PDB_STATUS; + values[0].data.d_status = status; +} + + +guint8 * +ico_alloc_map(gint width, + gint height, + gint bpp, + gint *length) +{ + 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); + } + + *length = len; + map = g_new0(guint8, len); + + return map; +} + + +static gboolean +ico_cmap_contains_black (guchar *cmap, + gint num_colors) +{ + gint i; + + for (i = 0; i < num_colors; i++) + { + if ((cmap[3*i] == 0) && + (cmap[3*i+1] == 0) && + (cmap[3*i+2] == 0)) + { + return TRUE; + } + } + + return FALSE; +} + + +void +ico_image_reduce_layer_bpp (guint32 layer, + gint bpp) +{ + GimpPixelRgn src_pixel_rgn, dst_pixel_rgn; + gint32 tmp_image; + gint32 tmp_layer; + gint w, h; + guchar *buffer; + + w = gimp_drawable_width(layer); + h = gimp_drawable_height(layer); + + if (bpp <= 8) + { + buffer = g_new (guchar, w * h * 4); + + tmp_image = gimp_image_new (gimp_drawable_width (layer), + gimp_drawable_height (layer), + GIMP_RGB); + + tmp_layer = gimp_layer_new (tmp_image, "tmp", w, h, + GIMP_RGBA_IMAGE, 100, GIMP_NORMAL_MODE); + gimp_pixel_rgn_init (&src_pixel_rgn, gimp_drawable_get(layer), + 0, 0, w, h, TRUE, FALSE); + gimp_pixel_rgn_init (&dst_pixel_rgn, gimp_drawable_get(tmp_layer), + 0, 0, w, h, TRUE, FALSE); + 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); + gimp_image_add_layer (tmp_image, tmp_layer, 0); + + gimp_image_convert_indexed (tmp_image, + GIMP_FS_DITHER, + GIMP_MAKE_PALETTE, + 1 << bpp, + TRUE, + FALSE, + "dummy"); + + gimp_image_convert_rgb (tmp_image); + + gimp_pixel_rgn_init (&dst_pixel_rgn, gimp_drawable_get (tmp_layer), + 0, 0, w, h, TRUE, FALSE); + gimp_pixel_rgn_get_rect (&dst_pixel_rgn, buffer, 0, 0, w, h); + gimp_pixel_rgn_set_rect (&src_pixel_rgn, buffer, 0, 0, w, h); + + gimp_image_delete (tmp_image); + + gimp_drawable_update (layer, 0, 0, w ,h); + /* WTF! What else can I do to make the layer dialog update??? */ + + g_free (buffer); + } +} + + +void +ico_image_get_reduced_buf (guint32 layer, + gint bpp, + gint *num_colors, + guchar **cmap_out, + guchar **buf_out, + gint *buf_bpp) +{ + GimpPixelRgn src_pixel_rgn, dst_pixel_rgn; + gint32 tmp_image; + gint32 tmp_layer; + gint w, h; + guchar *buffer; + guchar *cmap; + gboolean result; + GimpDrawable *drawable; + + w = gimp_drawable_width (layer); + h = gimp_drawable_height (layer); + *cmap_out = NULL; + *num_colors = 0; + + buffer = g_new (guchar, w * h * 4); + + if (bpp <= 8) + { + tmp_image = gimp_image_new (gimp_drawable_width (layer), + gimp_drawable_height (layer), + GIMP_RGB); + + tmp_layer = gimp_layer_new (tmp_image, "tmp", w, h, + GIMP_RGBA_IMAGE, 100, GIMP_NORMAL_MODE); + gimp_pixel_rgn_init (&src_pixel_rgn, gimp_drawable_get(layer), + 0, 0, w, h, TRUE, FALSE); + gimp_pixel_rgn_init (&dst_pixel_rgn, gimp_drawable_get(tmp_layer), + 0, 0, w, h, TRUE, FALSE); + 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); + gimp_image_add_layer (tmp_image, tmp_layer, 0); + + result = gimp_image_convert_indexed(tmp_image, + GIMP_FS_DITHER, + GIMP_MAKE_PALETTE, + 1 << bpp, + TRUE, + FALSE, + "dummy"); + + cmap = gimp_image_get_cmap (tmp_image, num_colors); + + if (*num_colors == (1 << bpp) && + !ico_cmap_contains_black(cmap, *num_colors)) + { + /* Damn. Windows icons with color maps need the color black. + We need to eliminate one more color to make room for black: */ + + result = gimp_image_convert_rgb(tmp_image); + + gimp_pixel_rgn_init (&dst_pixel_rgn, gimp_drawable_get(tmp_layer), + 0, 0, w, h, TRUE, FALSE); + gimp_pixel_rgn_set_rect (&dst_pixel_rgn, buffer, 0, 0, w, h); + + result = gimp_image_convert_indexed(tmp_image, + GIMP_FS_DITHER, + GIMP_MAKE_PALETTE, + (1 << bpp) - 1, + TRUE, + FALSE, + "dummy"); + } + + cmap = gimp_image_get_cmap(tmp_image, num_colors); + *cmap_out = g_memdup (cmap, *num_colors * 3); + + result = gimp_image_convert_rgb(tmp_image); + + gimp_pixel_rgn_init (&dst_pixel_rgn, gimp_drawable_get(tmp_layer), + 0, 0, w, h, TRUE, FALSE); + gimp_pixel_rgn_get_rect (&dst_pixel_rgn, buffer, 0, 0, w, h); + + drawable = gimp_drawable_get(tmp_layer); + *buf_bpp = drawable->bpp; + + gimp_image_delete(tmp_image); + } + else + { + gimp_pixel_rgn_init (&dst_pixel_rgn, gimp_drawable_get(layer), + 0, 0, w, h, TRUE, FALSE); + gimp_pixel_rgn_get_rect (&dst_pixel_rgn, buffer, 0, 0, w, h); + + drawable = gimp_drawable_get (layer); + *buf_bpp = drawable->bpp; + } + + *buf_out = buffer; +} + + +static void +ico_free_color_item (gpointer data1, + gpointer data2, + gpointer data3) +{ + g_free (data1); + + /* Shut up warnings: */ + data2 = NULL; + data3 = NULL; +} + + +gint +ico_get_layer_num_colors (gint32 layer, + gboolean *uses_alpha_levels) +{ + GimpPixelRgn pixel_rgn; + gint x, y, w, h, alpha, num_colors = 0; + guint32 *buffer = NULL, *color; + GHashTable *hash; + + w = gimp_drawable_width (layer); + h = gimp_drawable_height (layer); + buffer = g_new (gint32, w * h); + + gimp_pixel_rgn_init (&pixel_rgn, gimp_drawable_get (layer), + 0, 0, w, h, TRUE, FALSE); + gimp_pixel_rgn_get_rect (&pixel_rgn, (guchar*) buffer, 0, 0, w, h); + + hash = g_hash_table_new (g_int_hash, g_int_equal); + *uses_alpha_levels = FALSE; + + for (y = 0; y < h; y++) + for (x = 0; x < w; x++) + { + color = g_new0 (guint32, 1); + *color = buffer[y * w + x]; + alpha = ((guint8*) color)[3]; + + if (alpha != 0 && alpha != 255) + *uses_alpha_levels = TRUE; + + g_hash_table_insert(hash, color, color); + } + + num_colors = g_hash_table_size(hash); + + g_hash_table_foreach (hash, ico_free_color_item, NULL); + g_hash_table_destroy (hash); + + g_free(buffer); + + return num_colors; +} + + + +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); + } +} + + diff --git a/plug-ins/winicon/main.h b/plug-ins/winicon/main.h new file mode 100644 index 0000000000..68e0bf1249 --- /dev/null +++ b/plug-ins/winicon/main.h @@ -0,0 +1,115 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * GIMP Plug-in for Windows Icon files. + * Copyright (C) 2002 Christian Kreibich . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (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 + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __MAIN_H__ +#define __MAIN_H__ + + +extern gboolean interactive_ico; + + +#ifdef ICO_DBG +#define D(x) \ +{ \ + printf("ICO plugin: "); \ + printf x; \ +} +#else +#define D(x) +#endif + + +#define MAXLEN 4096 + +typedef struct _MsIconEntry +{ + 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; + +typedef struct _MsIconData +{ + /* Bitmap header data */ + guint32 header_size; /* = 40 Bytes */ + guint32 width; + guint32 height; + guint16 planes; + guint16 bpp; + guint32 compression; /* not used for icons */ + guint32 image_size; /* size of image */ + guint32 x_res; + guint32 y_res; + guint32 used_clrs; + guint32 important_clrs; + + 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 +{ + FILE *fp; + guint cp; + const gchar *filename; + + guint16 reserved; + guint16 resource_type; + guint16 icon_count; + MsIconEntry *icon_dir; + MsIconData *icon_data; +} MsIcon; + + +/* Miscellaneous helper functions below: */ + +/* 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_image_reduce_layer_bpp (guint32 layer, + gint bpp); +void ico_image_get_reduced_buf (guint32 layer, + gint bpp, + gint *num_colors, + guchar **cmap, + guchar **buffer, + gint *buf_bpp); +gint ico_get_layer_num_colors (gint32 layer, + gboolean *uses_alpha_levels); +void ico_cleanup (MsIcon *ico); + + +#endif diff --git a/po-plug-ins/ChangeLog b/po-plug-ins/ChangeLog index fd36c022e1..cb86e392c2 100644 --- a/po-plug-ins/ChangeLog +++ b/po-plug-ins/ChangeLog @@ -1,6 +1,7 @@ 2004-04-15 Sven Neumann - * POTFILES.in: removed plug-ins/common/gicon.c. + * POTFILES.in: removed plug-ins/common/gicon.c; added new files + for winicon plug-in. 2004-04-13 Dmitry G. Mastrukov diff --git a/po-plug-ins/POTFILES.in b/po-plug-ins/POTFILES.in index b1653f4da9..fb23674b1b 100644 --- a/po-plug-ins/POTFILES.in +++ b/po-plug-ins/POTFILES.in @@ -248,5 +248,9 @@ plug-ins/sel2path/pxl-outline.c plug-ins/sel2path/sel2path.c plug-ins/sgi/sgi.c plug-ins/twain/twain.c +plug-ins/winicon/icodialog.c +plug-ins/winicon/icoload.c +plug-ins/winicon/icosave.c +plug-ins/winicon/main.c plug-ins/winsnap/winsnap.c plug-ins/xjt/xjt.c