mirror of
https://gitlab.gnome.org/GNOME/gimp
synced 2024-10-22 04:22:29 +00:00
see bug #352899
This commit is contained in:
parent
5b021fc255
commit
f1a49507d1
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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__ */
|
||||
|
|
Loading…
Reference in a new issue