/* * Guillotine plug-in v0.9 by Adam D. Moss, adam@foxbox.org. 1998/09/01 */ /* 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 Spencer Kimball and Peter Mattis * * 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 . */ #include "config.h" #include #include #include "libpika/stdplugins-intl.h" #define PLUG_IN_PROC "plug-in-guillotine" typedef struct _Guillotine Guillotine; typedef struct _GuillotineClass GuillotineClass; struct _Guillotine { PikaPlugIn parent_instance; }; struct _GuillotineClass { PikaPlugInClass parent_class; }; #define GUILLOTINE_TYPE (guillotine_get_type ()) #define GUILLOTINE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GUILLOTINE_TYPE, Guillotine)) GType guillotine_get_type (void) G_GNUC_CONST; static GList * guillotine_query_procedures (PikaPlugIn *plug_in); static PikaProcedure * guillotine_create_procedure (PikaPlugIn *plug_in, const gchar *name); static PikaValueArray * guillotine_run (PikaProcedure *procedure, PikaRunMode run_mode, PikaImage *image, gint n_drawables, PikaDrawable **drawables, PikaProcedureConfig *config, gpointer run_data); static GList * guillotine (PikaImage *image, gboolean interactive); G_DEFINE_TYPE (Guillotine, guillotine, PIKA_TYPE_PLUG_IN) PIKA_MAIN (GUILLOTINE_TYPE) DEFINE_STD_SET_I18N static void guillotine_class_init (GuillotineClass *klass) { PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass); plug_in_class->query_procedures = guillotine_query_procedures; plug_in_class->create_procedure = guillotine_create_procedure; plug_in_class->set_i18n = STD_SET_I18N; } static void guillotine_init (Guillotine *film) { } static GList * guillotine_query_procedures (PikaPlugIn *plug_in) { return g_list_append (NULL, g_strdup (PLUG_IN_PROC)); } static PikaProcedure * guillotine_create_procedure (PikaPlugIn *plug_in, const gchar *name) { PikaProcedure *procedure = NULL; if (! strcmp (name, PLUG_IN_PROC)) { procedure = pika_image_procedure_new (plug_in, name, PIKA_PDB_PROC_TYPE_PLUGIN, guillotine_run, NULL, NULL); pika_procedure_set_image_types (procedure, "*"); pika_procedure_set_sensitivity_mask (procedure, PIKA_PROCEDURE_SENSITIVE_DRAWABLE | PIKA_PROCEDURE_SENSITIVE_DRAWABLES | PIKA_PROCEDURE_SENSITIVE_NO_DRAWABLES); pika_procedure_set_menu_label (procedure, _("Slice Using G_uides")); pika_procedure_add_menu_path (procedure, "/Image/[Crop]"); pika_procedure_set_documentation (procedure, _("Slice the image into subimages " "using guides"), "This function takes an image and " "slices it along its guides, creating " "new images. The original image is " "not modified.", name); pika_procedure_set_attribution (procedure, "Adam D. Moss (adam@foxbox.org)", "Adam D. Moss (adam@foxbox.org)", "1998"); PIKA_PROC_VAL_INT (procedure, "image-count", "Number of images created", "Number of images created", 0, G_MAXINT, 0, G_PARAM_READWRITE); PIKA_PROC_VAL_OBJECT_ARRAY (procedure, "images", "Output images", "Output images", PIKA_TYPE_IMAGE, G_PARAM_READWRITE); } return procedure; } static PikaValueArray * guillotine_run (PikaProcedure *procedure, PikaRunMode run_mode, PikaImage *image, gint n_drawables, PikaDrawable **drawables, PikaProcedureConfig *config, gpointer run_data) { PikaValueArray *return_vals = NULL; PikaPDBStatusType status = PIKA_PDB_SUCCESS; return_vals = pika_procedure_new_return_values (procedure, status, NULL); if (status == PIKA_PDB_SUCCESS) { GList *image_list; GList *list; PikaImage **images; gint num_images; gint i; pika_progress_init (_("Guillotine")); image_list = guillotine (image, run_mode == PIKA_RUN_INTERACTIVE); num_images = g_list_length (image_list); images = g_new (PikaImage *, num_images); for (list = image_list, i = 0; list; list = g_list_next (list), i++) { images[i] = g_object_ref (list->data); } g_list_free (image_list); PIKA_VALUES_SET_INT (return_vals, 1, num_images); PIKA_VALUES_TAKE_OBJECT_ARRAY (return_vals, 2, PIKA_TYPE_IMAGE, images, num_images); if (run_mode == PIKA_RUN_INTERACTIVE) pika_displays_flush (); } return return_vals; } static gint guide_sort_func (gconstpointer a, gconstpointer b) { return GPOINTER_TO_INT (a) - GPOINTER_TO_INT (b); } static GList * guillotine (PikaImage *image, gboolean interactive) { GList *images = NULL; gint guide; gint image_width; gint image_height; gboolean guides_found = FALSE; GList *hguides, *hg; GList *vguides, *vg; image_width = pika_image_get_width (image); image_height = pika_image_get_height (image); hguides = g_list_append (NULL, GINT_TO_POINTER (0)); hguides = g_list_append (hguides, GINT_TO_POINTER (image_height)); vguides = g_list_append (NULL, GINT_TO_POINTER (0)); vguides = g_list_append (vguides, GINT_TO_POINTER (image_width)); for (guide = pika_image_find_next_guide (image, 0); guide > 0; guide = pika_image_find_next_guide (image, guide)) { gint position = pika_image_get_guide_position (image, guide); switch (pika_image_get_guide_orientation (image, guide)) { case PIKA_ORIENTATION_HORIZONTAL: if (! g_list_find (hguides, GINT_TO_POINTER (position))) { hguides = g_list_insert_sorted (hguides, GINT_TO_POINTER (position), guide_sort_func); guides_found = TRUE; } break; case PIKA_ORIENTATION_VERTICAL: if (! g_list_find (vguides, GINT_TO_POINTER (position))) { vguides = g_list_insert_sorted (vguides, GINT_TO_POINTER (position), guide_sort_func); guides_found = TRUE; } break; case PIKA_ORIENTATION_UNKNOWN: g_assert_not_reached (); break; } } if (guides_found) { GFile *file; gint h, v, hpad, vpad; gint x, y; gchar *hformat; gchar *format; file = pika_image_get_file (image); if (! file) file = g_file_new_for_uri (_("Untitled")); /* get the number horizontal and vertical slices */ h = g_list_length (hguides); v = g_list_length (vguides); /* need the number of digits of h and v for the padding */ hpad = log10(h) + 1; vpad = log10(v) + 1; /* format for the x-y coordinates in the filename */ hformat = g_strdup_printf ("%%0%i", MAX (hpad, vpad)); format = g_strdup_printf ("-%si-%si", hformat, hformat); /* Do the actual dup'ing and cropping... this isn't a too naive a * way to do this since we got copy-on-write tiles, either. */ for (y = 0, hg = hguides; hg && hg->next; y++, hg = hg->next) { for (x = 0, vg = vguides; vg && vg->next; x++, vg = vg->next) { PikaImage *new_image = pika_image_duplicate (image); GString *new_uri; GFile *new_file; gchar *fileextension; gchar *fileindex; if (! new_image) { g_warning ("Couldn't create new image."); g_free (hformat); g_free (format); return images; } pika_image_undo_disable (new_image); pika_image_crop (new_image, GPOINTER_TO_INT (vg->next->data) - GPOINTER_TO_INT (vg->data), GPOINTER_TO_INT (hg->next->data) - GPOINTER_TO_INT (hg->data), GPOINTER_TO_INT (vg->data), GPOINTER_TO_INT (hg->data)); new_uri = g_string_new (g_file_get_uri (file)); /* show the rough coordinates of the image in the title */ fileindex = g_strdup_printf (format, x, y); /* get the position of the file extension - last . in filename */ fileextension = strrchr (new_uri->str, '.'); if (fileextension) { gint pos; /* Truncate the extension. */ pos = fileextension - new_uri->str; g_string_truncate (new_uri, pos); } /* insert the coordinates before the extension */ g_string_append (new_uri, fileindex); g_free (fileindex); g_string_append (new_uri, ".xcf"); new_file = g_file_new_for_uri (new_uri->str); g_string_free (new_uri, TRUE); pika_image_set_file (new_image, new_file); g_object_unref (new_file); while ((guide = pika_image_find_next_guide (new_image, 0))) pika_image_delete_guide (new_image, guide); pika_image_undo_enable (new_image); if (interactive) pika_display_new (new_image); images = g_list_prepend (images, GINT_TO_POINTER (new_image)); } } g_object_unref (file); g_free (hformat); g_free (format); } g_list_free (hguides); g_list_free (vguides); return g_list_reverse (images); }