/* * "$Id$" * * SGI image file plug-in for the GIMP. * * Copyright 1997-1998 Michael Sweet (mike@easysw.com) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Contents: * * main() - Main entry - just call gimp_main()... * query() - Respond to a plug-in query... * run() - Run the plug-in... * load_image() - Load a PNG image into a new image window. * save_image() - Save the specified image to a PNG file. * save_close_callback() - Close the save dialog window. * save_ok_callback() - Destroy the save dialog and save the image. * save_compression_callback() - Update the image compression level. * save_dialog() - Pop up the save dialog. * * Revision History: * * $Log$ * Revision 1.13 1999/10/20 01:45:41 neo * the rest of the save plug-ins !? * * * --Sven * * Revision 1.12 1999/04/23 06:32:41 asbjoer * use MAIN macro * * Revision 1.11 1999/04/15 21:49:06 yosh * * applied gimp-lecorfec-99041[02]-0, changes follow * * * plug-ins/FractalExplorer/Dialogs.h (make_color_map): * replaced free with g_free to fix segfault. * * * plug-ins/Lighting/lighting_preview.c (compute_preview): * allocate xpostab and ypostab only when needed (it could also be * allocated on stack with a compilation-fixed size like MapObject). * It avoids to lose some Kb on each preview :) * Also reindented (unfortunate C-c C-q) some other lines. * * * plug-ins/Lighting/lighting_main.c (run): * release allocated postabs. * * * plug-ins/Lighting/lighting_ui.c: * callbacks now have only one argument because gck widget use * gtk_signal_connect_object. Caused segfault for scale widget. * * * plug-ins/autocrop/autocrop.c (doit): * return if image has only background (thus fixing a segfault). * * * plug-ins/emboss/emboss.c (pluginCore, emboss_do_preview): * replaced malloc/free with g_malloc/g_free (unneeded, but * shouldn't everyone use glib calls ? :) * * * plug-ins/flame/flame.c : * replaced a segfaulting free, and several harmless malloc/free pairs. * * * plug-ins/flame/megawidget.c (mw_preview_build): * replaced harmless malloc/free pair. * Note : mwp->bits is malloc'ed but seems to be never freed. * * * plug-ins/fractaltrace/fractaltrace.c (pixels_free): * replaced a bunch of segfaulting free. * (pixels_get, dialog_show): replaced gtk_signal_connect_object * with gtk_signal_connect to accomodate callbacks (caused STRANGE * dialog behaviour, coz you destroyed buttons one by one). * * * plug-ins/illusion/illusion.c (dialog): * same gtk_signal_connect_object replacement for same reasons. * * * plug-ins/libgck/gck/gckcolor.c : * changed all gck_rgb_to_color* functions to use a static GdkColor * instead of a malloc'ed area. Provided reentrant functions with * the old behaviour (gck_rgb_to_color*_r). Made some private functions * static, too. * gck_rgb_to_gdkcolor now use the new functions while * gck_rgb_to_gdkcolor_r is the reentrant version. * Also affected by this change: gck_gc_set_foreground and * gck_gc_set_background (no more free(color)). * * * plug-ins/libgck/gck/gckcolor.h : * added the gck_rgb_to_gdkcolor_r proto. * * * plug-ins/lic/lic.c (ok_button_clicked, cancel_button_clicked) : * segfault on gtk_widget_destroy, now calls gtk_main_quit. * (dialog_destroy) : segfault on window closure when called by * "destroy" event. Now called by "delete_event". * * * plug-ins/megawidget/megawidget.c (mw_preview_build): * replaced harmless malloc/free pair. * Note : mwp->bits is malloc'ed but seems to be never freed. * * * plug-ins/png/png.c (load_image): * replaced 2 segfaulting free. * * * plug-ins/print/print-ps.c (ps_print): * replaced a segfaulting free (called many times :). * * * plug-ins/sgi/sgi.c (load_image, save_image): * replaced a bunch of segfaulting free, and did some harmless * inits to avoid a few gcc warnings. * * * plug-ins/wind/wind.c (render_wind): * replaced a segfaulting free. * (render_blast): replaced harmless malloc/free pair. * * * plug-ins/bmp/bmpread.c (ReadImage): * yet another free()/g_free() problem fixed. * * * plug-ins/exchange/exchange.c (real_exchange): * ditto. * * * plug-ins/fp/fp.h: added Frames_Check_Button_In_A_Box proto. * * plug-ins/fp/fp_gtk.c: closing subdialogs via window manager * wasn't handled, thus leading to errors and crashes. * Now delete_event signals the dialog control button * to close a dialog with the good way. * * * plug-ins/ifscompose/ifscompose.c (value_pair_create): * tried to set events mask on scale widget (a NO_WINDOW widget). * * * plug-ins/png/png.c (save_image): * Replaced 2 free() with g_free() for g_malloc'ed memory. * Mysteriously I corrected the loading bug but not the saving one :) * * -Yosh * * Revision 1.10 1999/01/15 17:34:37 unammx * 1999-01-15 Federico Mena Quintero * * * Updated gtk_toggle_button_set_state() to * gtk_toggle_button_set_active() in all the files. * * Revision 1.9 1998/06/06 23:22:19 yosh * * adding Lighting plugin * * * updated despeckle, png, sgi, and sharpen * * -Yosh * * Revision 1.5 1998/05/17 16:01:33 mike * Removed signal handler stuff used for debugging. * Added gtk_rc_parse(). * Removed extra variables. * * Revision 1.4 1998/04/23 17:40:49 mike * Updated to support 16-bit image data. * * Revision 1.3 1997/11/14 17:17:59 mike * Updated to dynamically allocate return params in the run() function. * Added warning message about advanced RLE compression not being supported * by SGI. * * Revision 1.2 1997/07/25 20:44:05 mike * Fixed image_load_sgi load error bug (causes GIMP hang/crash). * * Revision 1.1 1997/06/18 00:55:28 mike * Initial revision */ #include #include #include #include "sgi.h" /* SGI image library definitions */ #include #include #include /* * Constants... */ #define PLUG_IN_VERSION "1.1.1 - 17 May 1998" /* * Local functions... */ static void query (void); static void run (char *, int, GParam *, int *, GParam **); static gint32 load_image (char *); static gint save_image (char *, gint32, gint32); static void init_gtk (void); static gint save_dialog (void); static void save_close_callback (GtkWidget *, gpointer); static void save_ok_callback (GtkWidget *, gpointer); static void save_compression_callback (GtkWidget *, gpointer); /* * Globals... */ GPlugInInfo PLUG_IN_INFO = { NULL, /* init_proc */ NULL, /* quit_proc */ query, /* query_proc */ run, /* run_proc */ }; int compression = SGI_COMP_RLE; int runme = FALSE; /* * 'main()' - Main entry - just call gimp_main()... */ MAIN() /* * 'query()' - Respond to a plug-in query... */ static void query(void) { static GParamDef load_args[] = { { PARAM_INT32, "run_mode", "Interactive, non-interactive" }, { PARAM_STRING, "filename", "The name of the file to load" }, { PARAM_STRING, "raw_filename", "The name of the file to load" }, }; static GParamDef load_return_vals[] = { { PARAM_IMAGE, "image", "Output image" }, }; static int nload_args = sizeof (load_args) / sizeof (load_args[0]); static int nload_return_vals = sizeof (load_return_vals) / sizeof (load_return_vals[0]); static GParamDef save_args[] = { { PARAM_INT32, "run_mode", "Interactive, non-interactive" }, { PARAM_IMAGE, "image", "Input image" }, { PARAM_DRAWABLE, "drawable", "Drawable to save" }, { PARAM_STRING, "filename", "The name of the file to save the image in" }, { PARAM_STRING, "raw_filename", "The name of the file to save the image in" }, { PARAM_INT32, "compression", "Compression level (0 = none, 1 = RLE, 2 = ARLE)" } }; static int nsave_args = sizeof (save_args) / sizeof (save_args[0]); gimp_install_procedure("file_sgi_load", "Loads files in SGI image file format", "This plug-in loads SGI image files.", "Michael Sweet ", "Copyright 1997-1998 by Michael Sweet", PLUG_IN_VERSION, "/SGI", NULL, PROC_PLUG_IN, nload_args, nload_return_vals, load_args, load_return_vals); gimp_install_procedure("file_sgi_save", "Saves files in SGI image file format", "This plug-in saves SGI image files.", "Michael Sweet ", "Copyright 1997-1998 by Michael Sweet", PLUG_IN_VERSION, "/SGI", "RGB*,GRAY*", PROC_PLUG_IN, nsave_args, 0, save_args, NULL); gimp_register_magic_load_handler("file_sgi_load", "rgb,bw,sgi,icon", "", "0,short,474"); gimp_register_save_handler("file_sgi_save", "rgb,bw,sgi,icon", ""); } /* * 'run()' - Run the plug-in... */ static void run (char *name, /* I - Name of filter program. */ int nparams, /* I - Number of parameters passed in */ GParam *param, /* I - Parameter values */ int *nreturn_vals, /* O - Number of return values */ GParam **return_vals) /* O - Return values */ { GParam *values; /* Return values */ GRunModeType run_mode; gint32 image_ID; gint32 drawable_ID; GimpExportReturnType export = EXPORT_CANCEL; /* * Initialize parameter data... */ values = g_new(GParam, 2); values[0].type = PARAM_STATUS; values[0].data.d_status = STATUS_SUCCESS; *return_vals = values; /* * Load or save an image... */ if (strcmp(name, "file_sgi_load") == 0) { *nreturn_vals = 2; image_ID = load_image(param[1].data.d_string); if (image_ID != -1) { values[1].type = PARAM_IMAGE; values[1].data.d_image = image_ID; } else values[0].data.d_status = STATUS_EXECUTION_ERROR; } else if (strcmp (name, "file_sgi_save") == 0) { *nreturn_vals = 1; run_mode = param[0].data.d_int32; image_ID = param[1].data.d_int32; drawable_ID = param[2].data.d_int32; /* eventually export the image */ switch (run_mode) { case RUN_INTERACTIVE: case RUN_WITH_LAST_VALS: init_gtk (); export = gimp_export_image (&image_ID, &drawable_ID, "SGI", (CAN_HANDLE_RGB | CAN_HANDLE_GRAY | CAN_HANDLE_ALPHA)); if (export == EXPORT_CANCEL) { values[0].data.d_status = STATUS_EXECUTION_ERROR; return; } break; default: break; } switch (run_mode) { case RUN_INTERACTIVE : /* * Possibly retrieve data... */ gimp_get_data("file_sgi_save", &compression); /* * Then acquire information with a dialog... */ if (!save_dialog()) return; break; case RUN_NONINTERACTIVE : /* * Make sure all the arguments are there! */ if (nparams != 6) values[0].data.d_status = STATUS_CALLING_ERROR; else { compression = param[5].data.d_int32; if (compression < 0 || compression > 2) values[0].data.d_status = STATUS_CALLING_ERROR; }; break; case RUN_WITH_LAST_VALS : /* * Possibly retrieve data... */ gimp_get_data ("file_sgi_save", &compression); break; default : break; }; if (values[0].data.d_status == STATUS_SUCCESS) { if (save_image(param[3].data.d_string, image_ID, drawable_ID)) gimp_set_data("file_sgi_save", &compression, sizeof(compression)); else values[0].data.d_status = STATUS_EXECUTION_ERROR; }; if (export == EXPORT_EXPORT) gimp_image_delete (image_ID); } else values[0].data.d_status = STATUS_EXECUTION_ERROR; } /* * 'load_image()' - Load a PNG image into a new image window. */ static gint32 load_image (char *filename) /* I - File to load */ { int i, /* Looping var */ x, /* Current X coordinate */ y, /* Current Y coordinate */ image_type, /* Type of image */ layer_type, /* Type of drawable/layer */ tile_height, /* Height of tile in GIMP */ count; /* Count of rows to put in image */ sgi_t *sgip; /* File pointer */ gint32 image, /* Image */ layer; /* Layer */ GDrawable *drawable; /* Drawable for layer */ GPixelRgn pixel_rgn; /* Pixel region for layer */ guchar **pixels, /* Pixel rows */ *pixel, /* Pixel data */ *pptr; /* Current pixel */ unsigned short **rows; /* SGI image data */ char progress[255]; /* Title for progress display... */ /* * Open the file for reading... */ sgip = sgiOpen(filename, SGI_READ, 0, 0, 0, 0, 0); if (sgip == NULL) { g_print("can't open image file\n"); gimp_quit(); }; if (strrchr(filename, '/') != NULL) sprintf(progress, "Loading %s:", strrchr(filename, '/') + 1); else sprintf(progress, "Loading %s:", filename); gimp_progress_init(progress); /* * Get the image dimensions and create the image... */ image_type = 0; /* shut up warnings */ layer_type = 0; switch (sgip->zsize) { case 1 : /* Grayscale */ image_type = GRAY; layer_type = GRAY_IMAGE; break; case 2 : /* Grayscale + alpha */ image_type = GRAY; layer_type = GRAYA_IMAGE; break; case 3 : /* RGB */ image_type = RGB; layer_type = RGB_IMAGE; break; case 4 : /* RGBA */ image_type = RGB; layer_type = RGBA_IMAGE; break; }; image = gimp_image_new(sgip->xsize, sgip->ysize, image_type); if (image == -1) { g_print("can't allocate new image\n"); gimp_quit(); }; gimp_image_set_filename(image, filename); /* * Create the "background" layer to hold the image... */ layer = gimp_layer_new(image, "Background", sgip->xsize, sgip->ysize, layer_type, 100, NORMAL_MODE); gimp_image_add_layer(image, layer, 0); /* * Get the drawable and set the pixel region for our load... */ drawable = gimp_drawable_get(layer); gimp_pixel_rgn_init(&pixel_rgn, drawable, 0, 0, drawable->width, drawable->height, TRUE, FALSE); /* * Temporary buffers... */ tile_height = gimp_tile_height(); pixel = g_new(guchar, tile_height * sgip->xsize * sgip->zsize); pixels = g_new(guchar *, tile_height); for (i = 0; i < tile_height; i ++) pixels[i] = pixel + sgip->xsize * sgip->zsize * i; rows = g_new(unsigned short *, sgip->zsize); rows[0] = g_new(unsigned short, sgip->xsize * sgip->zsize); for (i = 1; i < sgip->zsize; i ++) rows[i] = rows[0] + i * sgip->xsize; /* * Load the image... */ for (y = 0, count = 0; y < sgip->ysize; y ++, count ++) { if (count >= tile_height) { gimp_pixel_rgn_set_rect(&pixel_rgn, pixel, 0, y - count, drawable->width, count); count = 0; gimp_progress_update((double)y / (double)sgip->ysize); }; for (i = 0; i < sgip->zsize; i ++) if (sgiGetRow(sgip, rows[i], sgip->ysize - 1 - y, i) < 0) printf("sgiGetRow(sgip, rows[i], %d, %d) failed!\n", sgip->ysize - 1 - y, i); if (sgip->bpp == 1) { /* * 8-bit (unsigned) pixels... */ for (x = 0, pptr = pixels[count]; x < sgip->xsize; x ++) for (i = 0; i < sgip->zsize; i ++, pptr ++) *pptr = rows[i][x]; } else { /* * 16-bit (unsigned) pixels... */ for (x = 0, pptr = pixels[count]; x < sgip->xsize; x ++) for (i = 0; i < sgip->zsize; i ++, pptr ++) *pptr = rows[i][x] >> 8; }; }; /* * Do the last n rows (count always > 0) */ gimp_pixel_rgn_set_rect(&pixel_rgn, pixel, 0, y - count, drawable->width, count); /* * Done with the file... */ sgiClose(sgip); g_free(pixel); g_free(pixels); g_free(rows[0]); g_free(rows); /* * Update the display... */ gimp_drawable_flush(drawable); gimp_drawable_detach(drawable); return (image); } /* * 'save_image()' - Save the specified image to a PNG file. */ static gint save_image (char *filename, /* I - File to save to */ gint32 image_ID, /* I - Image to save */ gint32 drawable_ID) /* I - Current drawable */ { int i, j, /* Looping var */ x, /* Current X coordinate */ y, /* Current Y coordinate */ image_type, /* Type of image */ layer_type, /* Type of drawable/layer */ tile_height, /* Height of tile in GIMP */ count, /* Count of rows to put in image */ zsize; /* Number of channels in file */ sgi_t *sgip; /* File pointer */ GDrawable *drawable; /* Drawable for layer */ GPixelRgn pixel_rgn; /* Pixel region for layer */ guchar **pixels, /* Pixel rows */ *pixel, /* Pixel data */ *pptr; /* Current pixel */ unsigned short **rows; /* SGI image data */ char progress[255]; /* Title for progress display... */ /* * Get the drawable for the current image... */ drawable = gimp_drawable_get(drawable_ID); gimp_pixel_rgn_init(&pixel_rgn, drawable, 0, 0, drawable->width, drawable->height, FALSE, FALSE); zsize = 0; switch (gimp_drawable_type(drawable_ID)) { case GRAY_IMAGE : zsize = 1; break; case GRAYA_IMAGE : zsize = 2; break; case RGB_IMAGE : zsize = 3; break; case RGBA_IMAGE : zsize = 4; break; }; /* * Open the file for writing... */ sgip = sgiOpen(filename, SGI_WRITE, compression, 1, drawable->width, drawable->height, zsize); if (sgip == NULL) { g_print("can't create image file\n"); gimp_quit(); }; if (strrchr(filename, '/') != NULL) sprintf(progress, "Saving %s:", strrchr(filename, '/') + 1); else sprintf(progress, "Saving %s:", filename); gimp_progress_init(progress); /* * Allocate memory for "tile_height" rows... */ tile_height = gimp_tile_height(); pixel = g_new(guchar, tile_height * drawable->width * zsize); pixels = g_new(guchar *, tile_height); for (i = 0; i < tile_height; i ++) pixels[i]= pixel + drawable->width * zsize * i; rows = g_new(unsigned short *, sgip->zsize); rows[0] = g_new(unsigned short, sgip->xsize * sgip->zsize); for (i = 1; i < sgip->zsize; i ++) rows[i] = rows[0] + i * sgip->xsize; /* * Save the image... */ for (y = 0; y < drawable->height; y += count) { /* * Grab more pixel data... */ if ((y + tile_height) >= drawable->height) count = drawable->height - y; else count = tile_height; gimp_pixel_rgn_get_rect(&pixel_rgn, pixel, 0, y, drawable->width, count); /* * Convert to shorts and write each color plane separately... */ for (i = 0, pptr = pixels[0]; i < count; i ++) { for (x = 0; x < drawable->width; x ++) for (j = 0; j < zsize; j ++, pptr ++) rows[j][x] = *pptr; for (j = 0; j < zsize; j ++) sgiPutRow(sgip, rows[j], drawable->height - 1 - y - i, j); }; gimp_progress_update((double)y / (double)drawable->height); }; /* * Done with the file... */ sgiClose(sgip); g_free(pixel); g_free(pixels); g_free(rows[0]); g_free(rows); return (1); } /* * 'save_close_callback()' - Close the save dialog window. */ static void save_close_callback (GtkWidget *w, /* I - Close button */ gpointer data) /* I - Callback data */ { gtk_main_quit(); } /* * 'save_ok_callback()' - Destroy the save dialog and save the image. */ static void save_ok_callback (GtkWidget *w, /* I - OK button */ gpointer data) /* I - Callback data */ { runme = TRUE; gtk_widget_destroy(GTK_WIDGET(data)); } /* * 'save_compression_callback()' - Update the image compression level. */ static void save_compression_callback (GtkWidget *w, /* I - Compression button */ gpointer data) /* I - Callback data */ { if (GTK_TOGGLE_BUTTON(w)->active) compression = (long)data; } static void init_gtk () { gchar **argv; gint argc; argc = 1; argv = g_new (gchar *, 1); argv[0] = g_strdup ("sgi"); gtk_init (&argc, &argv); gtk_rc_parse (gimp_gtkrc ()); } /* * 'save_dialog()' - Pop up the save dialog. */ static gint save_dialog(void) { int i; /* Looping var */ GtkWidget *dlg, /* Dialog window */ *button, /* OK/cancel/compression buttons */ *frame, /* Frame for dialog */ *vbox; /* Box for compression types */ GSList *group; /* Button grouping for compression */ static char *types[] = /* Compression types... */ { "No Compression", "RLE Compression", "Aggressive RLE\n(Not supported by SGI)" }; /* * Open a dialog window... */ dlg = gtk_dialog_new(); gtk_window_set_title(GTK_WINDOW(dlg), "SGI - " PLUG_IN_VERSION); gtk_window_set_wmclass(GTK_WINDOW(dlg), "sgi", "Gimp"); gtk_window_position(GTK_WINDOW(dlg), GTK_WIN_POS_MOUSE); gtk_signal_connect(GTK_OBJECT(dlg), "destroy", (GtkSignalFunc)save_close_callback, NULL); /* * OK/cancel buttons... */ button = gtk_button_new_with_label("OK"); GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT); gtk_signal_connect(GTK_OBJECT (button), "clicked", (GtkSignalFunc)save_ok_callback, dlg); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dlg)->action_area), button, TRUE, TRUE, 0); gtk_widget_grab_default(button); gtk_widget_show(button); button = gtk_button_new_with_label ("Cancel"); GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT); gtk_signal_connect_object(GTK_OBJECT(button), "clicked", (GtkSignalFunc)gtk_widget_destroy, GTK_OBJECT(dlg)); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dlg)->action_area), button, TRUE, TRUE, 0); gtk_widget_show(button); /* * Compression type... */ frame = gtk_frame_new("Parameter Settings"); gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN); gtk_container_border_width(GTK_CONTAINER(frame), 10); gtk_box_pack_start(GTK_BOX (GTK_DIALOG(dlg)->vbox), frame, TRUE, TRUE, 0); vbox = gtk_vbox_new(FALSE, 0); gtk_container_border_width(GTK_CONTAINER(vbox), 4); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_widget_show(vbox); group = NULL; for (i = 0; i < (sizeof(types) / sizeof(types[0])); i ++) { button = gtk_radio_button_new_with_label(group, types[i]); group = gtk_radio_button_group(GTK_RADIO_BUTTON(button)); if (i == compression) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); gtk_signal_connect(GTK_OBJECT(button), "toggled", (GtkSignalFunc)save_compression_callback, (gpointer)((long)i)); gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0); gtk_widget_show(button); }; /* * Show everything and go... */ gtk_widget_show(frame); gtk_widget_show(dlg); gtk_main(); gdk_flush(); return (runme); } /* * End of "$Id$". */