Initial checkin of Pika from heckimp

This commit is contained in:
2023-09-25 15:35:21 -07:00
commit 891e999216
6761 changed files with 5240685 additions and 0 deletions

View File

@ -0,0 +1,704 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
*
* PIKA Plug-in for Windows Icon files.
* Copyright (C) 2002 Christian Kreibich <christian@whoop.org>.
*
* 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 3 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, see <https://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <libpika/pika.h>
#include <libpika/pikaui.h>
/* #define ICO_DBG */
#include "ico.h"
#include "ico-dialog.h"
#include "ico-save.h"
#include "libpika/stdplugins-intl.h"
static void ico_dialog_bpp_changed (GtkWidget *combo,
GObject *hbox);
static void ico_dialog_toggle_compress (GtkWidget *checkbox,
GObject *hbox);
static void ico_dialog_check_compat (GtkWidget *dialog,
IcoSaveInfo *info);
static void ico_dialog_ani_update_inam (GtkEntry *entry,
gpointer data);
static void ico_dialog_ani_update_iart (GtkEntry *entry,
gpointer data);
GtkWidget *
ico_dialog_new (IcoSaveInfo *info,
AniFileHeader *ani_header,
AniSaveInfo *ani_info)
{
GtkWidget *dialog;
GtkWidget *main_vbox;
GtkWidget *vbox;
GtkWidget *frame;
GtkWidget *scrolled_window;
GtkWidget *viewport;
GtkWidget *warning;
dialog = pika_export_dialog_new (ani_header ?
_("Windows Animated Cursor") : info->is_cursor ?
_("Windows Cursor") : _("Windows Icon"),
PLUG_IN_BINARY,
"plug-in-winicon");
/* 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);
if (ani_header)
{
g_object_set_data (G_OBJECT (dialog), "save_ani_header", ani_header);
g_object_set_data (G_OBJECT (dialog), "save_ani_info", ani_info);
}
main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 6);
gtk_box_pack_start (GTK_BOX (pika_export_dialog_get_content_area (dialog)),
main_vbox, TRUE, TRUE, 0);
gtk_widget_show (main_vbox);
/*Animated Cursor */
if (ani_header)
{
GtkWidget *grid;
GtkAdjustment *adjustment;
GtkWidget *spin;
GtkWidget *label;
GtkWidget *hbox;
GtkWidget *entry;
frame = pika_frame_new (_("Animated Cursor Settings"));
gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
gtk_widget_show (frame);
grid = gtk_grid_new ();
gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
gtk_container_add (GTK_CONTAINER (frame), grid);
gtk_widget_show (grid);
/* Cursor Name */
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
pika_grid_attach_aligned (GTK_GRID (grid), 0, 1,
_("_Cursor Name (Optional)"),
0.0, 0.5,
hbox, 1);
entry = gtk_entry_new ();
gtk_entry_set_text (GTK_ENTRY (entry),
ani_info->inam ? ani_info->inam : "");
gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, FALSE, 0);
gtk_widget_show (entry);
g_signal_connect (GTK_ENTRY (entry), "focus-out-event",
G_CALLBACK (ico_dialog_ani_update_inam),
NULL);
/* Author Name */
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
pika_grid_attach_aligned (GTK_GRID (grid), 0, 3,
_("_Author Name (Optional)"),
0.0, 0.5,
hbox, 1);
entry = gtk_entry_new ();
gtk_entry_set_text (GTK_ENTRY (entry),
ani_info->iart ? ani_info->iart : "");
gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, FALSE, 0);
gtk_widget_show (entry);
g_signal_connect (GTK_ENTRY (entry), "focus-out-event",
G_CALLBACK (ico_dialog_ani_update_iart),
NULL);
/* Default delay spin */
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
pika_grid_attach_aligned (GTK_GRID (grid), 0, 5,
_("_Delay between frames:"),
0.0, 0.5,
hbox, 1);
adjustment = gtk_adjustment_new (ani_header->jif_rate, 1, G_MAXINT,
1, 10, 0);
spin = pika_spin_button_new (adjustment, 1, 0);
gtk_box_pack_start (GTK_BOX (hbox), spin, FALSE, FALSE, 0);
gtk_widget_show (spin);
label = gtk_label_new (_(" jiffies (16.66 ms)"));
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
gtk_widget_show (label);
g_signal_connect (adjustment, "value-changed",
G_CALLBACK (pika_int_adjustment_update),
&ani_header->jif_rate);
}
/* Cursor */
frame = pika_frame_new (_("Icon Details"));
gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 4);
gtk_widget_show (frame);
scrolled_window = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
gtk_container_add (GTK_CONTAINER (frame), scrolled_window);
gtk_widget_show (scrolled_window);
viewport = gtk_viewport_new (NULL, NULL);
gtk_container_add (GTK_CONTAINER (scrolled_window), viewport);
gtk_widget_show (viewport);
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
g_object_set_data (G_OBJECT (dialog), "icons_vbox", vbox);
gtk_container_add (GTK_CONTAINER (viewport), vbox);
gtk_widget_show (vbox);
warning = g_object_new (PIKA_TYPE_HINT_BOX,
"icon-name", PIKA_ICON_DIALOG_WARNING,
"hint",
_("Large icons and compression are not supported "
"by all programs. Older applications may not "
"open this file correctly."),
NULL);
gtk_box_pack_end (GTK_BOX (main_vbox), warning, FALSE, FALSE, 12);
/* don't show the warning here */
g_object_set_data (G_OBJECT (dialog), "warning", warning);
return dialog;
}
static GtkWidget *
ico_preview_new (PikaDrawable *layer)
{
GtkWidget *image;
GdkPixbuf *pixbuf;
gint width = pika_drawable_get_width (layer);
gint height = pika_drawable_get_height (layer);
pixbuf = pika_drawable_get_thumbnail (layer,
MIN (width, 128), MIN (height, 128),
PIKA_PIXBUF_SMALL_CHECKS);
image = gtk_image_new_from_pixbuf (pixbuf);
g_object_unref (pixbuf);
return image;
}
/* 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,
PikaDrawable *layer,
gint layer_num,
IcoSaveInfo *info)
{
static GtkSizeGroup *size = NULL;
GtkWidget *frame;
GtkWidget *hbox;
GtkWidget *vbox;
GtkWidget *combo;
GtkWidget *checkbox;
gchar *frame_header;
gint width = pika_drawable_get_width (layer);
gint height = pika_drawable_get_height (layer);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
frame_header = g_strdup_printf ("%dx%d", width, height);
frame = pika_frame_new (frame_header);
gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
gtk_widget_set_visible (frame, TRUE);
g_free (frame_header);
/* 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", 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_set_halign (icon_preview, GTK_ALIGN_END);
gtk_widget_set_valign (icon_preview, GTK_ALIGN_CENTER);
gtk_container_add (GTK_CONTAINER (frame), icon_preview);
gtk_widget_show (icon_preview);
if (! size)
size = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
gtk_size_group_add_widget (size, icon_preview);
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
gtk_widget_show (vbox);
combo = pika_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,
_("24 bpp, 1-bit alpha, no palette"), 24,
_("32 bpp, 8-bit alpha, no palette"), 32,
NULL);
pika_int_combo_box_set_active (PIKA_INT_COMBO_BOX (combo),
info->depths[layer_num]);
gtk_widget_set_margin_top (GTK_WIDGET (combo), 6);
g_signal_connect (combo, "changed",
G_CALLBACK (ico_dialog_bpp_changed),
hbox);
g_object_set_data (G_OBJECT (hbox), "icon_menu", combo);
gtk_box_pack_start (GTK_BOX (vbox), combo, FALSE, FALSE, 0);
gtk_widget_show (combo);
checkbox = gtk_check_button_new_with_label (_("Compressed (PNG)"));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbox),
info->compress[layer_num]);
g_signal_connect (checkbox, "toggled",
G_CALLBACK (ico_dialog_toggle_compress), hbox);
gtk_box_pack_start (GTK_BOX (vbox), checkbox, FALSE, FALSE, 0);
gtk_widget_show (checkbox);
return hbox;
}
static GtkWidget *
ico_dialog_get_layer_preview (GtkWidget *dialog,
PikaDrawable *layer)
{
GtkWidget *preview;
GtkWidget *icon_hbox;
gchar key[ICO_MAXBUF];
g_snprintf (key, sizeof (key), "layer_%i_hbox",
pika_item_get_id (PIKA_ITEM (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;
}
static void
ico_dialog_update_icon_preview (GtkWidget *dialog,
PikaDrawable *layer,
gint bpp)
{
GtkWidget *preview = ico_dialog_get_layer_preview (dialog, layer);
GdkPixbuf *pixbuf;
const Babl *format;
gint w = pika_drawable_get_width (layer);
gint h = pika_drawable_get_height (layer);
if (! preview)
return;
switch (pika_drawable_type (layer))
{
case PIKA_RGB_IMAGE:
format = babl_format ("R'G'B' u8");
break;
case PIKA_RGBA_IMAGE:
format = babl_format ("R'G'B'A u8");
break;
case PIKA_GRAY_IMAGE:
format = babl_format ("Y' u8");
break;
case PIKA_GRAYA_IMAGE:
format = babl_format ("Y'A u8");
break;
case PIKA_INDEXED_IMAGE:
case PIKA_INDEXEDA_IMAGE:
format = pika_drawable_get_format (layer);
break;
default:
g_return_if_reached ();
}
if (bpp <= 8)
{
GeglBuffer *buffer;
GeglBuffer *tmp;
PikaImage *image;
PikaImage *tmp_image;
PikaLayer *tmp_layer;
guchar *buf;
guchar *cmap;
gint num_colors;
image = pika_item_get_image (PIKA_ITEM (layer));
tmp_image = pika_image_new (w, h, pika_image_get_base_type (image));
pika_image_undo_disable (tmp_image);
if (pika_drawable_is_indexed (layer))
{
cmap = pika_image_get_colormap (image, NULL, &num_colors);
pika_image_set_colormap (tmp_image, cmap, num_colors);
g_free (cmap);
}
tmp_layer = pika_layer_new (tmp_image, "temporary", w, h,
pika_drawable_type (layer),
100,
pika_image_get_default_new_layer_mode (tmp_image));
pika_image_insert_layer (tmp_image, tmp_layer, NULL, 0);
buffer = pika_drawable_get_buffer (layer);
tmp = pika_drawable_get_buffer (PIKA_DRAWABLE (tmp_layer));
buf = g_malloc (w * h * 4);
gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, w, h), 1.0,
format, buf,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
gegl_buffer_copy (buffer, NULL, GEGL_ABYSS_NONE, tmp, NULL);
g_object_unref (tmp);
g_object_unref (buffer);
if (pika_drawable_is_indexed (layer))
pika_image_convert_rgb (tmp_image);
pika_image_convert_indexed (tmp_image,
PIKA_CONVERT_DITHER_FS,
PIKA_CONVERT_PALETTE_GENERATE,
1 << bpp, TRUE, FALSE, "dummy");
cmap = pika_image_get_colormap (tmp_image, NULL, &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 (pika_drawable_is_indexed (layer))
{
g_free (cmap);
cmap = pika_image_get_colormap (image, NULL, &num_colors);
pika_image_set_colormap (tmp_image, cmap, num_colors);
}
else if (pika_drawable_is_gray (layer))
{
pika_image_convert_grayscale (tmp_image);
}
else
{
pika_image_convert_rgb (tmp_image);
}
tmp = pika_drawable_get_buffer (PIKA_DRAWABLE (tmp_layer));
gegl_buffer_set (tmp, GEGL_RECTANGLE (0, 0, w, h), 0,
format, buf, GEGL_AUTO_ROWSTRIDE);
g_object_unref (tmp);
if (!pika_drawable_is_rgb (layer))
pika_image_convert_rgb (tmp_image);
pika_image_convert_indexed (tmp_image,
PIKA_CONVERT_DITHER_FS,
PIKA_CONVERT_PALETTE_GENERATE,
(1 << bpp) - 1, TRUE, FALSE, "dummy");
}
g_free (cmap);
g_free (buf);
pixbuf = pika_drawable_get_thumbnail (PIKA_DRAWABLE (tmp_layer),
MIN (w, 128), MIN (h, 128),
PIKA_PIXBUF_SMALL_CHECKS);
pika_image_delete (tmp_image);
}
else if (bpp == 24)
{
GeglBuffer *buffer;
GeglBuffer *tmp;
PikaImage *image;
PikaImage *tmp_image;
PikaLayer *tmp_layer;
PikaValueArray *return_vals;
image = pika_item_get_image (PIKA_ITEM (layer));
tmp_image = pika_image_new (w, h, pika_image_get_base_type (image));
pika_image_undo_disable (tmp_image);
if (pika_drawable_is_indexed (layer))
{
guchar *cmap;
gint num_colors;
cmap = pika_image_get_colormap (image, NULL, &num_colors);
pika_image_set_colormap (tmp_image, cmap, num_colors);
g_free (cmap);
}
tmp_layer = pika_layer_new (tmp_image, "temporary", w, h,
pika_drawable_type (layer),
100,
pika_image_get_default_new_layer_mode (tmp_image));
pika_image_insert_layer (tmp_image, tmp_layer, NULL, 0);
buffer = pika_drawable_get_buffer (layer);
tmp = pika_drawable_get_buffer (PIKA_DRAWABLE (tmp_layer));
gegl_buffer_copy (buffer, NULL, GEGL_ABYSS_NONE, tmp, NULL);
g_object_unref (tmp);
g_object_unref (buffer);
if (pika_drawable_is_indexed (layer))
pika_image_convert_rgb (tmp_image);
return_vals =
pika_pdb_run_procedure (pika_get_pdb (),
"plug-in-threshold-alpha",
PIKA_TYPE_RUN_MODE, PIKA_RUN_NONINTERACTIVE,
PIKA_TYPE_IMAGE, tmp_image,
PIKA_TYPE_DRAWABLE, tmp_layer,
G_TYPE_INT, ICO_ALPHA_THRESHOLD,
G_TYPE_NONE);
pika_value_array_unref (return_vals);
pixbuf = pika_drawable_get_thumbnail (PIKA_DRAWABLE (tmp_layer),
MIN (w, 128), MIN (h, 128),
PIKA_PIXBUF_SMALL_CHECKS);
pika_image_delete (tmp_image);
}
else
{
pixbuf = pika_drawable_get_thumbnail (layer,
MIN (w, 128), MIN (h, 128),
PIKA_PIXBUF_SMALL_CHECKS);
}
gtk_image_set_from_pixbuf (GTK_IMAGE (preview), pixbuf);
g_object_unref (pixbuf);
}
void
ico_dialog_add_icon (GtkWidget *dialog,
PikaDrawable *layer,
gint layer_num)
{
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",
pika_item_get_id (PIKA_ITEM (layer)));
g_object_set_data (G_OBJECT (dialog), key, hbox);
ico_dialog_update_icon_preview (dialog, layer, info->depths[layer_num]);
ico_dialog_check_compat (dialog, info);
/* Cursor */
if (info->is_cursor)
{
GtkWidget *grid;
GtkAdjustment *adj;
GtkWidget *spinbutton;
grid = gtk_grid_new ();
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
gtk_box_pack_start (GTK_BOX (hbox), grid, FALSE, FALSE, 0);
gtk_widget_show (grid);
adj = (GtkAdjustment *)
gtk_adjustment_new (info->hot_spot_x[layer_num], 0,
G_MAXUINT16, 1, 10, 0);
spinbutton = pika_spin_button_new (adj, 1.0, 0);
gtk_spin_button_set_range (GTK_SPIN_BUTTON (spinbutton),
0, G_MAXUINT16);
pika_grid_attach_aligned (GTK_GRID (grid), 0, 0,
_("Hot spot _X:"), 0.0, 0.5,
spinbutton, 1);
g_signal_connect (adj, "value-changed",
G_CALLBACK (pika_int_adjustment_update),
&info->hot_spot_x[layer_num]);
adj = (GtkAdjustment *)
gtk_adjustment_new (info->hot_spot_y[layer_num], 0,
G_MAXUINT16, 1, 10, 0);
spinbutton = pika_spin_button_new (adj, 1.0, 0);
gtk_spin_button_set_range (GTK_SPIN_BUTTON (spinbutton),
0, G_MAXUINT16);
pika_grid_attach_aligned (GTK_GRID (grid), 0, 1,
_("Hot spot _Y:"), 0.0, 0.5,
spinbutton, 1);
g_signal_connect (adj, "value-changed",
G_CALLBACK (pika_int_adjustment_update),
&info->hot_spot_y[layer_num]);
}
}
static void
ico_dialog_bpp_changed (GtkWidget *combo,
GObject *hbox)
{
GtkWidget *dialog;
PikaDrawable *layer;
gint layer_num;
gint bpp;
IcoSaveInfo *info;
dialog = gtk_widget_get_toplevel (combo);
pika_int_combo_box_get_active (PIKA_INT_COMBO_BOX (combo), &bpp);
info = g_object_get_data (G_OBJECT (dialog), "save_info");
g_assert (info);
layer = 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 ... */
info->depths[layer_num] = bpp;
ico_dialog_update_icon_preview (dialog, layer, bpp);
}
static void
ico_dialog_toggle_compress (GtkWidget *checkbox,
GObject *hbox)
{
GtkWidget *dialog;
gint layer_num;
IcoSaveInfo *info;
dialog = gtk_widget_get_toplevel (checkbox);
info = g_object_get_data (G_OBJECT (dialog), "save_info");
g_assert (info);
layer_num = GPOINTER_TO_INT (g_object_get_data (hbox, "icon_layer_num"));
/* Update vector entry for later when we're actually saving */
info->compress[layer_num] =
gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (checkbox));
ico_dialog_check_compat (dialog, info);
}
static void
ico_dialog_check_compat (GtkWidget *dialog,
IcoSaveInfo *info)
{
GtkWidget *warning;
GList *iter;
gboolean warn = FALSE;
gint i;
for (iter = info->layers, i = 0; iter; iter = iter->next, i++)
{
if (pika_drawable_get_width (iter->data) > 255 ||
pika_drawable_get_height (iter->data) > 255 ||
info->compress[i])
{
warn = TRUE;
break;
}
}
warning = g_object_get_data (G_OBJECT (dialog), "warning");
gtk_widget_set_visible (warning, warn);
}
static void
ico_dialog_ani_update_inam (GtkEntry *entry,
gpointer data)
{
AniSaveInfo *ani_info;
GtkWidget *dialog;
dialog = gtk_widget_get_toplevel (GTK_WIDGET (entry));
ani_info = g_object_get_data (G_OBJECT (dialog), "save_ani_info");
ani_info->inam = g_strdup_printf ("%s", gtk_entry_get_text (entry));
}
static void
ico_dialog_ani_update_iart (GtkEntry *entry,
gpointer data)
{
AniSaveInfo *ani_info;
GtkWidget *dialog;
dialog = gtk_widget_get_toplevel (GTK_WIDGET (entry));
ani_info = g_object_get_data (G_OBJECT (dialog), "save_ani_info");
ani_info->iart = g_strdup_printf ("%s", gtk_entry_get_text (entry));
}

View File

@ -0,0 +1,36 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
*
* PIKA Plug-in for Windows Icon files.
* Copyright (C) 2002 Christian Kreibich <christian@whoop.org>.
*
* 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 3 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, see <https://www.gnu.org/licenses/>.
*/
#ifndef __ICO_DIALOG_H__
#define __ICO_DIALOG_H__
GtkWidget * ico_dialog_new (IcoSaveInfo *info,
AniFileHeader *ani_header,
AniSaveInfo *ani_info);
void ico_dialog_add_icon (GtkWidget *dialog,
PikaDrawable *layer,
gint layer_num);
#endif /* __ICO_DIALOG_H__ */

1091
plug-ins/file-ico/ico-load.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,55 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
*
* PIKA Plug-in for Windows Icon files.
* Copyright (C) 2002 Christian Kreibich <christian@whoop.org>.
*
* 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 3 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, see <https://www.gnu.org/licenses/>.
*/
#ifndef __ICO_LOAD_H__
#define __ICO_LOAD_H__
PikaImage * ico_load_image (GFile *file,
gint32 *file_offset,
gint frame_num,
GError **error);
PikaImage * ani_load_image (GFile *file,
gboolean load_thumb,
gint *width,
gint *height,
GError **error);
PikaImage * ico_load_thumbnail_image (GFile *file,
gint *width,
gint *height,
gint32 file_offset,
GError **error);
gint ico_get_bit_from_data (const guint8 *data,
gint line_width,
gint bit);
gint ico_get_nibble_from_data (const guint8 *data,
gint line_width,
gint nibble);
gint ico_get_byte_from_data (const guint8 *data,
gint line_width,
gint byte);
#endif /* __ICO_LOAD_H__ */

1713
plug-ins/file-ico/ico-save.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,58 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
*
* PIKA Plug-in for Windows Icon files.
* Copyright (C) 2002 Christian Kreibich <christian@whoop.org>.
*
* 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 3 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, see <https://www.gnu.org/licenses/>.
*/
#ifndef __ICO_SAVE_H__
#define __ICO_SAVE_H__
PikaPDBStatusType ico_save_image (GFile *file,
PikaImage *image,
gint32 run_mode,
GError **error);
PikaPDBStatusType cur_save_image (GFile *file,
PikaImage *image,
gint32 run_mode,
gint *n_hot_spot_x,
gint32 **hot_spot_x,
gint *n_hot_spot_y,
gint32 **hot_spot_y,
GError **error);
PikaPDBStatusType ani_save_image (GFile *file,
PikaImage *image,
gint32 run_mode,
gint *n_hot_spot_x,
gint32 **hot_spot_x,
gint *n_hot_spot_y,
gint32 **hot_spot_y,
AniFileHeader *header,
AniSaveInfo *ani_info,
GError **error);
gboolean ico_cmap_contains_black (const guchar *cmap,
gint num_colors);
#endif /* __ICO_SAVE_H__ */

780
plug-ins/file-ico/ico.c Normal file
View File

@ -0,0 +1,780 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
*
* PIKA Plug-in for Windows Icon files.
* Copyright (C) 2002 Christian Kreibich <christian@whoop.org>.
*
* 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 3 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, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <glib/gstdio.h>
#include <libpika/pika.h>
#include <libpika/pikaui.h>
/* #define ICO_DBG */
#include "ico.h"
#include "ico-load.h"
#include "ico-save.h"
#include "libpika/stdplugins-intl.h"
#define LOAD_PROC "file-ico-load"
#define LOAD_CUR_PROC "file-cur-load"
#define LOAD_ANI_PROC "file-ani-load"
#define LOAD_THUMB_PROC "file-ico-load-thumb"
#define LOAD_ANI_THUMB_PROC "file-ani-load-thumb"
#define SAVE_PROC "file-ico-save"
#define SAVE_CUR_PROC "file-cur-save"
#define SAVE_ANI_PROC "file-ani-save"
typedef struct _Ico Ico;
typedef struct _IcoClass IcoClass;
struct _Ico
{
PikaPlugIn parent_instance;
};
struct _IcoClass
{
PikaPlugInClass parent_class;
};
#define ICO_TYPE (ico_get_type ())
#define ICO (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ICO_TYPE, Ico))
GType ico_get_type (void) G_GNUC_CONST;
static GList * ico_query_procedures (PikaPlugIn *plug_in);
static PikaProcedure * ico_create_procedure (PikaPlugIn *plug_in,
const gchar *name);
static PikaValueArray * ico_load (PikaProcedure *procedure,
PikaRunMode run_mode,
GFile *file,
const PikaValueArray *args,
gpointer run_data);
static PikaValueArray * ani_load (PikaProcedure *procedure,
PikaRunMode run_mode,
GFile *file,
const PikaValueArray *args,
gpointer run_data);
static PikaValueArray * ico_load_thumb (PikaProcedure *procedure,
GFile *file,
gint size,
const PikaValueArray *args,
gpointer run_data);
static PikaValueArray * ani_load_thumb (PikaProcedure *procedure,
GFile *file,
gint size,
const PikaValueArray *args,
gpointer run_data);
static PikaValueArray * ico_save (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
GFile *file,
const PikaValueArray *args,
gpointer run_data);
static PikaValueArray * cur_save (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
GFile *file,
const PikaValueArray *args,
gpointer run_data);
static PikaValueArray * ani_save (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
GFile *file,
const PikaValueArray *args,
gpointer run_data);
G_DEFINE_TYPE (Ico, ico, PIKA_TYPE_PLUG_IN)
PIKA_MAIN (ICO_TYPE)
DEFINE_STD_SET_I18N
static void
ico_class_init (IcoClass *klass)
{
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
plug_in_class->query_procedures = ico_query_procedures;
plug_in_class->create_procedure = ico_create_procedure;
plug_in_class->set_i18n = STD_SET_I18N;
}
static void
ico_init (Ico *ico)
{
}
static GList *
ico_query_procedures (PikaPlugIn *plug_in)
{
GList *list = NULL;
list = g_list_append (list, g_strdup (LOAD_THUMB_PROC));
list = g_list_append (list, g_strdup (LOAD_ANI_THUMB_PROC));
list = g_list_append (list, g_strdup (LOAD_PROC));
list = g_list_append (list, g_strdup (LOAD_CUR_PROC));
list = g_list_append (list, g_strdup (LOAD_ANI_PROC));
list = g_list_append (list, g_strdup (SAVE_PROC));
list = g_list_append (list, g_strdup (SAVE_CUR_PROC));
list = g_list_append (list, g_strdup (SAVE_ANI_PROC));
return list;
}
static PikaProcedure *
ico_create_procedure (PikaPlugIn *plug_in,
const gchar *name)
{
PikaProcedure *procedure = NULL;
if (! strcmp (name, LOAD_PROC))
{
procedure = pika_load_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
ico_load, NULL, NULL);
pika_procedure_set_menu_label (procedure, _("Microsoft Windows icon"));
pika_procedure_set_icon_name (procedure, PIKA_ICON_BRUSH);
pika_procedure_set_documentation (procedure,
"Loads files of Windows ICO file format",
"Loads files of Windows ICO file format",
name);
pika_procedure_set_attribution (procedure,
"Christian Kreibich <christian@whoop.org>",
"Christian Kreibich <christian@whoop.org>",
"2002");
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
"image/x-ico");
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
"ico");
/* We do not set magics here, since that interferes with certain types
of TGA images. */
pika_load_procedure_set_thumbnail_loader (PIKA_LOAD_PROCEDURE (procedure),
LOAD_THUMB_PROC);
}
else if (! strcmp (name, LOAD_CUR_PROC))
{
procedure = pika_load_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
ico_load, NULL, NULL);
pika_procedure_set_menu_label (procedure, _("Microsoft Windows cursor"));
pika_procedure_set_icon_name (procedure, PIKA_ICON_BRUSH);
pika_procedure_set_documentation (procedure,
"Loads files of Windows CUR file format",
"Loads files of Windows CUR file format",
name);
pika_procedure_set_attribution (procedure,
"Christian Kreibich <christian@whoop.org>, "
"Nikc M.",
"Christian Kreibich <christian@whoop.org>, "
"Nikc M.",
"2002-2022");
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
"image/vnd.microsoft.icon");
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
"cur");
/* We do not set magics here, since that interferes with certain types
of TGA images. */
pika_load_procedure_set_thumbnail_loader (PIKA_LOAD_PROCEDURE (procedure),
LOAD_THUMB_PROC);
}
else if (! strcmp (name, LOAD_ANI_PROC))
{
procedure = pika_load_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
ani_load, NULL, NULL);
pika_procedure_set_menu_label (procedure, _("Microsoft Windows animated cursor"));
pika_procedure_set_icon_name (procedure, PIKA_ICON_BRUSH);
pika_procedure_set_documentation (procedure,
_("Loads files of Windows ANI file format"),
"Loads files of Windows ANI file format",
name);
pika_procedure_set_attribution (procedure,
"Christian Kreibich <christian@whoop.org>, "
"James Huang, Alex S.",
"Christian Kreibich <christian@whoop.org>, "
"James Huang, Alex S.",
"2007-2022");
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
"application/x-navi-animation");
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
"ani");
pika_file_procedure_set_magics (PIKA_FILE_PROCEDURE (procedure),
"8,string,ACON");
pika_load_procedure_set_thumbnail_loader (PIKA_LOAD_PROCEDURE (procedure),
LOAD_ANI_THUMB_PROC);
}
else if (! strcmp (name, LOAD_THUMB_PROC))
{
procedure = pika_thumbnail_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
ico_load_thumb, NULL, NULL);
pika_procedure_set_documentation (procedure,
"Loads a preview from a Windows ICO or CUR files",
"",
name);
pika_procedure_set_attribution (procedure,
"Dom Lachowicz, Sven Neumann",
"Sven Neumann <sven@gimp.org>",
"2005");
}
else if (! strcmp (name, LOAD_ANI_THUMB_PROC))
{
procedure = pika_thumbnail_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
ani_load_thumb, NULL, NULL);
pika_procedure_set_documentation (procedure,
_("Loads a preview from a Windows ANI files"),
"",
name);
pika_procedure_set_attribution (procedure,
"Dom Lachowicz, Sven Neumann, James Huang, "
"Alex S.",
"Dom Lachowicz, "
"Sven Neumann <sven@gimp.org>, "
"James Huang, Alex S.",
"2007-2022");
}
else if (! strcmp (name, SAVE_PROC))
{
procedure = pika_save_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
ico_save, NULL, NULL);
pika_procedure_set_image_types (procedure, "*");
pika_procedure_set_menu_label (procedure, _("Microsoft Windows icon"));
pika_procedure_set_icon_name (procedure, PIKA_ICON_BRUSH);
pika_procedure_set_documentation (procedure,
"Saves files in Windows ICO file format",
"Saves files in Windows ICO file format",
name);
pika_procedure_set_attribution (procedure,
"Christian Kreibich <christian@whoop.org>",
"Christian Kreibich <christian@whoop.org>",
"2002");
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
"image/x-ico");
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
"ico");
}
else if (! strcmp (name, SAVE_CUR_PROC))
{
procedure = pika_save_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
cur_save, NULL, NULL);
pika_procedure_set_image_types (procedure, "*");
pika_procedure_set_menu_label (procedure, _("Microsoft Windows cursor"));
pika_procedure_set_icon_name (procedure, PIKA_ICON_BRUSH);
pika_procedure_set_documentation (procedure,
"Saves files in Windows CUR file format",
"Saves files in Windows CUR file format",
name);
pika_procedure_set_attribution (procedure,
"Christian Kreibich <christian@whoop.org>, "
"Nikc M.",
"Christian Kreibich <christian@whoop.org>, "
"Nikc M.",
"2002-2022");
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
"image/vnd.microsoft.icon");
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
"cur");
PIKA_PROC_ARG_INT (procedure, "n-hot-spot-x",
"Number of hot spot's X coordinates",
"Number of hot spot's X coordinates",
0, G_MAXINT, 0,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT32_ARRAY (procedure, "hot-spot-x",
"Hot spot X",
"X coordinates of hot spot (one per layer)",
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "n-hot-spot-y",
"Number of hot spot's Y coordinates",
"Number of hot spot's Y coordinates",
0, G_MAXINT, 0,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT32_ARRAY (procedure, "hot-spot-y",
"Hot spot Y",
"Y coordinates of hot spot (one per layer)",
G_PARAM_READWRITE);
}
else if (! strcmp (name, SAVE_ANI_PROC))
{
procedure = pika_save_procedure_new (plug_in, name,
PIKA_PDB_PROC_TYPE_PLUGIN,
ani_save, NULL, NULL);
pika_procedure_set_image_types (procedure, "*");
pika_procedure_set_menu_label (procedure, _("Microsoft Windows animated cursor"));
pika_procedure_set_icon_name (procedure, PIKA_ICON_BRUSH);
pika_procedure_set_documentation (procedure,
_("Saves files in Windows ANI file format"),
_("Saves files in Windows ANI file format"),
name);
pika_procedure_set_attribution (procedure,
"Christian Kreibich <christian@whoop.org>, "
"James Huang, Alex S.",
"Christian Kreibich <christian@whoop.org>, "
"James Huang, Alex S.",
"2007-2022");
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
"application/x-navi-animation");
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
"ani");
PIKA_PROC_ARG_STRING (procedure, "cursor-name",
"Cursor Name",
_("Cursor Name (Optional)"),
NULL,
G_PARAM_READWRITE);
PIKA_PROC_ARG_STRING (procedure, "author-name",
"Cursor Author",
_("Cursor Author (Optional)"),
NULL,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "default-delay",
"Default delay",
"Default delay between frames "
"in jiffies (1/60 of a second)",
0, G_MAXINT, 8,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "n-hot-spot-x",
"Number of hot spot's X coordinates",
"Number of hot spot's X coordinates",
0, G_MAXINT, 0,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT32_ARRAY (procedure, "hot-spot-x",
"Hot spot X",
"X coordinates of hot spot (one per layer)",
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT (procedure, "n-hot-spot-y",
"Number of hot spot's Y coordinates",
"Number of hot spot's Y coordinates",
0, G_MAXINT, 0,
G_PARAM_READWRITE);
PIKA_PROC_ARG_INT32_ARRAY (procedure, "hot-spot-y",
"Hot spot Y",
"Y coordinates of hot spot (one per layer)",
G_PARAM_READWRITE);
}
return procedure;
}
static PikaValueArray *
ico_load (PikaProcedure *procedure,
PikaRunMode run_mode,
GFile *file,
const PikaValueArray *args,
gpointer run_data)
{
PikaValueArray *return_vals;
PikaImage *image;
GError *error = NULL;
gegl_init (NULL, NULL);
image = ico_load_image (file, NULL, -1, &error);
if (! image)
return pika_procedure_new_return_values (procedure,
PIKA_PDB_EXECUTION_ERROR,
error);
return_vals = pika_procedure_new_return_values (procedure,
PIKA_PDB_SUCCESS,
NULL);
PIKA_VALUES_SET_IMAGE (return_vals, 1, image);
return return_vals;
}
static PikaValueArray *
ani_load (PikaProcedure *procedure,
PikaRunMode run_mode,
GFile *file,
const PikaValueArray *args,
gpointer run_data)
{
PikaValueArray *return_vals;
PikaImage *image;
GError *error = NULL;
gegl_init (NULL, NULL);
image = ani_load_image (file, FALSE,
NULL, NULL, &error);
if (! image)
return pika_procedure_new_return_values (procedure,
PIKA_PDB_EXECUTION_ERROR,
error);
return_vals = pika_procedure_new_return_values (procedure,
PIKA_PDB_SUCCESS,
NULL);
PIKA_VALUES_SET_IMAGE (return_vals, 1, image);
return return_vals;
}
static PikaValueArray *
ico_load_thumb (PikaProcedure *procedure,
GFile *file,
gint size,
const PikaValueArray *args,
gpointer run_data)
{
PikaValueArray *return_vals;
gint width;
gint height;
PikaImage *image;
GError *error = NULL;
gegl_init (NULL, NULL);
width = size;
height = size;
image = ico_load_thumbnail_image (file,
&width, &height, 0, &error);
if (! image)
return pika_procedure_new_return_values (procedure,
PIKA_PDB_EXECUTION_ERROR,
error);
return_vals = pika_procedure_new_return_values (procedure,
PIKA_PDB_SUCCESS,
NULL);
PIKA_VALUES_SET_IMAGE (return_vals, 1, image);
PIKA_VALUES_SET_INT (return_vals, 2, width);
PIKA_VALUES_SET_INT (return_vals, 3, height);
pika_value_array_truncate (return_vals, 4);
return return_vals;
}
static PikaValueArray *
ani_load_thumb (PikaProcedure *procedure,
GFile *file,
gint size,
const PikaValueArray *args,
gpointer run_data)
{
PikaValueArray *return_vals;
gint width;
gint height;
PikaImage *image;
GError *error = NULL;
gegl_init (NULL, NULL);
width = size;
height = size;
image = ani_load_image (file, TRUE,
&width, &height, &error);
if (! image)
return pika_procedure_new_return_values (procedure,
PIKA_PDB_EXECUTION_ERROR,
error);
return_vals = pika_procedure_new_return_values (procedure,
PIKA_PDB_SUCCESS,
NULL);
PIKA_VALUES_SET_IMAGE (return_vals, 1, image);
PIKA_VALUES_SET_INT (return_vals, 2, width);
PIKA_VALUES_SET_INT (return_vals, 3, height);
pika_value_array_truncate (return_vals, 4);
return return_vals;
}
static PikaValueArray *
ico_save (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
GFile *file,
const PikaValueArray *args,
gpointer run_data)
{
PikaPDBStatusType status;
GError *error = NULL;
gegl_init (NULL, NULL);
status = ico_save_image (file, image, run_mode, &error);
return pika_procedure_new_return_values (procedure, status, error);
}
static PikaValueArray *
cur_save (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
GFile *file,
const PikaValueArray *args,
gpointer run_data)
{
PikaProcedureConfig *config;
PikaPDBStatusType status;
GError *error = NULL;
gint32 *hot_spot_x = NULL;
gint32 *hot_spot_y = NULL;
gint n_hot_spot_x = 0;
gint n_hot_spot_y = 0;
gegl_init (NULL, NULL);
config = pika_procedure_create_config (procedure);
pika_procedure_config_begin_run (config, image, run_mode, args);
g_object_get (config,
"n-hot-spot-x", &n_hot_spot_x,
"n-hot-spot-y", &n_hot_spot_y,
"hot-spot-x", &hot_spot_x,
"hot-spot-y", &hot_spot_y,
NULL);
status = cur_save_image (file, image, run_mode,
&n_hot_spot_x, &hot_spot_x,
&n_hot_spot_y, &hot_spot_y,
&error);
if (status == PIKA_PDB_SUCCESS)
{
/* XXX: seems libpikaconfig is not able to serialize
* PikaInt32Array args yet anyway. Still leave this here for now,
* as reminder of missing feature when we see the warnings.
*/
g_object_set (config,
"n-hot-spot-x", n_hot_spot_x,
"n-hot-spot-y", n_hot_spot_y,
/*"hot-spot-x", hot_spot_x,*/
/*"hot-spot-y", hot_spot_y,*/
NULL);
g_free (hot_spot_x);
g_free (hot_spot_y);
}
pika_procedure_config_end_run (config, status);
g_object_unref (config);
return pika_procedure_new_return_values (procedure, status, error);
}
static PikaValueArray *
ani_save (PikaProcedure *procedure,
PikaRunMode run_mode,
PikaImage *image,
gint n_drawables,
PikaDrawable **drawables,
GFile *file,
const PikaValueArray *args,
gpointer run_data)
{
PikaProcedureConfig *config;
PikaPDBStatusType status;
GError *error = NULL;
gchar *inam = NULL;
gchar *iart = NULL;
gint jif_rate = 0;
gint32 *hot_spot_x = NULL;
gint32 *hot_spot_y = NULL;
gint n_hot_spot_x = 0;
gint n_hot_spot_y = 0;
AniFileHeader header;
AniSaveInfo ani_info;
gegl_init (NULL, NULL);
config = pika_procedure_create_config (procedure);
pika_procedure_config_begin_run (config, image, run_mode, args);
g_object_get (config,
"cursor-name", &inam,
"author-name", &iart,
"default-delay", &jif_rate,
"n-hot-spot-x", &n_hot_spot_x,
"n-hot-spot-y", &n_hot_spot_y,
"hot-spot-x", &hot_spot_x,
"hot-spot-y", &hot_spot_y,
NULL);
/* Jiffies (1/60th of a second) used if rate chunk not present. */
header.jif_rate = jif_rate;
ani_info.inam = inam;
ani_info.iart = iart;
status = ani_save_image (file, image, run_mode,
&n_hot_spot_x, &hot_spot_x,
&n_hot_spot_y, &hot_spot_y,
&header, &ani_info, &error);
if (status == PIKA_PDB_SUCCESS)
{
/* XXX: seems libpikaconfig is not able to serialize
* PikaInt32Array args yet anyway. Still leave this here for now,
* as reminder of missing feature when we see the warnings.
*/
g_object_set (config,
"cursor-name", NULL,
"author-name", NULL,
"default-delay", header.jif_rate,
"n-hot-spot-x", n_hot_spot_x,
"n-hot-spot-y", n_hot_spot_y,
/*"hot-spot-x", hot_spot_x,*/
/*"hot-spot-y", hot_spot_y,*/
NULL);
g_free (hot_spot_x);
g_free (hot_spot_y);
g_free (inam);
g_free (iart);
g_free (ani_info.inam);
g_free (ani_info.iart);
memset (&ani_info, 0, sizeof (AniSaveInfo));
}
pika_procedure_config_end_run (config, status);
g_object_unref (config);
return pika_procedure_new_return_values (procedure, status, error);
}
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,
gint bpp,
gint *length)
{
gint len = 0;
guint8 *map = NULL;
len = ico_rowstride (width, bpp) * height;
*length = len;
map = g_new0 (guint8, len);
return map;
}

134
plug-ins/file-ico/ico.h Normal file
View File

@ -0,0 +1,134 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
*
* PIKA Plug-in for Windows Icon files.
* Copyright (C) 2002 Christian Kreibich <christian@whoop.org>.
*
* 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 3 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, see <https://www.gnu.org/licenses/>.
*/
#ifndef __ICO_H__
#define __ICO_H__
#ifdef ICO_DBG
#define D(x) \
{ \
printf("ICO plugin: "); \
printf x; \
}
#else
#define D(x)
#endif
#define PLUG_IN_BINARY "file-ico"
#define PLUG_IN_ROLE "pika-file-ico"
#define ICO_PNG_MAGIC 0x474e5089
#define ICO_ALPHA_THRESHOLD 127
#define ICO_MAXBUF 4096
typedef struct _IcoFileHeader
{
guint16 reserved;
guint16 resource_type;
guint16 icon_count;
} IcoFileHeader;
typedef struct _IcoFileEntry
{
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 for ICO, x position of hot spot for CUR */
guint16 bpp; /* 1, 4, 8, 24 or 32 bits per pixel for ICO, y position of hot spot for CUR */
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 (without this header) */
guint32 x_res;
guint32 y_res;
guint32 used_clrs;
guint32 important_clrs;
} IcoFileDataHeader;
typedef struct _IcoLoadInfo
{
guint width;
guint height;
gint bpp;
gint planes;
gint offset;
gint size;
} IcoLoadInfo;
typedef struct _IcoSaveInfo
{
gint *depths;
gint *default_depths;
gboolean *compress;
GList *layers;
gint num_icons;
gboolean is_cursor;
gint *hot_spot_x;
gint *hot_spot_y;
} IcoSaveInfo;
typedef struct _AniFileHeader
{
guint32 bSizeOf; /* Number of bytes in AniFileHeader (36 bytes) */
guint32 frames; /* Number of unique icons in this cursor */
guint32 steps; /* Number of Blits before the animation cycles */
guint32 x, y; /* Reserved, must be zero. */
guint32 bpp, planes; /* Reserved, must be zero. */
guint32 jif_rate; /* Default Jiffies (1/60th of a second) if rate chunk's not present. */
guint32 flags; /* Animation Flag */
} AniFileHeader;
typedef struct _AniSaveInfo
{
gchar *inam; /* Cursor name metadata */
gchar *iart; /* Author name metadata */
} AniSaveInfo;
/* 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);
#endif /* __ICO_H__ */

View File

@ -0,0 +1,32 @@
plugin_name = 'file-ico'
plugin_sources = [
'ico-dialog.c',
'ico-load.c',
'ico-save.c',
'ico.c',
]
if platform_windows
plugin_sources += windows.compile_resources(
pika_plugins_rc,
args: [
'--define', 'ORIGINALFILENAME_STR="@0@"'.format(plugin_name+'.exe'),
'--define', 'INTERNALNAME_STR="@0@"' .format(plugin_name),
'--define', 'TOP_SRCDIR="@0@"' .format(meson.project_source_root()),
],
include_directories: [
rootInclude, appInclude,
],
)
endif
executable(plugin_name,
plugin_sources,
dependencies: [
libpikaui_dep,
libpng,
],
install: true,
install_dir: pikaplugindir / 'plug-ins' / plugin_name,
)