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