1 /*
2 * SGI image file plug-in for GIMP.
3 *
4 * Copyright 1997-1998 Michael Sweet (mike@easysw.com)
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the Free
8 * Software Foundation; either version 3 of the License, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 *
19 * Contents:
20 *
21 * main() - Main entry - just call gimp_main()...
22 * query() - Respond to a plug-in query...
23 * run() - Run the plug-in...
24 * load_image() - Load a PNG image into a new image window.
25 * save_image() - Export the specified image to a PNG file.
26 * save_ok_callback() - Destroy the export dialog and export the image.
27 * save_dialog() - Pop up the export dialog.
28 *
29 */
30
31 #include "config.h"
32
33 #include <string.h>
34
35 #include <libgimp/gimp.h>
36 #include <libgimp/gimpui.h>
37
38 #include "sgi-lib.h"
39
40 #include "libgimp/stdplugins-intl.h"
41
42
43 /*
44 * Constants...
45 */
46
47 #define LOAD_PROC "file-sgi-load"
48 #define SAVE_PROC "file-sgi-save"
49 #define PLUG_IN_BINARY "file-sgi"
50 #define PLUG_IN_ROLE "gimp-file-sgi"
51 #define PLUG_IN_VERSION "1.1.1 - 17 May 1998"
52
53
54 /*
55 * Local functions...
56 */
57
58 static void query (void);
59 static void run (const gchar *name,
60 gint nparams,
61 const GimpParam *param,
62 gint *nreturn_vals,
63 GimpParam **return_vals);
64
65 static gint32 load_image (const gchar *filename,
66 GError **error);
67 static gint save_image (const gchar *filename,
68 gint32 image_ID,
69 gint32 drawable_ID,
70 GError **error);
71
72 static gboolean save_dialog (void);
73
74 /*
75 * Globals...
76 */
77
78 const GimpPlugInInfo PLUG_IN_INFO =
79 {
80 NULL, /* init_proc */
81 NULL, /* quit_proc */
82 query, /* query_proc */
83 run, /* run_proc */
84 };
85
86 static gint compression = SGI_COMP_RLE;
87
88
MAIN()89 MAIN ()
90
91 static void
92 query (void)
93 {
94 static const GimpParamDef load_args[] =
95 {
96 { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
97 { GIMP_PDB_STRING, "filename", "The name of the file to load" },
98 { GIMP_PDB_STRING, "raw-filename", "The name of the file to load" },
99 };
100 static const GimpParamDef load_return_vals[] =
101 {
102 { GIMP_PDB_IMAGE, "image", "Output image" },
103 };
104
105 static const GimpParamDef save_args[] =
106 {
107 { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
108 { GIMP_PDB_IMAGE, "image", "Input image" },
109 { GIMP_PDB_DRAWABLE, "drawable", "Drawable to export" },
110 { GIMP_PDB_STRING, "filename", "The name of the file to export the image in" },
111 { GIMP_PDB_STRING, "raw-filename", "The name of the file to export the image in" },
112 { GIMP_PDB_INT32, "compression", "Compression level (0 = none, 1 = RLE, 2 = ARLE)" }
113 };
114
115 gimp_install_procedure (LOAD_PROC,
116 "Loads files in SGI image file format",
117 "This plug-in loads SGI image files.",
118 "Michael Sweet <mike@easysw.com>",
119 "Copyright 1997-1998 by Michael Sweet",
120 PLUG_IN_VERSION,
121 N_("Silicon Graphics IRIS image"),
122 NULL,
123 GIMP_PLUGIN,
124 G_N_ELEMENTS (load_args),
125 G_N_ELEMENTS (load_return_vals),
126 load_args,
127 load_return_vals);
128
129 gimp_register_file_handler_mime (LOAD_PROC, "image/x-sgi");
130 gimp_register_magic_load_handler (LOAD_PROC,
131 "sgi,rgb,rgba,bw,icon",
132 "",
133 "0,short,474");
134
135 gimp_install_procedure (SAVE_PROC,
136 "Exports files in SGI image file format",
137 "This plug-in exports SGI image files.",
138 "Michael Sweet <mike@easysw.com>",
139 "Copyright 1997-1998 by Michael Sweet",
140 PLUG_IN_VERSION,
141 N_("Silicon Graphics IRIS image"),
142 "RGB*, GRAY*, INDEXED*",
143 GIMP_PLUGIN,
144 G_N_ELEMENTS (save_args),
145 0,
146 save_args,
147 NULL);
148
149 gimp_register_file_handler_mime (SAVE_PROC, "image/x-sgi");
150 gimp_register_save_handler (SAVE_PROC, "sgi,rgb,rgba,bw,icon", "");
151 }
152
153 static void
run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)154 run (const gchar *name,
155 gint nparams,
156 const GimpParam *param,
157 gint *nreturn_vals,
158 GimpParam **return_vals)
159 {
160 static GimpParam values[2];
161 GimpRunMode run_mode;
162 GimpPDBStatusType status = GIMP_PDB_SUCCESS;
163 gint32 image_ID;
164 gint32 drawable_ID;
165 GimpExportReturn export = GIMP_EXPORT_CANCEL;
166 GError *error = NULL;
167
168 INIT_I18N ();
169 gegl_init (NULL, NULL);
170
171 run_mode = param[0].data.d_int32;
172
173 *nreturn_vals = 1;
174 *return_vals = values;
175
176 values[0].type = GIMP_PDB_STATUS;
177 values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
178
179 if (strcmp (name, LOAD_PROC) == 0)
180 {
181 image_ID = load_image (param[1].data.d_string, &error);
182
183 if (image_ID != -1)
184 {
185 *nreturn_vals = 2;
186 values[1].type = GIMP_PDB_IMAGE;
187 values[1].data.d_image = image_ID;
188 }
189 else
190 {
191 status = GIMP_PDB_EXECUTION_ERROR;
192 }
193 }
194 else if (strcmp (name, SAVE_PROC) == 0)
195 {
196 image_ID = param[1].data.d_int32;
197 drawable_ID = param[2].data.d_int32;
198
199 /* eventually export the image */
200 switch (run_mode)
201 {
202 case GIMP_RUN_INTERACTIVE:
203 case GIMP_RUN_WITH_LAST_VALS:
204 gimp_ui_init (PLUG_IN_BINARY, FALSE);
205
206 export = gimp_export_image (&image_ID, &drawable_ID, "SGI",
207 GIMP_EXPORT_CAN_HANDLE_RGB |
208 GIMP_EXPORT_CAN_HANDLE_GRAY |
209 GIMP_EXPORT_CAN_HANDLE_INDEXED |
210 GIMP_EXPORT_CAN_HANDLE_ALPHA);
211
212 if (export == GIMP_EXPORT_CANCEL)
213 {
214 values[0].data.d_status = GIMP_PDB_CANCEL;
215 return;
216 }
217 break;
218 default:
219 break;
220 }
221
222 switch (run_mode)
223 {
224 case GIMP_RUN_INTERACTIVE:
225 /*
226 * Possibly retrieve data...
227 */
228 gimp_get_data (SAVE_PROC, &compression);
229
230 /*
231 * Then acquire information with a dialog...
232 */
233 if (!save_dialog ())
234 status = GIMP_PDB_CANCEL;
235 break;
236
237 case GIMP_RUN_NONINTERACTIVE:
238 /*
239 * Make sure all the arguments are there!
240 */
241 if (nparams != 6)
242 {
243 status = GIMP_PDB_CALLING_ERROR;
244 }
245 else
246 {
247 compression = param[5].data.d_int32;
248
249 if (compression < 0 || compression > 2)
250 status = GIMP_PDB_CALLING_ERROR;
251 };
252 break;
253
254 case GIMP_RUN_WITH_LAST_VALS:
255 /*
256 * Possibly retrieve data...
257 */
258 gimp_get_data (SAVE_PROC, &compression);
259 break;
260
261 default:
262 break;
263 };
264
265 if (status == GIMP_PDB_SUCCESS)
266 {
267 if (save_image (param[3].data.d_string, image_ID, drawable_ID,
268 &error))
269 {
270 gimp_set_data (SAVE_PROC, &compression, sizeof (compression));
271 }
272 else
273 {
274 status = GIMP_PDB_EXECUTION_ERROR;
275 }
276 }
277
278 if (export == GIMP_EXPORT_EXPORT)
279 gimp_image_delete (image_ID);
280 }
281 else
282 {
283 status = GIMP_PDB_CALLING_ERROR;
284 }
285
286 if (status != GIMP_PDB_SUCCESS && error)
287 {
288 *nreturn_vals = 2;
289 values[1].type = GIMP_PDB_STRING;
290 values[1].data.d_string = error->message;
291 }
292
293 values[0].data.d_status = status;
294 }
295
296
297 /*
298 * 'load_image()' - Load a PNG image into a new image window.
299 */
300
301 static gint32
load_image(const gchar * filename,GError ** error)302 load_image (const gchar *filename,
303 GError **error)
304 {
305 gint i, /* Looping var */
306 x, /* Current X coordinate */
307 y, /* Current Y coordinate */
308 image_type, /* Type of image */
309 layer_type, /* Type of drawable/layer */
310 tile_height, /* Height of tile in GIMP */
311 count, /* Count of rows to put in image */
312 bytes; /* Number of channels to use */
313 sgi_t *sgip; /* File pointer */
314 gint32 image, /* Image */
315 layer; /* Layer */
316 GeglBuffer *buffer; /* Buffer for layer */
317 guchar **pixels, /* Pixel rows */
318 *pptr; /* Current pixel */
319 gushort **rows; /* SGI image data */
320
321 /*
322 * Open the file for reading...
323 */
324
325 gimp_progress_init_printf (_("Opening '%s'"),
326 gimp_filename_to_utf8 (filename));
327
328 sgip = sgiOpen (filename, SGI_READ, 0, 0, 0, 0, 0);
329 if (sgip == NULL)
330 {
331 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
332 _("Could not open '%s' for reading."),
333 gimp_filename_to_utf8 (filename));
334 free (sgip);
335 return -1;
336 };
337
338 /*
339 * Get the image dimensions and create the image...
340 */
341
342 /* Sanitize dimensions (note that they are unsigned short and can
343 * thus never be larger than GIMP_MAX_IMAGE_SIZE
344 */
345 if (sgip->xsize == 0 /*|| sgip->xsize > GIMP_MAX_IMAGE_SIZE*/)
346 {
347 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
348 _("Invalid width: %hu"), sgip->xsize);
349 free (sgip);
350 return -1;
351 }
352
353 if (sgip->ysize == 0 /*|| sgip->ysize > GIMP_MAX_IMAGE_SIZE*/)
354 {
355 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
356 _("Invalid height: %hu"), sgip->ysize);
357 free (sgip);
358 return -1;
359 }
360
361 if (sgip->zsize == 0 /*|| sgip->zsize > GIMP_MAX_IMAGE_SIZE*/)
362 {
363 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
364 _("Invalid number of channels: %hu"), sgip->zsize);
365 free (sgip);
366 return -1;
367 }
368
369 bytes = sgip->zsize;
370
371 switch (sgip->zsize)
372 {
373 case 1 : /* Grayscale */
374 image_type = GIMP_GRAY;
375 layer_type = GIMP_GRAY_IMAGE;
376 break;
377
378 case 2 : /* Grayscale + alpha */
379 image_type = GIMP_GRAY;
380 layer_type = GIMP_GRAYA_IMAGE;
381 break;
382
383 case 3 : /* RGB */
384 image_type = GIMP_RGB;
385 layer_type = GIMP_RGB_IMAGE;
386 break;
387
388 case 4 : /* RGBA */
389 image_type = GIMP_RGB;
390 layer_type = GIMP_RGBA_IMAGE;
391 break;
392
393 default:
394 image_type = GIMP_RGB;
395 layer_type = GIMP_RGBA_IMAGE;
396 bytes = 4;
397 break;
398 }
399
400 image = gimp_image_new (sgip->xsize, sgip->ysize, image_type);
401 if (image == -1)
402 {
403 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
404 "Could not allocate new image: %s",
405 gimp_get_pdb_error());
406 free (sgip);
407 return -1;
408 }
409
410 gimp_image_set_filename (image, filename);
411
412 /*
413 * Create the "background" layer to hold the image...
414 */
415
416 layer = gimp_layer_new (image, _("Background"), sgip->xsize, sgip->ysize,
417 layer_type,
418 100,
419 gimp_image_get_default_new_layer_mode (image));
420 gimp_image_insert_layer (image, layer, -1, 0);
421
422 /*
423 * Get the drawable and set the pixel region for our load...
424 */
425
426 buffer = gimp_drawable_get_buffer (layer);
427
428 /*
429 * Temporary buffers...
430 */
431
432 tile_height = gimp_tile_height ();
433 pixels = g_new (guchar *, tile_height);
434 pixels[0] = g_new (guchar, ((gsize) tile_height) * sgip->xsize * bytes);
435
436 for (i = 1; i < tile_height; i ++)
437 pixels[i] = pixels[0] + sgip->xsize * bytes * i;
438
439 rows = g_new (unsigned short *, sgip->zsize);
440 rows[0] = g_new (unsigned short, ((gsize) sgip->xsize) * sgip->zsize);
441
442 for (i = 1; i < sgip->zsize; i ++)
443 rows[i] = rows[0] + i * sgip->xsize;
444
445 /*
446 * Load the image...
447 */
448
449 for (y = 0, count = 0;
450 y < sgip->ysize;
451 y ++, count ++)
452 {
453 if (count >= tile_height)
454 {
455 gegl_buffer_set (buffer, GEGL_RECTANGLE (0, y - count,
456 sgip->xsize, count), 0,
457 NULL, pixels[0], GEGL_AUTO_ROWSTRIDE);
458
459 count = 0;
460
461 gimp_progress_update ((double) y / (double) sgip->ysize);
462 }
463
464 for (i = 0; i < sgip->zsize; i ++)
465 if (sgiGetRow (sgip, rows[i], sgip->ysize - 1 - y, i) < 0)
466 g_printerr ("sgiGetRow(sgip, rows[i], %d, %d) failed!\n",
467 sgip->ysize - 1 - y, i);
468
469 if (sgip->bpp == 1)
470 {
471 /*
472 * 8-bit (unsigned) pixels...
473 */
474
475 for (x = 0, pptr = pixels[count]; x < sgip->xsize; x ++)
476 for (i = 0; i < bytes; i ++, pptr ++)
477 *pptr = rows[i][x];
478 }
479 else
480 {
481 /*
482 * 16-bit (unsigned) pixels...
483 */
484
485 for (x = 0, pptr = pixels[count]; x < sgip->xsize; x ++)
486 for (i = 0; i < bytes; i ++, pptr ++)
487 *pptr = rows[i][x] >> 8;
488 }
489 }
490
491 /*
492 * Do the last n rows (count always > 0)
493 */
494
495 gegl_buffer_set (buffer, GEGL_RECTANGLE (0, y - count,
496 sgip->xsize, count), 0,
497 NULL, pixels[0], GEGL_AUTO_ROWSTRIDE);
498
499 /*
500 * Done with the file...
501 */
502
503 sgiClose (sgip);
504
505 g_free (pixels[0]);
506 g_free (pixels);
507 g_free (rows[0]);
508 g_free (rows);
509
510 g_object_unref (buffer);
511
512 gimp_progress_update (1.0);
513
514 return image;
515 }
516
517
518 /*
519 * 'save_image()' - Save the specified image to a SGI file.
520 */
521
522 static gint
save_image(const gchar * filename,gint32 image_ID,gint32 drawable_ID,GError ** error)523 save_image (const gchar *filename,
524 gint32 image_ID,
525 gint32 drawable_ID,
526 GError **error)
527 {
528 gint i, j, /* Looping var */
529 x, /* Current X coordinate */
530 y, /* Current Y coordinate */
531 width, /* Drawable width */
532 height, /* Drawable height */
533 tile_height, /* Height of tile in GIMP */
534 count, /* Count of rows to put in image */
535 zsize; /* Number of channels in file */
536 sgi_t *sgip; /* File pointer */
537 GeglBuffer *buffer; /* Buffer for layer */
538 const Babl *format;
539 guchar **pixels, /* Pixel rows */
540 *pptr; /* Current pixel */
541 gushort **rows; /* SGI image data */
542
543 /*
544 * Get the drawable for the current image...
545 */
546
547 width = gimp_drawable_width (drawable_ID);
548 height = gimp_drawable_height (drawable_ID);
549
550 buffer = gimp_drawable_get_buffer (drawable_ID);
551
552 switch (gimp_drawable_type (drawable_ID))
553 {
554 case GIMP_GRAY_IMAGE:
555 zsize = 1;
556 format = babl_format ("Y' u8");
557 break;
558
559 case GIMP_GRAYA_IMAGE:
560 zsize = 2;
561 format = babl_format ("Y'A u8");
562 break;
563
564 case GIMP_RGB_IMAGE:
565 case GIMP_INDEXED_IMAGE:
566 zsize = 3;
567 format = babl_format ("R'G'B' u8");
568 break;
569
570 case GIMP_RGBA_IMAGE:
571 case GIMP_INDEXEDA_IMAGE:
572 format = babl_format ("R'G'B'A u8");
573 zsize = 4;
574 break;
575
576 default:
577 return FALSE;
578 }
579
580 /*
581 * Open the file for writing...
582 */
583
584 gimp_progress_init_printf (_("Exporting '%s'"),
585 gimp_filename_to_utf8 (filename));
586
587 sgip = sgiOpen (filename, SGI_WRITE, compression, 1,
588 width, height, zsize);
589 if (sgip == NULL)
590 {
591 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
592 _("Could not open '%s' for writing."),
593 gimp_filename_to_utf8 (filename));
594 return FALSE;
595 };
596
597 /*
598 * Allocate memory for "tile_height" rows...
599 */
600
601 tile_height = gimp_tile_height ();
602 pixels = g_new (guchar *, tile_height);
603 pixels[0] = g_new (guchar, ((gsize) tile_height) * width * zsize);
604
605 for (i = 1; i < tile_height; i ++)
606 pixels[i]= pixels[0] + width * zsize * i;
607
608 rows = g_new (gushort *, sgip->zsize);
609 rows[0] = g_new (gushort, ((gsize) sgip->xsize) * sgip->zsize);
610
611 for (i = 1; i < sgip->zsize; i ++)
612 rows[i] = rows[0] + i * sgip->xsize;
613
614 /*
615 * Save the image...
616 */
617
618 for (y = 0; y < height; y += count)
619 {
620 /*
621 * Grab more pixel data...
622 */
623
624 if ((y + tile_height) >= height)
625 count = height - y;
626 else
627 count = tile_height;
628
629 gegl_buffer_get (buffer, GEGL_RECTANGLE (0, y, width, count), 1.0,
630 format, pixels[0],
631 GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
632
633 /*
634 * Convert to shorts and write each color plane separately...
635 */
636
637 for (i = 0, pptr = pixels[0]; i < count; i ++)
638 {
639 for (x = 0; x < width; x ++)
640 for (j = 0; j < zsize; j ++, pptr ++)
641 rows[j][x] = *pptr;
642
643 for (j = 0; j < zsize; j ++)
644 sgiPutRow (sgip, rows[j], height - 1 - y - i, j);
645 };
646
647 gimp_progress_update ((double) y / (double) height);
648 }
649
650 /*
651 * Done with the file...
652 */
653
654 sgiClose (sgip);
655
656 g_free (pixels[0]);
657 g_free (pixels);
658 g_free (rows[0]);
659 g_free (rows);
660
661 g_object_unref (buffer);
662
663 gimp_progress_update (1.0);
664
665 return TRUE;
666 }
667
668 static gboolean
save_dialog(void)669 save_dialog (void)
670 {
671 GtkWidget *dialog;
672 GtkWidget *frame;
673 gboolean run;
674
675 dialog = gimp_export_dialog_new (_("SGI"), PLUG_IN_BINARY, SAVE_PROC);
676
677 frame = gimp_int_radio_group_new (TRUE, _("Compression type"),
678 G_CALLBACK (gimp_radio_button_update),
679 &compression, compression,
680
681 _("_No compression"),
682 SGI_COMP_NONE, NULL,
683 _("_RLE compression"),
684 SGI_COMP_RLE, NULL,
685 _("_Aggressive RLE\n(not supported by SGI)"),
686 SGI_COMP_ARLE, NULL,
687
688 NULL);
689
690 gtk_container_set_border_width (GTK_CONTAINER (frame), 12);
691 gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
692 frame, TRUE, TRUE, 0);
693 gtk_widget_show (frame);
694
695 gtk_widget_show (dialog);
696
697 run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
698
699 gtk_widget_destroy (dialog);
700
701 return run;
702 }
703